From 3971e30affcb04323ec9202e45c281c69e3282c8 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 3 Nov 2021 20:55:04 -0400 Subject: [PATCH] :sparkles: Add user group resource to Slack Node (#2405) --- .../credentials/SlackOAuth2Api.credentials.ts | 2 + .../nodes/Slack/GenericFunctions.ts | 6 + packages/nodes-base/nodes/Slack/Slack.node.ts | 110 ++++- .../nodes/Slack/UserGroupDescription.ts | 378 ++++++++++++++++++ 4 files changed, 491 insertions(+), 5 deletions(-) create mode 100644 packages/nodes-base/nodes/Slack/UserGroupDescription.ts diff --git a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts index 7c44beeb6..e4aef0c82 100644 --- a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts @@ -15,6 +15,8 @@ const userScopes = [ 'reactions:write', 'stars:read', 'stars:write', + 'usergroups:write', + 'usergroups:read', 'users.profile:read', 'users.profile:write', ]; diff --git a/packages/nodes-base/nodes/Slack/GenericFunctions.ts b/packages/nodes-base/nodes/Slack/GenericFunctions.ts index 3d92cdab2..e9ee77117 100644 --- a/packages/nodes-base/nodes/Slack/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Slack/GenericFunctions.ts @@ -58,6 +58,12 @@ export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFu } if (response.ok === false) { + if (response.error === 'paid_teams_only') { + throw new NodeOperationError(this.getNode(), `Your current Slack plan does not include the resource '${this.getNodeParameter('resource', 0) as string}'`, { + description: `Hint: Upgrate to the Slack plan that includes the funcionality you want to use.`, + }); + } + throw new NodeOperationError(this.getNode(), 'Slack error response: ' + JSON.stringify(response)); } diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index 5213489c7..cd506473d 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -41,6 +41,11 @@ import { reactionOperations, } from './ReactionDescription'; +import { + userGroupFields, + userGroupOperations, +} from './UserGroupDescription'; + import { userFields, userOperations, @@ -191,6 +196,10 @@ export class Slack implements INodeType { name: 'User', value: 'user', }, + { + name: 'User Group', + value: 'userGroup', + }, { name: 'User Profile', value: 'userProfile', @@ -212,6 +221,8 @@ export class Slack implements INodeType { ...reactionFields, ...userOperations, ...userFields, + ...userGroupOperations, + ...userGroupFields, ...userProfileOperations, ...userProfileFields, ], @@ -295,13 +306,14 @@ export class Slack implements INodeType { try { const response = await this.helpers.request(options); + if (!response.ok) { return { status: 'Error', message: `${response.error}`, }; } - } catch(err) { + } catch (err) { return { status: 'Error', message: `${err.message}`, @@ -414,10 +426,10 @@ export class Slack implements INodeType { qs.inclusive = filters.inclusive as boolean; } if (filters.latest) { - qs.latest = new Date(filters.latest as string).getTime()/1000; + qs.latest = new Date(filters.latest as string).getTime() / 1000; } if (filters.oldest) { - qs.oldest = new Date(filters.oldest as string).getTime()/1000; + qs.oldest = new Date(filters.oldest as string).getTime() / 1000; } if (returnAll === true) { responseData = await slackApiRequestAllItems.call(this, 'messages', 'GET', '/conversations.history', {}, qs); @@ -508,10 +520,10 @@ export class Slack implements INodeType { qs.inclusive = filters.inclusive as boolean; } if (filters.latest) { - qs.latest = new Date(filters.latest as string).getTime()/1000; + qs.latest = new Date(filters.latest as string).getTime() / 1000; } if (filters.oldest) { - qs.oldest = new Date(filters.oldest as string).getTime()/1000; + qs.oldest = new Date(filters.oldest as string).getTime() / 1000; } if (returnAll === true) { responseData = await slackApiRequestAllItems.call(this, 'messages', 'GET', '/conversations.replies', {}, qs); @@ -1036,6 +1048,94 @@ export class Slack implements INodeType { responseData = await slackApiRequest.call(this, 'GET', '/users.getPresence', {}, qs); } } + if (resource === 'userGroup') { + //https://api.slack.com/methods/usergroups.create + if (operation === 'create') { + const name = this.getNodeParameter('name', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + name, + }; + + Object.assign(body, additionalFields); + + responseData = await slackApiRequest.call(this, 'POST', '/usergroups.create', body, qs); + + responseData = responseData.usergroup; + } + //https://api.slack.com/methods/usergroups.enable + if (operation === 'enable') { + const userGroupId = this.getNodeParameter('userGroupId', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + usergroup: userGroupId, + }; + + Object.assign(body, additionalFields); + + responseData = await slackApiRequest.call(this, 'POST', '/usergroups.enable', body, qs); + + responseData = responseData.usergroup; + } + //https://api.slack.com/methods/usergroups.disable + if (operation === 'disable') { + const userGroupId = this.getNodeParameter('userGroupId', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + usergroup: userGroupId, + }; + + Object.assign(body, additionalFields); + + responseData = await slackApiRequest.call(this, 'POST', '/usergroups.disable', body, qs); + + responseData = responseData.usergroup; + } + + //https://api.slack.com/methods/usergroups.list + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const qs: IDataObject = {}; + + Object.assign(qs, additionalFields); + + responseData = await slackApiRequest.call(this, 'GET', '/usergroups.list', {}, qs); + + responseData = responseData.usergroups; + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); + } + } + + //https://api.slack.com/methods/usergroups.update + if (operation === 'update') { + const userGroupId = this.getNodeParameter('userGroupId', i) as string; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + const body: IDataObject = { + usergroup: userGroupId, + }; + + Object.assign(body, updateFields); + + responseData = await slackApiRequest.call(this, 'POST', '/usergroups.update', body, qs); + + responseData = responseData.usergroup; + } + } if (resource === 'userProfile') { //https://api.slack.com/methods/users.profile.set if (operation === 'update') { diff --git a/packages/nodes-base/nodes/Slack/UserGroupDescription.ts b/packages/nodes-base/nodes/Slack/UserGroupDescription.ts new file mode 100644 index 000000000..f352e6af2 --- /dev/null +++ b/packages/nodes-base/nodes/Slack/UserGroupDescription.ts @@ -0,0 +1,378 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const userGroupOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'userGroup', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a user group', + }, + { + name: 'Disable', + value: 'disable', + description: 'Disable a user group', + }, + { + name: 'Enable', + value: 'enable', + description: 'Enable a user group', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all user groups', + }, + { + name: 'Update', + value: 'update', + description: 'Update a user group', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const userGroupFields = [ + + /* -------------------------------------------------------------------------- */ + /* userGroup:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'userGroup', + ], + }, + }, + required: true, + description: 'A name for the User Group. Must be unique among User Groups.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userGroup', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Channel IDs', + name: 'channelIds', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + default: [], + description: 'A comma separated string of encoded channel IDs for which the User Group uses as a default.', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'A short description of the User Group.', + }, + { + displayName: 'Handle', + name: 'handle', + type: 'string', + default: '', + description: 'A mention handle. Must be unique among channels, users and User Groups.', + }, + { + displayName: 'Include Count', + name: 'include_count', + type: 'boolean', + default: true, + description: 'Include the number of users in each User Group.', + }, + ], + }, + /* ----------------------------------------------------------------------- */ + /* userGroup:disable */ + /* ----------------------------------------------------------------------- */ + { + displayName: 'User Group ID', + name: 'userGroupId', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'disable', + ], + resource: [ + 'userGroup', + ], + }, + }, + required: true, + description: 'The encoded ID of the User Group to update.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userGroup', + ], + operation: [ + 'disable', + ], + }, + }, + options: [ + { + displayName: 'Include Count', + name: 'include_count', + type: 'boolean', + default: true, + description: 'Include the number of users in each User Group.', + }, + ], + }, + /* ----------------------------------------------------------------------- */ + /* userGroup:enable */ + /* ----------------------------------------------------------------------- */ + { + displayName: 'User Group ID', + name: 'userGroupId', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'enable', + ], + resource: [ + 'userGroup', + ], + }, + }, + required: true, + description: 'The encoded ID of the User Group to update.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userGroup', + ], + operation: [ + 'enable', + ], + }, + }, + options: [ + { + displayName: 'Include Count', + name: 'include_count', + type: 'boolean', + default: true, + description: 'Include the number of users in each User Group.', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* userGroup:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'userGroup', + ], + }, + }, + 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: [ + 'userGroup', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userGroup', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Include Count', + name: 'include_count', + type: 'boolean', + default: true, + description: 'Include the number of users in each User Group.', + }, + { + displayName: 'Include Disabled', + name: 'include_disabled', + type: 'boolean', + default: true, + description: 'Include disabled User Groups.', + }, + { + displayName: 'Include Users', + name: 'include_users', + type: 'boolean', + default: true, + description: 'Include the list of users for each User Group.', + }, + ], + }, + /* ----------------------------------------------------------------------- */ + /* userGroup:update */ + /* ----------------------------------------------------------------------- */ + { + displayName: 'User Group ID', + name: 'userGroupId', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'userGroup', + ], + }, + }, + required: true, + description: 'The encoded ID of the User Group to update.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userGroup', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Channel IDs', + name: 'channels', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + default: [], + description: 'A comma separated string of encoded channel IDs for which the User Group uses as a default.', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'A short description of the User Group.', + }, + { + displayName: 'Handle', + name: 'handle', + type: 'string', + default: '', + description: 'A mention handle. Must be unique among channels, users and User Groups.', + }, + { + displayName: 'Include Count', + name: 'include_count', + type: 'boolean', + default: true, + description: 'Include the number of users in each User Group.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'A name for the User Group. Must be unique among User Groups.', + }, + ], + }, +] as INodeProperties[]; \ No newline at end of file