csenayeem025 / google-workspace-sdk
Google Workspace API SDK for Laravel
Requires
- php: ^8.1
- doctrine/annotations: ^1.13
- glamstack/google-auth-sdk: ^2.5
- illuminate/contracts: ^10.26
- spatie/laravel-package-tools: ^1.16.1
- symfony/options-resolver: ^6.0
- symfony/validator: ^6.0
Requires (Dev)
- ext-dom: *
- nunomaduro/collision: ^6.0
- nunomaduro/larastan: ^2.0.1
- orchestra/testbench: ^7.0
- pestphp/pest: ^1.21
- pestphp/pest-plugin-laravel: ^2.2
- pestphp/pest-plugin-mock: ^1.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9.5
- spatie/laravel-ray: ^1.33
This package is auto-updated.
Last update: 2024-09-10 11:28:03 UTC
README
概述
Google Workspace SDK 是一个开源的 Composer 包,由 GitLab IT Engineering 创建,用于在 GitLab Access Manager Laravel 应用程序中连接到 Google API 端点,用于用户、组、组成员以及其他相关功能的配置和取消配置。
免责声明:这不是由 Google 或 GitLab 产品和开发团队维护的官方包。这是一个我们作为公司价值观的一部分开源的 GitLab IT 部门的内部工具。
请自行承担风险,并为您遇到的所有错误创建问题。
依赖项
注意:此包运行需要 glamstack/google-auth-sdk
包。这已在 composer.json 文件中配置为必需包,并在安装此包时应自动加载。
默认情况下,此包的所有配置都将位于
glamstack-google-workspace.php
文件中,当安装此包时将加载。有关进一步指导,请参阅 安装文档
维护者
工作原理
该包利用 glamstack/google-auth-sdk 包创建 Google JWT Web Token 以与 Google Workspace API 进行身份验证。
有关 glamstack/google-auth-sdk 的更多信息,请参阅 Google Auth SDK README.md。
此包并不打算为 Google Workspace API 的每个端点提供功能。端点将根据需要构建。如果您希望添加任何其他端点,请参阅 CONTRIBUTING。
如果您需要的端点尚未创建,我们已提供 REST 类,可以对 Google Workspace API 文档中找到的任何端点执行 GET、POST、PUT 和 DELETE 请求。该类将处理 API 响应、错误处理和分页。
⚠️
PATCH
请求目前不可用,但将在将来实现。
此包基于由 Guzzle HTTP 客户端提供的 Laravel HTTP 客户端的简单性,为 Google Workspace API 的响应提供“最后代码解析”,以改善开发人员的体验。
// Initialized Client with `connection_key` parameter $google_workspace_api = new \Glamstack\GoogleWorkspace\ApiClient('workspace'); // Retrieves a paginated list of either deleted users or all users in a domain. // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list $records = $google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users'); // Retrieves a paginated list of either deleted users or all users in a domain // with query parameters included. // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list#OrderBy // https://developers.google.com/admin-sdk/directory/v1/guides/search-users $records = $google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users',[ 'maxResults' => '200', 'orderBy' => 'EMAIL', 'query' => [ 'orgDepartment' => 'Test Department' ] ]); // Get a specific user from Google Workspace // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/get $user_key = 'klibby@example.com'; $record = $google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/'.$user_key); // Create new Google Workspace User // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/insert // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users#User $record = $google_workspace_api->rest()->post('https://admin.googleapis.com/admin/directory/v1/users', [ 'name' => [ 'familyName' => 'Libby', 'givenName' => 'Kate' ], 'password' => 'ac!dBurnM3ss3sWithTheB4$t', 'primaryEmail' => 'klibby@example.com' ]); // Update an existing Google Workspace User // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/update $user_key = 'klibby@example.com'; $record = $google_workspace_api->rest()->put('https://admin.googleapis.com/admin/directory/v1/users/'.$user_key, [ 'name' => [ 'givenName' => 'Libby-Murphy' ] ]); // Delete a Google Workspace User // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/delete $user_key = 'klibby@example.com'; $record = $google_workspace_api->rest()->delete('https://admin.googleapis.com/admin/directory/v1/users/'.$user_key);
包初始化
此包通过配置初始化,请参阅 如何使用配置文件初始化 了解初始化方法的说明。
安装
要求
添加 Composer 包
本包使用日历版本控制。
我们建议始终在您的composer.json
文件中使用特定版本,并在假定最新版本适合您的项目之前,查看变更日志以查看每次发布中的重大更改。
composer require csenayeem025/google-workspace-sdk
如果您正在为该包做出贡献,请参阅CONTRIBUTING,了解配置具有符号链接的本地composer包的说明。
发布配置文件
php artisan vendor:publish --tag=glamstack-google-workspace
版本升级
如果您已升级到该包的新版本,请备份现有的配置文件,以避免您的自定义配置被覆盖。
cp config/glamstack-google-workspace.php config/glamstack-google-workspace.php.bak php artisan vendor:publish --tag=glamstack-google-workspace
日历版本控制
GitLab IT工程团队使用的是日历版本控制(CalVer)的修改版本,而不是语义版本控制(SemVer)。CalVer有YY(例如2021 => 21),但对我们来说,版本21.xx
感觉不太直观。由于我们的团队从2021年开始,我们决定只使用年份的最后一位整数(2021 => 1.x,2022 => 2.x等)。
版本号表示发布日期,格式为vY.M.D
。
为什么我们不使用语义版本控制
- 我们持续向
main
/master
/production
发布,并在大多数版本中做出重大更改,因此对我们来说,具有语义向后兼容的版本号是不直观的。 - 我们不喜欢争论如何称呼我们的发布/里程碑,以及它是否是主要版本、次要版本还是补丁版本。我们只是编写代码,编写变更日志,并在完成的那一天发布。变更日志发布日期成为标记的版本号(例如
2022-02-01
是v2.2.1
)。我们可能会为更大的发布使用更大的版本号(例如v2.2
),但这仅用于月度里程碑计划和规范目的。所有代码标记都包括发布日期(例如v2.2.1
)。 - 这使我们能够使用GitLab CI/CD自动化版本标记过程,基于管道作业运行的日期。
- 我们将在计划变更窗口期间更新使用此包的所有项目
composer.json
文件,以特定或新版本号,而无需担心“保持最新版本”的差异和/或重大更改。我们不维护任何分叉或分支。 - 我们的包使用您现有Laravel应用程序中的底层包,因此保持您的Laravel应用程序版本更新可以解决大多数安全问题。
初始化SDK
API客户端的初始化可以通过传递一个(字符串)连接密钥或传递一个(数组)连接配置来完成。
Google API身份验证
该包使用glamstack/google-auth-sdk包来创建Google JWT Web Token,以与Google Cloud API端点进行身份验证。
有关glamstack/google-auth-sdk的更多信息,请参阅Google Auth SDK README.md。
连接密钥
我们使用连接密钥的概念,它引用了config/glamstack-google-workspace.php
中的配置数组,允许您预先配置一个或多个API连接。
每个连接密钥都与一个GCP服务账户的JSON密钥相关联。这可以用来配置不同的认证范围连接和权限,用于您的GCP组织或不同的GCP项目(根据您使用的API调用)。这允许针对特定的API调用实现最小权限,并且您还可以配置多个与相同GCP项目关联的连接,这些连接具有不同的API令牌和权限级别。
示例连接密钥初始化
// Initialize the SDK using the `test` configuration from `glamstack-google-workspace.php` $client = new Glamstack\GoogleWorkspace\ApiClient('test');
示例连接密钥配置
return [ 'connections' => [ 'test' => [ 'api_scopes' => [ 'https://www.googleapis.com/auth/admin.directory.group', 'https://www.googleapis.com/auth/admin.directory.user' ], 'json_key_file_path' => storage_path(env('GOOGLE_WORKSPACE_TEST_JSON_KEY_FILE_PATH')), 'log_channels' => ['single'], 'customer_id' => env('GOOGLE_WORKSPACE_TEST_CUSTOMER_ID'), 'domain' => env('GOOGLE_WORKSPACE_TEST_DOMAIN'), 'subject_email' => env('GOOGLE_WORKSPACE_TEST_SUBJECT_EMAIL'), 'test_group_email' => env('GOOGLE_WORKSPACE_TEST_GROUP_EMAIL') ], ] ]
动态连接配置数组
如果您不想预先配置您的连接,并希望动态使用存储在您数据库中的连接变量,您可以通过数组传递所需的配置(参见示例连接配置数组初始化),使用ApiClient
构造方法的第二个参数中的connection_config
数组。
必需参数
在您的文件系统上使用JSON密钥文件
$client = new Glamstack\GoogleWorkspace\ApiClient(null, [ 'api_scopes' => [ 'https://www.googleapis.com/auth/admin.directory.group', 'https://www.googleapis.com/auth/contacts' ], 'customer_id' => config('tests.connections.test.customer_id'), 'domain' => config('tests.connections.test.domain'), 'json_key_file_path' => storage_path('keys/glamstack-google-workspace/test.json'), 'log_channels' => ['single'], 'subject_email' => config('tests.connections.test.subject_email') ]);
在您的数据库中使用JSON密钥字符串
安全警告:您绝对不应该将服务账户密钥(JSON内容)作为变量提交到源代码中,以避免泄露您GCP组织或项目的凭据。
建议在加密之前将JSON密钥转换为base 64编码的字符串,因为这是GCP服务账户API用于privateKeyData
字段的格式。
// Get service account from your model (`GoogleServiceAccount` is an example) $service_account = \App\Models\GoogleServiceAccount::where('id', '123456')->firstOrFail(); // Get JSON key string from database column that has an encrypted value $json_key_string = decrypt(json_decode($service_account->json_key)); $client = new \Glamstack\GoogleWorkspace\ApiClient(null, [ 'api_scopes' => [ 'https://www.googleapis.com/auth/admin.directory.group', 'https://www.googleapis.com/auth/contacts' ], 'customer_id' => config('tests.connections.test.customer_id'), 'domain' => config('tests.connections.test.domain'), 'json_key' => $json_key_string, 'log_channels' => ['single'], 'subject_email' => config('tests.connections.test.subject_email') ]);
以下示例显示了存储在您的数据库中的JSON密钥的值。
// Get service account from your model (`GoogleServiceAccount` is an example) $service_account = \App\Models\GoogleServiceAccount::where('id', '123456')->firstOrFail(); dd(decrypt(json_decode($service_account->json_key)); // { // "type": "service_account", // "project_id": "project_id", // "private_key_id": "key_id", // "private_key": "key_data", // "client_email": "xxxxx@xxxxx.iam.gserviceaccount.com", // "client_id": "123455667897654", // "auth_uri": "https://#/o/oauth2/auth", // "token_uri": "https://oauth2.googleapis.com/token", // "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", // "client_x509_cert_url": "some stuff" // }
自定义日志配置
默认情况下,我们使用所有日志都配置在您的应用程序的config/logging.php
文件中的single
通道。这会将所有Google Workspace日志消息发送到storage/logs/laravel.log
文件。
如果您希望将Google Workspace日志显示在单独的日志文件中,以便更容易地进行故障排除,而无需无关的日志消息,您可以创建一个自定义日志通道。例如,我们建议使用glamstack-google-workspace
的值,但您可以选择任何您喜欢的名称。
将自定义日志通道添加到config/logging.php
。
'channels' => [ // Add anywhere in the `channels` array 'glamstack-google-workspace' => [ 'name' => 'glamstack-google-workspace', 'driver' => 'single', 'level' => 'debug', 'path' => storage_path('logs/glamstack-google-workspace.log') ] ],
更新channels.stack.channels
数组以包括自定义通道的数组键(例如glamstack-google-workspace
)。请确保将glamstack-google-workspace
添加到现有的数组值中,而不是替换现有的值。
'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => [ 'single','slack', 'glamstack-google-workspace' ], 'ignore_exceptions' => false ] ],
REST API请求
您可以对Google Workspace Admin SDK Directory文档中任何资源端点的API进行请求。
内联使用
// Initialize the SDK $api_client = new \Glamstack\GoogleWorkspace\ApiClient('workspace'); $response = $api_client->rest()->get('https://admin.googleapis.com/admin/directory/v1/users');
GET请求
当使用REST类时,端点将需要完整的端点URL。
例如,列出Google Workspace用户API文档显示了端点
GET https://admin.googleapis.com/admin/directory/v1/users
使用SDK时,您使用带有Google Workspace用户端点的get()方法。
$google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users');
您还可以使用变量或数据库模型来获取用于构建端点数据。
$endpoint = 'https://admin.googleapis.com/admin/directory/v1/users'; $records = $google_workspace_api->rest()->get($endpoint);
以下是一些使用端点的更多示例。
// Get a list of Google Workspace Users // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list $records = $google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users'); // Get a specific Google Workspace User // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/get $user_key = 'klibby@example.com'; $record = $google_workspace_api->get('https://admin.googleapis.com/admin/directory/v1/users/' . $userKey);
带有查询字符串参数的GET请求
get()
方法的第二个参数是一个可选的数组参数,该参数由SDK和Laravel HTTP客户端解析,并自动添加?
和&
作为查询字符串。
// Retrieves a paginated list of either deleted users or all users in a domain // with query parameters included. // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list#OrderBy // https://developers.google.com/admin-sdk/directory/v1/guides/search-users $records = $google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users',[ 'maxResults' => '200', 'orderBy' => 'EMAIL' ]); // This will parse the array and render the query string // https://admin.googleapis.com/admin/directory/v1/users?maxResults='200'&orderBy='EMAIL'
POST请求
post()
方法几乎与带有参数数组的get()
请求相同,但是参数作为application/json
内容类型的数据通过表单数据传递,而不是在URL中作为查询字符串。这是行业标准,并非仅限于SDK。
您可以在Laravel HTTP 客户端文档中了解更多关于请求数据的信息。
// Create new Google Workspace User // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/insert // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users#User $record = $google_workspace_api->rest()->post('https://admin.googleapis.com/admin/directory/v1/users', [ 'name' => [ 'familyName' => 'Libby', 'givenName' => 'Kate' ], 'password' => 'ac!dBurnM3ss3sWithTheB4$t', 'primaryEmail' => 'klibby@example.com' ]);
PUT 请求
使用 put()
方法来更新现有记录(类似于 PATCH
请求)。您需要确保要更新的记录的 ID 存在于第一个参数(URI)中。
在大多数应用程序中,这通常是从数据库或其他位置获取的变量,而不是硬编码。
// Update an existing Google Workspace User // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/update $user_key = 'klibby@example.com'; $record = $google_workspace_api->rest()->put('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key, [ 'name' => [ 'givenName' => 'Libby-Murphy' ] ]);
DELETE 请求
使用 delete()
方法来根据您提供的 ID 销毁资源。
请注意,delete()
方法将根据供应商的不同返回不同的状态码(例如,200、201、202、204 等)。Google Workspace API 将成功删除的资源返回 204
状态码。
// Delete a Google Workspace User // https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/delete $user_key = 'klibby@example.com'; $record = $google_workspace_api->rest()->delete('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key);
类方法
上面的示例展示了适用于大多数用例的基本内联使用。如果您更喜欢使用类和构造函数,下面的示例将提供一个有用的例子。
<?php use Glamstack\GoogleWorkspace\ApiClient; class GoogleWorkspaceUserService { protected $google_workspace_api; public function __construct() { $this->google_workspace_api = new \Glamstack\GoogleWorkspace\ApiClient(); } public function listUsers(array $query = []) : object { $users = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users', $query); return $users->object; } public function getUser(string $user_key, array $query = []) : object { $user = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key, $query); return $user->object; } public function storeUser(string $user_key, array $request_data = []) : object { $response = $this->google_workspace_api->rest()->post('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key, $request_data); return $response->object; } public function updateUser(string $user_key, array $request_data = []) : object { $response = $this->google_workspace_api->rest()->put('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key, $request_data); return $response->object; } public function deleteUser(string $user_key) : bool { $response = $this->google_workspace_api->rest()->delete('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key); return $response->status->successful; } }
API 响应
此 SDK 使用 GLAM Stack 标准 API 响应格式化。
// API Request $response = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key); // API Response $response->headers; // object $response->json; // json $response->object; // object $response->status; // object $response->status->code; // int (ex. 200) $response->status->ok; // bool $response->status->successful; // bool $response->status->failed; // bool $response->status->serverError; // bool $response->status->clientError; // bool
API 响应头部
$response = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key); $response->headers;
{ +"ETag": ""nMRgLWac8h8NyH7Uk5VvV4DiNp4uxXg5gNUd9YhyaJE/MgKWL9SwIVWCY7rRA988mR8yR-k"" +"Content-Type": "application/json; charset=UTF-8" +"Vary": "Origin X-Origin Referer" +"Date": "Thu, 20 Jan 2022 16:36:03 GMT" +"Server": "ESF" +"Content-Length": "1257" +"X-XSS-Protection": "0" +"X-Frame-Options": "SAMEORIGIN" +"X-Content-Type-Options": "nosniff" +"Alt-Svc": "h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"" }
API 响应特定头部
$headers = (array) $response->headers; $content_type = $headers['Content-Type'];
application/json
API 响应 JSON
$response = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key); $response->json;
{ "kind":"admin#directory#user","id":"1111111111111", "etag":"\"nMRgLWac8h8NyH7Uk5VvV4DiNp4uxXg5gNUd9YhyaJE\/MgKWL9SwIVWCY7rRA988mR8yR-k\"", "primaryEmail":"klibby@example.com", "name":{ "givenName":"Kate", "familyName":"Libby", "fullName":"Kate Libby" }, "isAdmin":true, "isDelegatedAdmin":false, "lastLoginTime":"2022-01-18T15:26:16.000Z", "creationTime":"2021-12-08T13:15:43.000Z", "agreedToTerms":true, "suspended":false, "archived":false, "changePasswordAtNextLogin":false, "ipWhitelisted":false, "emails":[ { "address":"klibby@example.com", "type":"work" }, { "address":"klibby@example.com", "primary":true }, { "address":"klibby@example.com.test-google-a.com" } ], "phones":[ { "value":"5555555555", "type":"work" } ], "languages":[ { "languageCode":"en", "preference":"preferred" } ], "nonEditableAliases":[ "klibby@example.com.test-google-a.com" ], "customerId":"C000aaaaa", "orgUnitPath":"\/", "isMailboxSetup":true, "isEnrolledIn2Sv":false, "isEnforcedIn2Sv":false, "includeInGlobalAddressList":true }
API 响应对象
$response = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key); $response->object;
{#1256 +"kind": "admin#directory#user" +"id": "1111111111111" +"etag": ""nMRgLWac8h8NyH7Uk5VvV4DiNp4uxXg5gNUd9YhyaJE/MgKWL9SwIVWCY7rRA988mR8yR-k"" +"primaryEmail": "klibby@example.com" +"name": {#1242 +"givenName": "Kate" +"familyName": "Libby" +"fullName": "Kate Libby" } +"isAdmin": true +"isDelegatedAdmin": false +"lastLoginTime": "2022-01-18T15:26:16.000Z" +"creationTime": "2021-12-08T13:15:43.000Z" +"agreedToTerms": true +"suspended": false +"archived": false +"changePasswordAtNextLogin": false +"ipWhitelisted": false +"emails": array:3 [ 0 => {#1253 +"address": "klibby@example.com" +"type": "work" } 1 => {#1258 +"address": "klibby@example.com" +"primary": true } 2 => {#1259 +"address": "klibby@example.com.test-google-a.com" } ] +"phones": array:1 [ 0 => {#1247 +"value": "5555555555" +"type": "work" } ] +"languages": array:1 [ 0 => {#1250 +"languageCode": "en" +"preference": "preferred" } ] +"nonEditableAliases": array:1 [ 0 => "klibby@example-test.com.test-google-a.com" ] +"customerId": "C000aaaaa" +"orgUnitPath": "/" +"isMailboxSetup": true +"isEnrolledIn2Sv": false +"isEnforcedIn2Sv": false +"includeInGlobalAddressList": true }
API 响应状态
有关不同状态布尔值的更多信息,请参阅Laravel HTTP 客户端文档。
$response = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key); $response->status;
{ +"code": 200 +"ok": true +"successful": true +"failed": false +"serverError": false +"clientError": false }
API 响应状态码
$response = $this->google_workspace_api->rest()->get('https://admin.googleapis.com/admin/directory/v1/users/' . $user_key); $response->status->code;
200
错误处理
API 响应的 HTTP 状态码包含在每条日志消息和 JSON 的 status_code
中。任何内部 SDK 错误也包括等效的状态码,具体取决于错误的类型。《message》包括 SDK 友好的消息。如果抛出异常,则 reference
如果 API 返回 5xx
错误,则 GoogleWorkspaceApiClient
的 handleException
方法将返回一个响应。
有关 SDK 如何处理错误和日志的更多信息,请参阅下面的日志输出。
日志输出
错误消息的输出显示在
README
中,以便搜索引擎索引这些消息以支持开发者调试。任何 5xx 错误消息都将作为Symfony\Component\HttpKernel\Exception\HttpException
或配置错误返回,包括ApiClient::setApiConnectionVariables()
方法中的任何错误。
问题跟踪和错误报告
请访问我们的问题跟踪器并创建一个问题或对现有问题进行评论。
贡献
请参阅CONTRIBUTING.md 了解如何贡献。