From e411e46ef72fc0b6fa7328bd5c6f3bd3089a8231 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 16 Jan 2020 18:51:01 -0500 Subject: [PATCH 1/3] :sparkles: gumroad node --- .../credentials/GumroadApi.credentials.ts | 17 +++ .../nodes/Gumroad/GenericFunctions.ts | 39 +++++ .../nodes/Gumroad/GumroadTrigger.node.ts | 143 ++++++++++++++++++ packages/nodes-base/nodes/Gumroad/gumroad.png | Bin 0 -> 3418 bytes packages/nodes-base/package.json | 2 + 5 files changed, 201 insertions(+) create mode 100644 packages/nodes-base/credentials/GumroadApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Gumroad/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts create mode 100644 packages/nodes-base/nodes/Gumroad/gumroad.png diff --git a/packages/nodes-base/credentials/GumroadApi.credentials.ts b/packages/nodes-base/credentials/GumroadApi.credentials.ts new file mode 100644 index 0000000000..668aa1a1a6 --- /dev/null +++ b/packages/nodes-base/credentials/GumroadApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class GumroadApi implements ICredentialType { + name = 'gumroadApi'; + displayName = 'Gumroad API'; + properties = [ + { + displayName: 'Access Token', + name: 'accessToken', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts new file mode 100644 index 0000000000..11e9e99872 --- /dev/null +++ b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts @@ -0,0 +1,39 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function gumroadApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('gumroadApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + body = Object.assign({ access_token: credentials.accessToken }, body) + + let options: OptionsWithUri = { + method, + qs, + body, + uri: uri ||`https://api.gumroad.com/v2${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) { + let errorMessage = error + if (!error.success) { + errorMessage.message + } + throw new Error('Gumroad Error: ' + errorMessage); + } +} diff --git a/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts b/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts new file mode 100644 index 0000000000..bd4fa04dc1 --- /dev/null +++ b/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts @@ -0,0 +1,143 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, +} from 'n8n-workflow'; + +import { + gumroadApiRequest, +} from './GenericFunctions'; + +export class GumroadTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Gumroad Trigger', + name: 'gumroad', + icon: 'file:gumroad.png', + group: ['trigger'], + version: 1, + description: 'Handle Gumroad events via webhooks', + defaults: { + name: 'Gumroad Trigger', + color: '#60c2cd', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'gumroadApi', + required: true, + } + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + required: true, + default: '', + options: [ + { + name: 'Sale', + value: 'sale', + description: `When subscribed to this resource, you will be notified of the user's sales`, + }, + { + name: 'Refund', + value: 'refund', + description: `When subscribed to this resource, you will be notified of refunds to the user's sales`, + }, + { + name: 'Dispute', + value: 'dispute', + description: `When subscribed to this resource, you will be notified of the disputes raised against user's sales`, + }, + { + name: 'Dispute Won', + value: 'dispute_won', + description: `When subscribed to this resource, you will be notified of the sale disputes won`, + }, + { + name: 'Cancellation', + value: 'cancellation', + description: `When subscribed to this resource, you will be notified of cancellations of the user's subscribers`, + }, + ], + description: 'The resource is gonna fire the event', + }, + ], + + }; + + // @ts-ignore + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId === undefined) { + return false; + } + const endpoint = `/resource_subscriptions`; + const { resource_subscriptions } = await gumroadApiRequest.call(this, 'GET', endpoint); + if (Array.isArray(resource_subscriptions)) { + for (const resource of resource_subscriptions) { + if (resource.id === webhookData.webhookId) { + return true + } + } + } + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + const resource = this.getNodeParameter('resource') as string; + const endpoint = '/resource_subscriptions'; + const body: IDataObject = { + post_url: webhookUrl, + resource_name: resource, + }; + const { resource_subscription } = await gumroadApiRequest.call(this, 'PUT', endpoint, body); + webhookData.webhookId = resource_subscription.id; + return true; + }, + async delete(this: IHookFunctions): Promise { + let responseData; + const webhookData = this.getWorkflowStaticData('node'); + const endpoint = `/resource_subscriptions/${webhookData.webhookId}`; + try { + responseData = await gumroadApiRequest.call(this, 'DELETE', endpoint); + } catch(error) { + return false; + } + if (!responseData.success) { + return false; + } + delete webhookData.webhookId; + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const req = this.getRequestObject(); + return { + workflowData: [ + this.helpers.returnJsonArray(req.body), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/Gumroad/gumroad.png b/packages/nodes-base/nodes/Gumroad/gumroad.png new file mode 100644 index 0000000000000000000000000000000000000000..70f23c5f19afeaa277771ba65b53dd3832c61289 GIT binary patch literal 3418 zcmY*cc{~(e_a207DM@x?$WqLZA=}uAu`k0+D!azmnHWUKl6@;=-}fwIiDW5^?Aut2 zlAVM`65^%r_t)=z@8@&wJ?Gr#KF>YpuX`g5^lmfIveN;u(ozAjH z(;lY_nWxch6+q=6=h|sNHU@Kc!YdbK` z+3gg4%7l|Wb)D_7RzRGylZ&S;4hH&zkUjO!(hv~v4+QH710i({fU2$@c0frm6f6c( zpalYf@*cMKvPNne{~kY$U?4OW>n00%^wX6I?+;oydKaCHHm@mg8CKE%R6pfjPrEvQ7(x zob5ow!D5iV+^0w7&$6=m9u9V=%4hrv;_`ox|C9SyM;>w}{$H2*r_w*!(^3^^ztZ z4tU~T)bD=>`ikDBgE{KAE`-V>*rkh&$)gsn;#}CN|_W7FA!x6{yL!) z$%W$D7=-WfP+GxaMV0Wo8`1pf4Df3U3iJNvG zE%8Wc1a?Z|)rGBNq3f%wOaebtXqyIQ^)h`8_FIP^86W5w{rb$Klj*ss|YcTgTINJ6E35$FpeJ~BEJ6PKo{QNcr zXK-PU;)llv4r{fp`5%`ZRcx~2)E_SUWvnS^MkrI~Iw?F4(gglAESBo{taFg)fF470 zdOSH$!bfYpxxBlPB~ZxiU61oDm)qM?jOG6Ln?DFO>Azg93EQ3}1|4?`##WOh4Jh?D4Ptw@7`g{KFPdOR{%0y0;mGFHG9cu@$G?uG{oCFLQ9t1 z3zp>Kq2V^HfrqhAl&N@|;5^UxS57R`Ebms^4kyy!*%chA>H9P-75%^4a-91enVn81 z-lZj#DJRO-Mx%5Q@{I1>p#p1chEn4*RgcyvY8kR(abh^EhkvG2dE$9$oJ8QWSOys`)zADm)f_#_~w;W54lLb9em#<-2#|b64QSF=pgoG zT6c5xrlRsOQdMv#+Ln3W>Y1USVucsnfnLUW9~;35r{oY8Z2H``a&@Rhm+$43z*qpQ zwn4C-Hhx%*ZSALZPw)yp^|3H%s?U9pvCEo$OtOTMQnx+#EBF1B9Hp{Urp=NY8pF!* zGx6FXWk&7RIp2W6${Qq#%nzTYWN^onbR9ctiP+7+KJp7C!HiT0{-V&e@1B`duc$J$ zUvODyckQYgtEb3Z%wB1}oHKp9Kz+2hB%0drY6QDz=Xq;#Zb?ZM2{n|JT+P#$gbwq8 zDbg0!-pB<~Fa#2v~3ip{JSIs%KtJp=m4eH>0YozTlWU zS|wA%`Qq}Pepstx)r6S5{TAuqTQLov0C4<;x*rpS^GFJ&;+FEvQ$YmEjz1+0&~2w4|%S@OOf zHp+(NBA%}cP`u`3R#6tsg3|w^Z^tKj9;p)4nNp;SK3Ai$`YH-bxQ_Ly<)b=^iJgI(hOqo5i$}`PM_O8x~JbU8!0rU_mOY8!$Lc`H8R4L(DRW1!D+N$7hGx3B3~Zd_=0)H`y&6b zI{IiRt?GwkbT-*$Ly?0QUip(e?7sBy_Gr`Yrel+CXdH>}HQ~ap6(q^m?AwFHYQ4BS zLH2RUAPQ3;jaXM8-K*+5nLIpL+pvi7HueC=yw$DDokeKD`$VM?G^QeVxARmnwe1pLXC^CP>Z58V}kPC%~plk<^@!V?*ox5hj6Q2SXc8`;dY}i zI?o`xAlv7mVIk(r_S@3r(+k?l8v2Uu>}xMzG#PHr}{fdK^TD z)k3wB=-(!XBGYC8UHo=Mct9tMjZ*GI461UBo+#3x#G~gF8V|f5va+X zqW;w)Sy{_(s_kyt(*0kSRWt3Y0wUKMHJ;mabmWGR3-mL|wb_=3Ws=gbmKKM*{qo*O zQF9E^(`K%S4}p;avXW;$P&~ zFG=^(4A}_`4vG_Xi8hTr5+gjSZ0=IM;HY_EUNey;dZd)$6Q@!3yl>jPjE^cFVtud6 zR+`}bi*aZP!E8EFz9fE|HL_>bn`Iwv*AX%I8}$4jNHNCz>X@ZyBN>YBU0T8|G9+Im zh{Hg?gj2~db|E5HF}8s<$V?~4V>^}CSja5r9-zA=s7!9yx=#Dil^*w8F_aruBVkA~ zbARJ-6u$B1oj!zGhW+#BbgwkyO2v^w(EV?ONsOcMHlrgT3~-BFhsg(YE<;HM5c=j^ zG9W*@pqQU0nW6Db4#Oru=V2EsIpf;Ga-v?(7O#e%%lOV)I>Wb2^CN&8CA&$~KOncb zU^8F&TVfHdM6<(I{)aA|Nh165Z#j=D$Py`Q;Pr+R^AQNP&W$oX|6LOGZUXa&-f!ut z@&GY?c9Sp+2RFFH->Z0OKzr-@&249@w(hG((5){$c+97hs3CkFH%;{o-%Akn6t^X~ z!A{*QpMWAV+$R`VT8nSXlP_-J)JM=O)p$w`r+v6IA)ts(#3q>4*UlmTK79Xij;h>0 Zw|$2?vvf5VzB&8wXsPR|RjOD9{|EL%NACat literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index f68ef4e6bb..85b2511544 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -42,6 +42,7 @@ "dist/credentials/GithubApi.credentials.js", "dist/credentials/GitlabApi.credentials.js", "dist/credentials/GoogleApi.credentials.js", + "dist/credentials/GumroadApi.credentials.js", "dist/credentials/HttpBasicAuth.credentials.js", "dist/credentials/HttpDigestAuth.credentials.js", "dist/credentials/HttpHeaderAuth.credentials.js", @@ -119,6 +120,7 @@ "dist/nodes/Google/GoogleDrive.node.js", "dist/nodes/Google/GoogleSheets.node.js", "dist/nodes/GraphQL/GraphQL.node.js", + "dist/nodes/Gumroad/GumroadTrigger.node.js", "dist/nodes/HtmlExtract/HtmlExtract.node.js", "dist/nodes/HttpRequest.node.js", "dist/nodes/Hubspot/Hubspot.node.js", From f03936b468e82fdb738fe733b66ca9737b5a6761 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 16 Jan 2020 18:52:58 -0500 Subject: [PATCH 2/3] :zap: small improvements --- packages/nodes-base/nodes/Gumroad/GenericFunctions.ts | 6 +++--- packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts index 11e9e99872..301ffd56e5 100644 --- a/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts @@ -13,7 +13,7 @@ export async function gumroadApiRequest(this: IHookFunctions | IExecuteFunctions if (credentials === undefined) { throw new Error('No credentials got returned!'); } - body = Object.assign({ access_token: credentials.accessToken }, body) + body = Object.assign({ access_token: credentials.accessToken }, body); let options: OptionsWithUri = { method, @@ -30,9 +30,9 @@ export async function gumroadApiRequest(this: IHookFunctions | IExecuteFunctions try { return await this.helpers.request!(options); } catch (error) { - let errorMessage = error + let errorMessage = error; if (!error.success) { - errorMessage.message + errorMessage.message; } throw new Error('Gumroad Error: ' + errorMessage); } diff --git a/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts b/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts index bd4fa04dc1..807d826aab 100644 --- a/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts +++ b/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts @@ -79,9 +79,7 @@ export class GumroadTrigger implements INodeType { description: 'The resource is gonna fire the event', }, ], - }; - // @ts-ignore webhookMethods = { default: { @@ -90,12 +88,12 @@ export class GumroadTrigger implements INodeType { if (webhookData.webhookId === undefined) { return false; } - const endpoint = `/resource_subscriptions`; + const endpoint = '/resource_subscriptions'; const { resource_subscriptions } = await gumroadApiRequest.call(this, 'GET', endpoint); if (Array.isArray(resource_subscriptions)) { for (const resource of resource_subscriptions) { if (resource.id === webhookData.webhookId) { - return true + return true; } } } From eb4c98618592b508e8504d7efcda01ea432a4481 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 16 Jan 2020 18:28:11 -0600 Subject: [PATCH 3/3] :zap: Small Gumroad-Node improvement --- packages/nodes-base/nodes/Gumroad/GenericFunctions.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts index 301ffd56e5..9025211a6e 100644 --- a/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts @@ -30,10 +30,6 @@ export async function gumroadApiRequest(this: IHookFunctions | IExecuteFunctions try { return await this.helpers.request!(options); } catch (error) { - let errorMessage = error; - if (!error.success) { - errorMessage.message; - } - throw new Error('Gumroad Error: ' + errorMessage); + throw new Error('Gumroad Error: ' + error.message); } }