From f58e336d5f75060aa58dd1fa8b40fd4e1d0eb9f8 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 3 Oct 2020 12:09:07 -0400 Subject: [PATCH] :sparkles: Add GSuite Admin Node (#992) * :sparkles: GSuite Admin Node * :zap: Improvements --- .../GSuiteAdminOAuth2Api.credentials.ts | 27 + .../Google/GSuiteAdmin/GSuiteAdmin.node.ts | 361 +++++++ .../Google/GSuiteAdmin/GenericFunctions.ts | 67 ++ .../Google/GSuiteAdmin/UserDescription.ts | 964 ++++++++++++++++++ .../nodes/Google/GSuiteAdmin/gSuiteAdmin.png | Bin 0 -> 6297 bytes packages/nodes-base/package.json | 2 + 6 files changed, 1421 insertions(+) create mode 100644 packages/nodes-base/credentials/GSuiteAdminOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts create mode 100644 packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Google/GSuiteAdmin/UserDescription.ts create mode 100644 packages/nodes-base/nodes/Google/GSuiteAdmin/gSuiteAdmin.png diff --git a/packages/nodes-base/credentials/GSuiteAdminOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GSuiteAdminOAuth2Api.credentials.ts new file mode 100644 index 0000000000..88c369c665 --- /dev/null +++ b/packages/nodes-base/credentials/GSuiteAdminOAuth2Api.credentials.ts @@ -0,0 +1,27 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +const scopes = [ + 'https://www.googleapis.com/auth/admin.directory.user', + 'https://www.googleapis.com/auth/admin.directory.domain.readonly', + 'https://www.googleapis.com/auth/admin.directory.userschema.readonly', +]; + +export class GSuiteAdminOAuth2Api implements ICredentialType { + name = 'gSuiteAdminOAuth2Api'; + extends = [ + 'googleOAuth2Api', + ]; + displayName = 'G Suite Admin OAuth2 API'; + documentationUrl = 'google'; + properties = [ + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' '), + }, + ]; +} diff --git a/packages/nodes-base/nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts b/packages/nodes-base/nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts new file mode 100644 index 0000000000..6d89f6e8a6 --- /dev/null +++ b/packages/nodes-base/nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts @@ -0,0 +1,361 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeTypeDescription, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, +} from 'n8n-workflow'; + +import { + googleApiRequest, + googleApiRequestAllItems, +} from './GenericFunctions'; + +import { + userOperations, + userFields, +} from './UserDescription'; + +export class GSuiteAdmin implements INodeType { + description: INodeTypeDescription = { + displayName: 'G Suite Admin', + name: 'gSuiteAdmin', + icon: 'file:gSuiteAdmin.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume G Suite Admin API', + defaults: { + name: 'G Suite Admin', + color: '#ecbb26', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'gSuiteAdminOAuth2Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'User', + value: 'user', + }, + ], + default: 'user', + description: 'The resource to operate on.' + }, + ...userOperations, + ...userFields, + ], + }; + + methods = { + loadOptions: { + // Get all the domains to display them to user so that he can + // select them easily + async getDomains( + this: ILoadOptionsFunctions + ): Promise { + const returnData: INodePropertyOptions[] = []; + const domains = await googleApiRequestAllItems.call( + this, + 'domains', + 'GET', + '/directory/v1/customer/my_customer/domains' + ); + for (const domain of domains) { + const domainName = domain.domainName; + const domainId = domain.domainName; + returnData.push({ + name: domainName, + value: domainId + }); + } + return returnData; + }, + // Get all the schemas to display them to user so that he can + // select them easily + async getSchemas( + this: ILoadOptionsFunctions + ): Promise { + const returnData: INodePropertyOptions[] = []; + const schemas = await googleApiRequestAllItems.call( + this, + 'schemas', + 'GET', + '/directory/v1/customer/my_customer/schemas' + ); + for (const schema of schemas) { + const schemaName = schema.displayName; + const schemaId = schema.schemaName; + returnData.push({ + name: schemaName, + value: schemaId + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = (items.length as unknown) as number; + const qs: IDataObject = {}; + let responseData; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + for (let i = 0; i < length; i++) { + + if (resource === 'user') { + + //https://developers.google.com/admin-sdk/directory/v1/reference/users/insert + if (operation === 'create') { + + const domain = this.getNodeParameter('domain', i) as string; + + const firstName = this.getNodeParameter('firstName', i) as string; + + const lastName = this.getNodeParameter('lastName', i) as string; + + const password = this.getNodeParameter('password', i) as string; + + const username = this.getNodeParameter('username', i) as string; + + const makeAdmin = this.getNodeParameter('makeAdmin', i) as boolean; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + name: { + familyName: lastName, + givenName: firstName, + }, + password, + primaryEmail: `${username}@${domain}`, + }; + + Object.assign(body, additionalFields); + + if (additionalFields.phoneUi) { + + const phones = (additionalFields.phoneUi as IDataObject).phoneValues as IDataObject[]; + + body.phones = phones; + + delete body.phoneUi; + } + + if (additionalFields.emailUi) { + + const emails = (additionalFields.emailUi as IDataObject).emailValues as IDataObject[]; + + body.emails = emails; + + delete body.emailUi; + } + + responseData = await googleApiRequest.call( + this, + 'POST', + `/directory/v1/users`, + body, + qs + ); + + if (makeAdmin) { + + await googleApiRequest.call( + this, + 'POST', + `/directory/v1/users/${responseData.id}/makeAdmin`, + { status: true }, + ); + + responseData.isAdmin = true; + } + } + + //https://developers.google.com/admin-sdk/directory/v1/reference/users/delete + if (operation === 'delete') { + + const userId = this.getNodeParameter('userId', i) as string; + + responseData = await googleApiRequest.call( + this, + 'DELETE', + `/directory/v1/users/${userId}`, + {} + ); + + responseData = { success: true }; + } + + //https://developers.google.com/admin-sdk/directory/v1/reference/users/get + if (operation === 'get') { + + const userId = this.getNodeParameter('userId', i) as string; + + const projection = this.getNodeParameter('projection', i) as string; + + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.projection = projection; + + Object.assign(qs, options); + + if (qs.customFieldMask) { + qs.customFieldMask = (qs.customFieldMask as string[]).join(' '); + } + + if (qs.projection === 'custom' && qs.customFieldMask === undefined) { + throw new Error('When projection is set to custom, the custom schemas field must be defined'); + } + + responseData = await googleApiRequest.call( + this, + 'GET', + `/directory/v1/users/${userId}`, + {}, + qs + ); + } + + //https://developers.google.com/admin-sdk/directory/v1/reference/users/list + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + const projection = this.getNodeParameter('projection', i) as string; + + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.projection = projection; + + Object.assign(qs, options); + + if (qs.customer === undefined) { + qs.customer = 'my_customer'; + } + + if (qs.customFieldMask) { + qs.customFieldMask = (qs.customFieldMask as string[]).join(' '); + } + + if (qs.projection === 'custom' && qs.customFieldMask === undefined) { + throw new Error('When projection is set to custom, the custom schemas field must be defined'); + } + + if (returnAll) { + + responseData = await googleApiRequestAllItems.call( + this, + 'users', + 'GET', + `/directory/v1/users`, + {}, + qs + ); + + } else { + + qs.maxResults = this.getNodeParameter('limit', i) as number; + + responseData = await googleApiRequest.call( + this, + 'GET', + `/directory/v1/users`, + {}, + qs + ); + + responseData = responseData.users; + } + } + + //https://developers.google.com/admin-sdk/directory/v1/reference/users/update + if (operation === 'update') { + + const userId = this.getNodeParameter('userId', i) as string; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + const body: { name: { givenName?: string, familyName?: string }, emails?: IDataObject[], phones?: IDataObject[] } = { name: {} }; + + Object.assign(body, updateFields); + + if (updateFields.firstName) { + body.name.givenName = updateFields.firstName as string; + //@ts-ignore + delete body.firstName; + } + + if (updateFields.lastName) { + body.name.familyName = updateFields.lastName as string; + //@ts-ignore + delete body.lastName; + } + + if (Object.keys(body.name).length === 0) { + delete body.name; + } + + if (updateFields.phoneUi) { + + const phones = (updateFields.phoneUi as IDataObject).phoneValues as IDataObject[]; + + body.phones = phones; + + //@ts-ignore + delete body.phoneUi; + } + + if (updateFields.emailUi) { + + const emails = (updateFields.emailUi as IDataObject).emailValues as IDataObject[]; + + body.emails = emails; + + //@ts-ignore + delete body.emailUi; + } + + //@ts-ignore + body['customSchemas'] = { testing: { hasdog: true } }; + + responseData = await googleApiRequest.call( + this, + 'PUT', + `/directory/v1/users/${userId}`, + body, + qs + ); + } + } + } + + if (Array.isArray(responseData)) { + + returnData.push.apply(returnData, responseData as IDataObject[]); + + } else if (responseData !== undefined) { + + returnData.push(responseData as IDataObject); + } + + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts b/packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts new file mode 100644 index 0000000000..e41fc33061 --- /dev/null +++ b/packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts @@ -0,0 +1,67 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise { // tslint:disable-line:no-any + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://www.googleapis.com/admin${resource}`, + json: true + }; + try { + if (Object.keys(headers).length !== 0) { + options.headers = Object.assign({}, options.headers, headers); + } + if (Object.keys(body).length === 0) { + delete options.body; + } + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'gSuiteAdminOAuth2Api', options); + } catch (error) { + if (error.response && error.response.body && error.response.body.error) { + + let errors = error.response.body.error.errors; + + errors = errors.map((e: IDataObject) => e.message); + // Try to return the error prettier + throw new Error( + `G Suite Admin error response [${error.statusCode}]: ${errors.join('|')}` + ); + } + throw error; + } +} + +export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + query.maxResults = 100; + + do { + responseData = await googleApiRequest.call(this, method, endpoint, body, query); + query.pageToken = responseData['nextPageToken']; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData['nextPageToken'] !== undefined && + responseData['nextPageToken'] !== '' + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/Google/GSuiteAdmin/UserDescription.ts b/packages/nodes-base/nodes/Google/GSuiteAdmin/UserDescription.ts new file mode 100644 index 0000000000..436cab6801 --- /dev/null +++ b/packages/nodes-base/nodes/Google/GSuiteAdmin/UserDescription.ts @@ -0,0 +1,964 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const userOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'user', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a user', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a user', + }, + { + name: 'Get', + value: 'get', + description: 'Get a user', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all users', + }, + { + name: 'Update', + value: 'update', + description: 'Update a user', + }, + ], + default: 'create', + description: 'The operation to perform.' + } +] as INodeProperties[]; + +export const userFields = [ + /* -------------------------------------------------------------------------- */ + /* user:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'user', + ], + }, + }, + default: '', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'create' + ], + resource: [ + 'user' + ], + }, + }, + default: '' + }, + { + displayName: 'Password', + name: 'password', + type: 'string', + typeOptions: { + password: true, + }, + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'user', + ], + }, + }, + default: '', + description: 'Stores the password for the user account. A minimum of 8 characters is required. The maximum length is 100 characters.' + }, + { + displayName: 'Domain', + name: 'domain', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getDomains', + }, + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'user', + ], + }, + }, + default: '', + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'user', + ], + }, + }, + default: '', + description: `The username that will be set to the user. Example: If you domain is example.com and you set the username to jhon then the user's final email address will be jhon@example.com.` + }, + { + displayName: 'Make Admin', + name: 'makeAdmin', + type: 'boolean', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'user', + ], + }, + }, + default: false, + description: 'Makes a user a super administrator', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'user', + ], + }, + }, + options: [ + { + displayName: 'Change Password At Next Login', + name: 'changePasswordAtNextLogin', + type: 'boolean', + default: false, + description: 'Indicates if the user is forced to change their password at next login.', + }, + { + displayName: 'Phones', + name: 'phoneUi', + placeholder: 'Add Phone', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'phoneValues', + displayName: 'Phone', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Assistant', + value: 'assistant', + }, + { + name: 'Callback', + value: 'callback', + }, + { + name: 'Car', + value: 'car', + }, + { + name: 'Company Main', + value: 'company_main', + }, + { + name: 'Custom', + value: 'custom', + }, + { + name: 'Grand Central', + value: 'grand_central', + }, + { + name: 'Home', + value: 'home', + }, + { + name: 'Home Fax', + value: 'home_fax', + }, + { + name: 'isdn', + value: 'isdn', + }, + { + name: 'Main', + value: 'main', + }, + { + name: 'Mobile', + value: 'mobile', + }, + { + name: 'Other', + value: 'other', + }, + { + name: 'Other Fax', + value: 'other_fax', + }, + { + name: 'Pager', + value: 'pager', + }, + { + name: 'Radio', + value: 'radio', + }, + { + name: 'Telex', + value: 'telex', + }, + { + name: 'tty tdd', + value: 'tty_tdd', + }, + { + name: 'Work', + value: 'work', + }, + { + name: 'Work Fax', + value: 'work_fax', + }, + { + name: 'Work Mobile', + value: 'work_mobile', + }, + { + name: 'Work Pager', + value: 'work_pager', + }, + ], + default: 'work', + description: 'The type of phone number', + }, + { + displayName: 'Phone Number', + name: 'value', + type: 'string', + default: '', + }, + { + displayName: 'Primary', + name: 'primary', + type: 'boolean', + default: false, + description: `Indicates if this is the user's primary phone number. A user may only have one primary phone number.`, + }, + ], + }, + ], + }, + { + displayName: 'Secondary Emails', + name: 'emailUi', + placeholder: 'Add Email', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'emailValues', + displayName: 'Email', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Home', + value: 'home', + }, + { + name: 'Work', + value: 'work', + }, + { + name: 'Other', + value: 'other', + }, + ], + default: 'work', + description: 'The type of the email account', + }, + { + displayName: 'Email', + name: 'address', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* user:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'user', + ], + }, + }, + default: '', + description: `The value can be the user's primary email address, alias email address, or unique user ID.`, + }, + /* -------------------------------------------------------------------------- */ + /* user:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'user', + ], + }, + }, + default: '', + description: `The value can be the user's primary email address, alias email address, or unique user ID.`, + }, + { + displayName: 'Projection', + name: 'projection', + type: 'options', + required: true, + options: [ + { + name: 'Basic', + value: 'basic', + description: 'Do not include any custom fields for the user', + }, + { + name: 'Custom', + value: 'custom', + description: 'Include custom fields from schemas requested in customField', + }, + { + name: 'Full', + value: 'full', + description: 'Include all fields associated with this user', + }, + ], + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'user', + ], + }, + }, + default: 'basic', + description: 'What subset of fields to fetch for this user', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Options', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'user', + ], + }, + }, + options: [ + { + displayName: 'Custom Schemas', + name: 'customFieldMask', + type: 'multiOptions', + displayOptions: { + show: { + '/projection': [ + 'custom', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSchemas', + }, + default: [], + description: `A comma-separated list of schema names. All fields from these schemas are fetched. This should only be set when projection=custom.`, + }, + { + displayName: 'View Type', + name: 'viewType', + type: 'options', + options: [ + { + name: 'Admin View', + value: 'admin_view', + description: 'Results include both administrator-only and domain-public fields for the user', + }, + { + name: 'Descending', + value: 'DESCENDING', + description: 'Results only include fields for the user that are publicly visible to other users in the domain', + }, + ], + default: 'admin_view', + description: 'Whether to fetch the administrator-only or domain-wide public view of the user. For more information, see Retrieve a user as a non-administrator.', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* user:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'user', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'user', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Projection', + name: 'projection', + type: 'options', + required: true, + options: [ + { + name: 'Basic', + value: 'basic', + description: 'Do not include any custom fields for the user', + }, + { + name: 'Custom', + value: 'custom', + description: 'Include custom fields from schemas requested in customField', + }, + { + name: 'Full', + value: 'full', + description: 'Include all fields associated with this user', + }, + ], + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'user', + ], + }, + }, + default: 'basic', + description: 'What subset of fields to fetch for this user', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'user', + ], + }, + }, + options: [ + { + displayName: 'Custom Schemas', + name: 'customFieldMask', + type: 'multiOptions', + displayOptions: { + show: { + '/projection': [ + 'custom', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSchemas', + }, + default: [], + description: `A comma-separated list of schema names. All fields from these schemas are fetched. This should only be set when projection=custom.`, + }, + { + displayName: 'Customer', + name: 'customer', + type: 'string', + default: '', + description: `The unique ID for the customer's G Suite account. In case of a multi-domain account, to fetch all groups for a customer, fill this field instead of domain`, + }, + { + displayName: 'Domain', + name: 'domain', + type: 'string', + default: '', + description: 'The domain name. Use this field to get fields from only one domain.', + }, + { + displayName: 'Order By', + name: 'orderBy', + type: 'options', + options: [ + { + name: 'Email', + value: 'email', + }, + { + name: 'Family Name', + value: 'familyName', + }, + { + name: 'Given Name', + value: 'givenName', + }, + ], + default: '', + description: 'Property to use for sorting results.', + }, + { + displayName: 'Projection', + name: 'projection', + type: 'options', + options: [ + { + name: 'Basic', + value: 'basic', + description: 'Do not include any custom fields for the user', + }, + { + name: 'Custom', + value: 'custom', + description: 'Include custom fields from schemas requested in customField', + }, + { + name: 'Full', + value: 'full', + description: 'Include all fields associated with this user', + }, + ], + default: 'basic', + description: 'Property to use for sorting results.', + }, + { + displayName: 'Query', + name: 'query', + type: 'string', + default: '', + description: `Free text search terms to find users that match these terms in any field, except for extended properties.
+ For more information on constructing user queries, see Search for Users`, + }, + { + displayName: 'Show Deleted', + name: 'showDeleted', + type: 'boolean', + default: false, + description: 'Whether to include deleted users (with status equals "cancelled") in the result.', + }, + { + displayName: 'Sort Order', + name: 'sortOrder', + type: 'options', + options: [ + { + name: 'Ascending', + value: 'ASCENDING', + }, + { + name: 'Descending', + value: 'DESCENDING', + }, + ], + default: '', + description: 'Whether to return results in ascending or descending order', + }, + { + displayName: 'View Type', + name: 'viewType', + type: 'options', + options: [ + { + name: 'Admin View', + value: 'admin_view', + description: 'Results include both administrator-only and domain-public fields for the user', + }, + { + name: 'Descending', + value: 'DESCENDING', + description: 'Results only include fields for the user that are publicly visible to other users in the domain', + }, + ], + default: 'admin_view', + description: 'Whether to fetch the administrator-only or domain-wide public view of the user. For more information, see Retrieve a user as a non-administrator.', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* user:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'user', + ], + }, + }, + default: '', + description: `The value can be the user's primary email address, alias email address, or unique user ID.`, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'user', + ], + }, + }, + options: [ + { + displayName: 'Archived', + name: 'archived', + type: 'boolean', + default: false, + description: 'Indicates if user is archived.', + }, + { + displayName: 'Change Password At Next Login', + name: 'changePasswordAtNextLogin', + type: 'boolean', + default: false, + description: 'Indicates if the user is forced to change their password at next login.', + }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '' + }, + { + displayName: 'Password', + name: 'password', + type: 'string', + default: '', + description: 'Stores the password for the user account. A minimum of 8 characters is required. The maximum length is 100 characters.' + }, + { + displayName: 'Phones', + name: 'phoneUi', + placeholder: 'Add Phone', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'phoneValues', + displayName: 'Phone', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Assistant', + value: 'assistant', + }, + { + name: 'Callback', + value: 'callback', + }, + { + name: 'Car', + value: 'car', + }, + { + name: 'Company Main', + value: 'company_main', + }, + { + name: 'Custom', + value: 'custom', + }, + { + name: 'Grand Central', + value: 'grand_central', + }, + { + name: 'Home', + value: 'home', + }, + { + name: 'Home Fax', + value: 'home_fax', + }, + { + name: 'isdn', + value: 'isdn', + }, + { + name: 'Main', + value: 'main', + }, + { + name: 'Mobile', + value: 'mobile', + }, + { + name: 'Other', + value: 'other', + }, + { + name: 'Other Fax', + value: 'other_fax', + }, + { + name: 'Pager', + value: 'pager', + }, + { + name: 'Radio', + value: 'radio', + }, + { + name: 'Telex', + value: 'telex', + }, + { + name: 'tty tdd', + value: 'tty_tdd', + }, + { + name: 'Work', + value: 'work', + }, + { + name: 'Work Fax', + value: 'work_fax', + }, + { + name: 'Work Mobile', + value: 'work_mobile', + }, + { + name: 'Work Pager', + value: 'work_pager', + }, + ], + default: 'work', + description: 'The type of phone number', + }, + { + displayName: 'Phone Number', + name: 'value', + type: 'string', + default: '', + }, + { + displayName: 'Primary', + name: 'primary', + type: 'boolean', + default: false, + description: `Indicates if this is the user's primary phone number. A user may only have one primary phone number.`, + }, + ], + }, + ], + }, + { + displayName: 'Primary Email', + name: 'primaryEmail', + type: 'string', + default: '', + description: `The user's primary email address. This property is required in a request to create a user account. The primaryEmail must be unique and cannot be an alias of another user.`, + }, + { + displayName: 'Secondary Emails', + name: 'emailUi', + placeholder: 'Add Email', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'emailValues', + displayName: 'Email', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Home', + value: 'home', + }, + { + name: 'Work', + value: 'work', + }, + { + name: 'Other', + value: 'other', + }, + ], + default: 'work', + description: 'The type of the email account', + }, + { + displayName: 'Email', + name: 'address', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Google/GSuiteAdmin/gSuiteAdmin.png b/packages/nodes-base/nodes/Google/GSuiteAdmin/gSuiteAdmin.png new file mode 100644 index 0000000000000000000000000000000000000000..70e45de81477cbc80830adefd0f9dc5536cfe887 GIT binary patch literal 6297 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 A*8l(j literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 362ebdd3b6..56bd0cb5f6 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -79,6 +79,7 @@ "dist/credentials/GoogleDriveOAuth2Api.credentials.js", "dist/credentials/GoogleOAuth2Api.credentials.js", "dist/credentials/GoogleSheetsOAuth2Api.credentials.js", + "dist/credentials/GSuiteAdminOAuth2Api.credentials.js", "dist/credentials/GoogleTasksOAuth2Api.credentials.js", "dist/credentials/YouTubeOAuth2Api.credentials.js", "dist/credentials/GumroadApi.credentials.js", @@ -260,6 +261,7 @@ "dist/nodes/Google/Contacts/GoogleContacts.node.js", "dist/nodes/Google/Drive/GoogleDrive.node.js", "dist/nodes/Google/Gmail/Gmail.node.js", + "dist/nodes/Google/GSuiteAdmin/GSuiteAdmin.node.js", "dist/nodes/Google/Sheet/GoogleSheets.node.js", "dist/nodes/Google/Task/GoogleTasks.node.js", "dist/nodes/Google/YouTube/YouTube.node.js",