From 9e0ba42945f8a97afc98fd8d4a9e2f4bf739206b Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 5 Jan 2020 18:47:55 -0500 Subject: [PATCH 1/4] :sparkles: bitbucket trigger --- .../credentials/BitbucketApi.credentials.ts | 23 ++ .../nodes/Bitbucket/BitbucketTrigger.node.ts | 358 ++++++++++++++++++ .../nodes/Bitbucket/GenericFunctions.ts | 63 +++ .../nodes-base/nodes/Bitbucket/bitbucket.png | Bin 0 -> 3113 bytes packages/nodes-base/package.json | 8 +- 5 files changed, 449 insertions(+), 3 deletions(-) create mode 100644 packages/nodes-base/credentials/BitbucketApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts create mode 100644 packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Bitbucket/bitbucket.png diff --git a/packages/nodes-base/credentials/BitbucketApi.credentials.ts b/packages/nodes-base/credentials/BitbucketApi.credentials.ts new file mode 100644 index 0000000000..adb3375d52 --- /dev/null +++ b/packages/nodes-base/credentials/BitbucketApi.credentials.ts @@ -0,0 +1,23 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class BitbucketApi implements ICredentialType { + name = 'bitbucketApi'; + displayName = 'Bitbucket API'; + properties = [ + { + displayName: 'Username', + name: 'username', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'APP Password', + name: 'appPassword', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts b/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts new file mode 100644 index 0000000000..41c0d11c46 --- /dev/null +++ b/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts @@ -0,0 +1,358 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + INodeTypeDescription, + INodeType, + IWebhookResponseData, + ILoadOptionsFunctions, + INodePropertyOptions, + IDataObject, +} from 'n8n-workflow'; + +import { + bitbucketApiRequest, + bitbucketApiRequestAllItems, +} from './GenericFunctions'; + +export class BitbucketTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Bitbucket Trigger', + name: 'bitbucket', + icon: 'file:bitbucket.png', + group: ['trigger'], + version: 1, + description: 'Handle Bitbucket events via webhooks', + defaults: { + name: 'Bitbucket Trigger', + color: '#559922', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'bitbucketApi', + required: true, + } + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + required: true, + options: [ + { + name: 'User', + value: 'user', + }, + { + name: 'Team', + value: 'team', + }, + { + name: 'Repository', + value: 'repository', + }, + ], + default: 'user', + description: '', + }, + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + displayOptions: { + show: { + resource: [ + 'user' + ] + } + }, + typeOptions: { + loadOptionsMethod: 'getUsersEvents', + }, + options: [], + required: true, + default: [], + description: '', + }, + { + displayName: 'Team', + name: 'team', + type: 'options', + displayOptions: { + show: { + resource: [ + 'team' + ] + } + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + default: '', + description: '', + }, + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + displayOptions: { + show: { + resource: [ + 'team' + ] + } + }, + typeOptions: { + loadOptionsMethod: 'getTeamEvents', + }, + options: [], + required: true, + default: [], + description: '', + }, + { + displayName: 'Repository', + name: 'repository', + type: 'options', + displayOptions: { + show: { + resource: [ + 'repository' + ] + } + }, + typeOptions: { + loadOptionsMethod: 'getRepositories', + }, + required: true, + default: '', + description: '', + }, + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + displayOptions: { + show: { + resource: [ + 'repository' + ] + } + }, + typeOptions: { + loadOptionsMethod: 'getRepositoriesEvents', + }, + options: [], + required: true, + default: [], + description: '', + }, + ], + + }; + + methods = { + loadOptions: { + // Get all the events to display them to user so that he can + // select them easily + async getUsersEvents(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/user'); + for (const event of events) { + const eventName = event.event; + const eventId = event.event; + const eventDescription = event.description; + returnData.push({ + name: eventName, + value: eventId, + description: eventDescription, + }); + } + return returnData; + }, + // Get all the events to display them to user so that he can + // select them easily + async getTeamEvents(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/team'); + for (const event of events) { + const eventName = event.event; + const eventId = event.event; + const eventDescription = event.description; + returnData.push({ + name: eventName, + value: eventId, + description: eventDescription, + }); + } + return returnData; + }, + // Get all the events to display them to user so that he can + // select them easily + async getRepositoriesEvents(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/repository'); + for (const event of events) { + const eventName = event.event; + const eventId = event.event; + const eventDescription = event.description; + returnData.push({ + name: eventName, + value: eventId, + description: eventDescription, + }); + } + return returnData; + }, + // Get all the repositories to display them to user so that he can + // select them easily + async getRepositories(this: ILoadOptionsFunctions): Promise { + const credentials = this.getCredentials('bitbucketApi'); + const returnData: INodePropertyOptions[] = []; + const repositories = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', `/repositories/${credentials!.username}`); + for (const repository of repositories) { + const repositoryName = repository.slug; + const repositoryId = repository.slug; + const repositoryDescription = repository.description; + returnData.push({ + name: repositoryName, + value: repositoryId, + description: repositoryDescription, + }); + } + return returnData; + }, + // Get all the teams to display them to user so that he can + // select them easily + async getTeams(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const qs: IDataObject = { + role: 'member', + }; + const teams = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/teams', {}, qs); + for (const team of teams) { + const teamName = team.display_name; + const teamId = team.username; + returnData.push({ + name: teamName, + value: teamId, + }); + } + return returnData; + }, + }, + }; + // @ts-ignore + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + let endpoint: string = ''; + const credentials = this.getCredentials('bitbucketApi'); + const resource = this.getNodeParameter('resource', 0) as string; + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId === undefined) { + return false; + } + if (resource === 'user') { + endpoint = `/users/${credentials!.username}/hooks/${webhookData.webhookId}`; + } + if (resource === 'team') { + const team = this.getNodeParameter('team', 0) as string; + endpoint = `/teams/${team}/hooks/${webhookData.webhookId}`; + } + if (resource === 'repository') { + const repository = this.getNodeParameter('repository', 0) as string; + endpoint = `/repositories/${credentials!.username}/${repository}/hooks/${webhookData.webhookId}`; + } + try { + await bitbucketApiRequest.call(this, 'GET', endpoint); + } catch (e) { + return false; + } + return true; + }, + async create(this: IHookFunctions): Promise { + let responseData; + let endpoint: string = ''; + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + const events = this.getNodeParameter('events') as string[]; + const resource = this.getNodeParameter('resource', 0) as string; + const credentials = this.getCredentials('bitbucketApi'); + + if (resource === 'user') { + endpoint = `/users/${credentials!.username}/hooks`; + } + if (resource === 'team') { + const team = this.getNodeParameter('team', 0) as string; + endpoint = `/teams/${team}/hooks`; + } + if (resource === 'repository') { + const repository = this.getNodeParameter('repository', 0) as string; + endpoint = `/repositories/${credentials!.username}/${repository}/hooks`; + } + const body: IDataObject = { + description: 'N8N webhook', + url: webhookUrl, + active: true, + events, + }; + responseData = await bitbucketApiRequest.call(this, 'POST', endpoint, body); + webhookData.webhookId = responseData.uuid.replace('{', '').replace('}', ''); + return true; + }, + async delete(this: IHookFunctions): Promise { + let endpoint: string = ''; + const webhookData = this.getWorkflowStaticData('node'); + const credentials = this.getCredentials('bitbucketApi'); + const resource = this.getNodeParameter('resource', 0) as string; + if (resource === 'user') { + endpoint = `/users/${credentials!.username}/hooks/${webhookData.webhookId}`; + } + if (resource === 'team') { + const team = this.getNodeParameter('team', 0) as string; + endpoint = `/teams/${team}/hooks/${webhookData.webhookId}`; + } + if (resource === 'repository') { + const repository = this.getNodeParameter('repository', 0) as string; + endpoint = `/repositories/${credentials!.username}/${repository}/hooks/${webhookData.webhookId}`; + } + try { + await bitbucketApiRequest.call(this, 'DELETE', endpoint); + } catch(error) { + return false; + } + delete webhookData.webhookId; + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const req = this.getRequestObject(); + const headerData = this.getHeaderData() as IDataObject; + const webhookData = this.getWorkflowStaticData('node'); + if (headerData['x-hook-uuid'] !== webhookData.webhookId) { + return {}; + } + return { + workflowData: [ + this.helpers.returnJsonArray(req.body) + ], + }; + } +} diff --git a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts new file mode 100644 index 0000000000..76e8e539d9 --- /dev/null +++ b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts @@ -0,0 +1,63 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IExecuteSingleFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function bitbucketApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('bitbucketApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + const userpass = `${credentials.username}:${credentials.appPassword}` + console.log() + let options: OptionsWithUri = { + method, + qs, + body, + uri: uri ||`https://${userpass}@api.bitbucket.org/2.0${resource}`, + json: true + }; + options = Object.assign({}, options, option); + if (Object.keys(options.body).length === 0) { + delete options.body; + } + + try { + return await this.helpers.request!(options); + } catch (error) { + console.log(error) + let errorMessage = error.message; + if (error.response.body) { + errorMessage = error.response.body.message || error.response.body.Message || error.message; + } + + throw new Error(errorMessage); + } +} + +/** + * Make an API request to paginated flow endpoint + * and return all results + */ +export async function bitbucketApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + let uri: string | undefined; + + do { + responseData = await bitbucketApiRequest.call(this, method, resource, body, query, uri); + uri = responseData.next + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData.next !== undefined + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/Bitbucket/bitbucket.png b/packages/nodes-base/nodes/Bitbucket/bitbucket.png new file mode 100644 index 0000000000000000000000000000000000000000..e064031b9e1cc69be2f9c8602ebfe8820d512b72 GIT binary patch literal 3113 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 literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index dc5747eb36..3d0286066e 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -31,6 +31,7 @@ "dist/credentials/Amqp.credentials.js", "dist/credentials/AsanaApi.credentials.js", "dist/credentials/Aws.credentials.js", + "dist/credentials/BitbucketApi.credentials.js", "dist/credentials/ChargebeeApi.credentials.js", "dist/credentials/CodaApi.credentials.js", "dist/credentials/DropboxApi.credentials.js", @@ -71,9 +72,9 @@ "dist/credentials/TwilioApi.credentials.js", "dist/credentials/TypeformApi.credentials.js", "dist/credentials/MandrillApi.credentials.js", - "dist/credentials/TodoistApi.credentials.js", - "dist/credentials/TypeformApi.credentials.js", - "dist/credentials/TogglApi.credentials.js", + "dist/credentials/TodoistApi.credentials.js", + "dist/credentials/TypeformApi.credentials.js", + "dist/credentials/TogglApi.credentials.js", "dist/credentials/VeroApi.credentials.js", "dist/credentials/WordpressApi.credentials.js" ], @@ -86,6 +87,7 @@ "dist/nodes/Asana/AsanaTrigger.node.js", "dist/nodes/Aws/AwsLambda.node.js", "dist/nodes/Aws/AwsSns.node.js", + "dist/nodes/Bitbucket/BitbucketTrigger.node.js", "dist/nodes/Chargebee/Chargebee.node.js", "dist/nodes/Chargebee/ChargebeeTrigger.node.js", "dist/nodes/Cron.node.js", From dff0a14d73dacc1dba1987f8d3bd5ed57b9c9c79 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 5 Jan 2020 19:07:28 -0500 Subject: [PATCH 2/4] :zap: added error message --- .../nodes-base/nodes/Bitbucket/GenericFunctions.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts index 76e8e539d9..c9f3624041 100644 --- a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts @@ -28,13 +28,11 @@ export async function bitbucketApiRequest(this: IHookFunctions | IExecuteFunctio try { return await this.helpers.request!(options); - } catch (error) { - console.log(error) - let errorMessage = error.message; - if (error.response.body) { - errorMessage = error.response.body.message || error.response.body.Message || error.message; + } catch (err) { + let errorMessage = ''; + if (err.error && err.error.message) { + errorMessage = err.error.message; } - throw new Error(errorMessage); } } From 406f96dd8243366464e0143caa7f7eb185fff319 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 5 Jan 2020 20:22:09 -0500 Subject: [PATCH 3/4] :zap: removed console.log --- packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts index c9f3624041..ff90213f4f 100644 --- a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts @@ -13,7 +13,6 @@ export async function bitbucketApiRequest(this: IHookFunctions | IExecuteFunctio throw new Error('No credentials got returned!'); } const userpass = `${credentials.username}:${credentials.appPassword}` - console.log() let options: OptionsWithUri = { method, qs, From b1237c756b257cbc48746175f486b63afb4fa70d Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 6 Jan 2020 12:33:22 -0600 Subject: [PATCH 4/4] :zap: Some minor improvements --- .../credentials/BitbucketApi.credentials.ts | 2 +- .../nodes/Bitbucket/BitbucketTrigger.node.ts | 32 +++++++++---------- .../nodes/Bitbucket/GenericFunctions.ts | 17 +++++----- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/nodes-base/credentials/BitbucketApi.credentials.ts b/packages/nodes-base/credentials/BitbucketApi.credentials.ts index adb3375d52..5a12902fe3 100644 --- a/packages/nodes-base/credentials/BitbucketApi.credentials.ts +++ b/packages/nodes-base/credentials/BitbucketApi.credentials.ts @@ -14,7 +14,7 @@ export class BitbucketApi implements ICredentialType { default: '', }, { - displayName: 'APP Password', + displayName: 'App Password', name: 'appPassword', type: 'string' as NodePropertyTypes, default: '', diff --git a/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts b/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts index 41c0d11c46..1e506deb92 100644 --- a/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts +++ b/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts @@ -4,12 +4,12 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - IWebhookResponseData, - ILoadOptionsFunctions, - INodePropertyOptions, IDataObject, + ILoadOptionsFunctions, + INodeType, + INodeTypeDescription, + INodePropertyOptions, + IWebhookResponseData, } from 'n8n-workflow'; import { @@ -27,7 +27,7 @@ export class BitbucketTrigger implements INodeType { description: 'Handle Bitbucket events via webhooks', defaults: { name: 'Bitbucket Trigger', - color: '#559922', + color: '#0052cc', }, inputs: [], outputs: ['main'], @@ -66,7 +66,7 @@ export class BitbucketTrigger implements INodeType { }, ], default: 'user', - description: '', + description: 'The resource to operate on.', }, { displayName: 'Events', @@ -85,7 +85,7 @@ export class BitbucketTrigger implements INodeType { options: [], required: true, default: [], - description: '', + description: 'The events to listen to.', }, { displayName: 'Team', @@ -103,7 +103,7 @@ export class BitbucketTrigger implements INodeType { }, required: true, default: '', - description: '', + description: 'The team of which to listen to the events.', }, { displayName: 'Events', @@ -122,7 +122,7 @@ export class BitbucketTrigger implements INodeType { options: [], required: true, default: [], - description: '', + description: 'The events to listen to.', }, { displayName: 'Repository', @@ -140,7 +140,7 @@ export class BitbucketTrigger implements INodeType { }, required: true, default: '', - description: '', + description: 'The repository of which to listen to the events.', }, { displayName: 'Events', @@ -159,7 +159,7 @@ export class BitbucketTrigger implements INodeType { options: [], required: true, default: [], - description: '', + description: 'The events to listen to.', }, ], @@ -260,7 +260,7 @@ export class BitbucketTrigger implements INodeType { webhookMethods = { default: { async checkExists(this: IHookFunctions): Promise { - let endpoint: string = ''; + let endpoint = ''; const credentials = this.getCredentials('bitbucketApi'); const resource = this.getNodeParameter('resource', 0) as string; const webhookData = this.getWorkflowStaticData('node'); @@ -287,7 +287,7 @@ export class BitbucketTrigger implements INodeType { }, async create(this: IHookFunctions): Promise { let responseData; - let endpoint: string = ''; + let endpoint = ''; const webhookUrl = this.getNodeWebhookUrl('default'); const webhookData = this.getWorkflowStaticData('node'); const events = this.getNodeParameter('events') as string[]; @@ -306,7 +306,7 @@ export class BitbucketTrigger implements INodeType { endpoint = `/repositories/${credentials!.username}/${repository}/hooks`; } const body: IDataObject = { - description: 'N8N webhook', + description: 'n8n webhook', url: webhookUrl, active: true, events, @@ -316,7 +316,7 @@ export class BitbucketTrigger implements INodeType { return true; }, async delete(this: IHookFunctions): Promise { - let endpoint: string = ''; + let endpoint = ''; const webhookData = this.getWorkflowStaticData('node'); const credentials = this.getCredentials('bitbucketApi'); const resource = this.getNodeParameter('resource', 0) as string; diff --git a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts index ff90213f4f..5f9c784464 100644 --- a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts @@ -1,9 +1,9 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, } from 'n8n-core'; import { IDataObject } from 'n8n-workflow'; @@ -12,12 +12,15 @@ export async function bitbucketApiRequest(this: IHookFunctions | IExecuteFunctio if (credentials === undefined) { throw new Error('No credentials got returned!'); } - const userpass = `${credentials.username}:${credentials.appPassword}` let options: OptionsWithUri = { method, + auth: { + user: credentials.username as string, + password: credentials.appPassword as string, + }, qs, body, - uri: uri ||`https://${userpass}@api.bitbucket.org/2.0${resource}`, + uri: uri ||`https://api.bitbucket.org/2.0${resource}`, json: true }; options = Object.assign({}, options, option); @@ -28,11 +31,7 @@ export async function bitbucketApiRequest(this: IHookFunctions | IExecuteFunctio try { return await this.helpers.request!(options); } catch (err) { - let errorMessage = ''; - if (err.error && err.error.message) { - errorMessage = err.error.message; - } - throw new Error(errorMessage); + throw new Error('Bitbucket Error: ' + err.message); } } @@ -50,7 +49,7 @@ export async function bitbucketApiRequestAllItems(this: IHookFunctions | IExecut do { responseData = await bitbucketApiRequest.call(this, method, resource, body, query, uri); - uri = responseData.next + uri = responseData.next; returnData.push.apply(returnData, responseData[propertyName]); } while ( responseData.next !== undefined