From e770fa37ad4203dfc8633f52569727645ce747ad Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 2 Dec 2020 17:13:59 -0500 Subject: [PATCH] :sparkles: Add Yourls Node (#1216) * :sparkles: Yourls Node * :zap: Minor improvements to Yourls-Node Co-authored-by: Jan Oberhauser --- .../credentials/YourlsApi.credentials.ts | 25 ++++ .../nodes/Yourls/GenericFunctions.ts | 52 +++++++ .../nodes-base/nodes/Yourls/UrlDescription.ts | 137 ++++++++++++++++++ .../nodes-base/nodes/Yourls/Yourls.node.ts | 103 +++++++++++++ packages/nodes-base/nodes/Yourls/yourls.png | Bin 0 -> 1441 bytes packages/nodes-base/package.json | 2 + 6 files changed, 319 insertions(+) create mode 100644 packages/nodes-base/credentials/YourlsApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Yourls/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Yourls/UrlDescription.ts create mode 100644 packages/nodes-base/nodes/Yourls/Yourls.node.ts create mode 100644 packages/nodes-base/nodes/Yourls/yourls.png diff --git a/packages/nodes-base/credentials/YourlsApi.credentials.ts b/packages/nodes-base/credentials/YourlsApi.credentials.ts new file mode 100644 index 0000000000000..8752f9a6e8589 --- /dev/null +++ b/packages/nodes-base/credentials/YourlsApi.credentials.ts @@ -0,0 +1,25 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class YourlsApi implements ICredentialType { + name = 'yourlsApi'; + displayName = 'Yourls API'; + documentationUrl = 'yourls'; + properties = [ + { + displayName: 'Signature', + name: 'signature', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'URL', + name: 'url', + type: 'string' as NodePropertyTypes, + default: '', + placeholder: 'http://localhost:8080', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Yourls/GenericFunctions.ts b/packages/nodes-base/nodes/Yourls/GenericFunctions.ts new file mode 100644 index 0000000000000..3ba2f5e3c52bd --- /dev/null +++ b/packages/nodes-base/nodes/Yourls/GenericFunctions.ts @@ -0,0 +1,52 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function yourlsApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, body: any = {}, qs: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const credentials = this.getCredentials('yourlsApi') as IDataObject; + + qs.signature = credentials.signature as string; + qs.format = 'json'; + + const options: OptionsWithUri = { + method, + body, + qs, + uri: `${credentials.url}/yourls-api.php`, + json: true, + }; + try { + //@ts-ignore + const response = await this.helpers.request.call(this, options); + + if (response.status === 'fail') { + throw new Error( + `Yourls error response [400]: ${response.message}`, + ); + } + + return response; + } catch (error) { + if (error.response && error.response.body && error.response.body.msg) { + + const message = error.response.body.msg; + + // Try to return the error prettier + throw new Error( + `Yourls error response [${error.statusCode}]: ${message}`, + ); + } + throw error; + } +} diff --git a/packages/nodes-base/nodes/Yourls/UrlDescription.ts b/packages/nodes-base/nodes/Yourls/UrlDescription.ts new file mode 100644 index 0000000000000..ccfdaef0ac2fb --- /dev/null +++ b/packages/nodes-base/nodes/Yourls/UrlDescription.ts @@ -0,0 +1,137 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const urlOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'url', + ], + }, + }, + options: [ + { + name: 'Expand', + value: 'expand', + description: 'Expand a URL', + }, + { + name: 'Shorten', + value: 'shorten', + description: 'Shorten a URL', + }, + { + name: 'Stats', + value: 'stats', + description: 'Get stats about one short URL', + }, + ], + default: 'shorten', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const urlFields = [ + + /* -------------------------------------------------------------------------- */ + /* url:shorten */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'URL', + name: 'url', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'url', + ], + operation: [ + 'shorten', + ], + }, + }, + default: '', + description: 'The URL to shorten.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'url', + ], + operation: [ + 'shorten', + ], + }, + }, + options: [ + { + displayName: 'Keyword', + name: 'keyword', + type: 'string', + default: '', + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Title for custom short URLs', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* url:expand */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Short URL', + name: 'shortUrl', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'url', + ], + operation: [ + 'expand', + ], + }, + }, + default: '', + description: 'The short URL to expand.', + }, + + /* -------------------------------------------------------------------------- */ + /* url:stats */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Short URL', + name: 'shortUrl', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'url', + ], + operation: [ + 'stats', + ], + }, + }, + default: '', + description: 'The short URL for which to get stats.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Yourls/Yourls.node.ts b/packages/nodes-base/nodes/Yourls/Yourls.node.ts new file mode 100644 index 0000000000000..0ddf94c7a193b --- /dev/null +++ b/packages/nodes-base/nodes/Yourls/Yourls.node.ts @@ -0,0 +1,103 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + yourlsApiRequest, +} from './GenericFunctions'; + +import { + urlFields, + urlOperations, +} from './UrlDescription'; + +export class Yourls implements INodeType { + description: INodeTypeDescription = { + displayName: 'Yourls', + name: 'yourls', + icon: 'file:yourls.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Yourls API', + defaults: { + name: 'Yourls', + color: '#336498', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'yourlsApi', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'URL', + value: 'url', + }, + ], + default: 'url', + description: 'The resource to operate on.', + }, + ...urlOperations, + ...urlFields, + ], + }; + + 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 === 'url') { + if (operation === 'shorten') { + const url = this.getNodeParameter('url', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + qs.url = url; + qs.action = 'shorturl'; + Object.assign(qs, additionalFields); + responseData = await yourlsApiRequest.call(this, 'GET', {}, qs); + } + + if (operation === 'expand') { + const shortUrl = this.getNodeParameter('shortUrl', i) as string; + qs.shorturl = shortUrl; + qs.action = 'expand'; + responseData = await yourlsApiRequest.call(this, 'GET', {}, qs); + } + + if (operation === 'stats') { + const shortUrl = this.getNodeParameter('shortUrl', i) as string; + qs.shorturl = shortUrl; + qs.action = 'url-stats'; + responseData = await yourlsApiRequest.call(this, 'GET', {}, qs); + responseData = responseData.link; + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Yourls/yourls.png b/packages/nodes-base/nodes/Yourls/yourls.png new file mode 100644 index 0000000000000000000000000000000000000000..b7732e433c2a2438bc56b15feb3a1173cd099589 GIT binary patch literal 1441 zcmX9-2{_bg82^hHldD~Z{&W3jMvgpKk~@uS7;@BDXTnToCdW8(v{Wb*Av6^=2e~Oq z*c@Y9vR2sHU6oKhi5xwYs2rJT|MTqgyx;e|-+O((_xC~VN0iIofXA9vl0*ePlSHoUdhXwZHnS3Ir0p1O1WimS)#^U4QTv%ZX_Jv*| z(o1j=Tw;8a%*7VMb{tE94l4$I({(&kk$4LTF3#jbQI&X>EEj%PRw42x_IL@F$=5q} zNsAhfJ5h#>xC;8^V8e^Gs0rA}a+C*0g|uJODPAKynG_AD2%)Gd9FzY)1|my9imHIv z0s^Z78_7cl0A~?m>hw+uap7D@27reRD*%TBef@hYUbkv&$>`+(OxkI|GrJ^jANCpRLzHP2oNw$|Z zH$F>d6{TBSv*JH2_hKbq-;X`Hz%u#b<6 zovAJ+%p4^K0C^16#nI1waGC2yn<4|0RMBMUR4G(S91_uA#-=HYa!^qQ1kt);^*T{` zg1i0h&qvva5#x)PZD#K$wos513lI-M=1GpK@^p?B#i(sg3EyE+JpVS zc7Gec+Z}`K|04ZRUOurYc2!7dPxg01*H{Lh?1Fo^sQHO(nX5N6GN;CB7CR)zl7|n&O*tQ<)LcVIHu|ZowtUZxxfJQ zKx7W9$#WE$LHg(K_{Gw8bWerJT?Zpg@HrITTWHjW=@=+#V}2!m7Ln+yJwfz?TyQ=$ zRUC3v)kC+#&nB*SnOyIq9fFQcFcqf)7CN73Pj-78a2~Vna!zr|_Prf+gBy=g-8+>_ zSm1W<4Y3#LtI4rOrnt?Q$H$*N3lNG)zolHL)ax8WDHZrz>F5MJoj0i)OP)+q0ME){ z_H>M*XqGd%qEe}%oi|IfA$S<6Gn_Wu@o<98AZPqIWjXTjrOHfmuVYqQ55O>UY#+Fq z-m9=-L&&?opCuO9yvV7-3fl9X&M4^K%y5W{N>va_o;@3#oL zd#odS5RY@I2$}1SO*o&}tKf9Gu~#H&7rm>)HN;3hIP2zZ8EH?Y#Q>EQ;T*bkV4=%^ z#YeQ9?GlPjdn6nAkq@-R{BE}Kp`l$_>$`SsZcC&`?B@!Gy~az7b4{y$NMBz3ov>Da zeD_oa2K0^59-1Z1y&?FtC<_*b8D^)8xqS`Iy%e_@a-G#|_o{-zw1eZyY`)K0idtY} zi;-5XR0+spB>&Z7VB4hV7i;>K`ubqn^7)Zhx>}w$CmUPSTvp~E4YkMx7Nz&|B6RK~ z+Ps#3Q_^=&|6EXn@qqH{CG$)T-ks2}`Pv5o4BEOs1!(Ioxt2~VZ@RzO89AQSSV~f4 rSTQE{eII>j#w?DEdRl*ECTtbWjkwk6=u)u(|673S>h03(6n_3cf&y)a literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index c935133f0f43a..60e6dd98846d5 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -219,6 +219,7 @@ "dist/credentials/WordpressApi.credentials.js", "dist/credentials/WufooApi.credentials.js", "dist/credentials/XeroOAuth2Api.credentials.js", + "dist/credentials/YourlsApi.credentials.js", "dist/credentials/ZendeskApi.credentials.js", "dist/credentials/ZendeskOAuth2Api.credentials.js", "dist/credentials/ZohoOAuth2Api.credentials.js", @@ -448,6 +449,7 @@ "dist/nodes/Wufoo/WufooTrigger.node.js", "dist/nodes/Xero/Xero.node.js", "dist/nodes/Xml.node.js", + "dist/nodes/Yourls/Yourls.node.js", "dist/nodes/Zendesk/Zendesk.node.js", "dist/nodes/Zendesk/ZendeskTrigger.node.js", "dist/nodes/Zoho/ZohoCrm.node.js",