From f34a1d577cbbbcd10fa2fafb0335861ac92c7d42 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 5 Apr 2020 15:49:47 +0200 Subject: [PATCH] :zap: Small improvements on Cockpit-Node --- .../nodes-base/nodes/Cockpit/Cockpit.node.ts | 83 ++++++++++----- .../nodes/Cockpit/CollectionDescription.ts | 96 +++++++++++------- .../nodes/Cockpit/CollectionFunctions.ts | 50 +++++---- .../nodes/Cockpit/FormDescription.ts | 16 +++ .../nodes/Cockpit/GenericFunctions.ts | 7 +- .../nodes/Cockpit/SingletonDescription.ts | 29 +++++- .../nodes/Cockpit/SingletonFunctions.ts | 6 +- packages/nodes-base/nodes/Cockpit/cockpit.png | Bin 2944 -> 834 bytes 8 files changed, 201 insertions(+), 86 deletions(-) diff --git a/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts b/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts index 7680f85d94..350531f78f 100644 --- a/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts +++ b/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts @@ -1,25 +1,34 @@ import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, + ILoadOptionsFunctions, INodeExecutionData, + INodePropertyOptions, INodeType, - INodeTypeDescription + INodeTypeDescription, } from 'n8n-workflow'; import { collectionFields, - collectionOperations + collectionOperations, } from './CollectionDescription'; import { - getCollectionEntries, - saveCollectionEntry + createCollectionEntry, + getAllCollectionEntries, + getAllCollectionNames, } from './CollectionFunctions'; import { formFields, formOperations } from './FormDescription'; import { submitForm } from './FormFunctions'; -import { singletonOperations } from "./SingletonDescription"; -import { getSingleton } from "./SingletonFunctions"; +import { + singletonFields, + singletonOperations, +} from './SingletonDescription'; +import { + getAllSingleton, + getAllSingletonNames, +} from './SingletonFunctions'; export class Cockpit implements INodeType { description: INodeTypeDescription = { @@ -28,7 +37,7 @@ export class Cockpit implements INodeType { icon: 'file:cockpit.png', group: ['output'], version: 1, - subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"] + "/" + $parameter["resourceName"]}}', + subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}', description: 'Consume Cockpit API', defaults: { name: 'Cockpit', @@ -64,57 +73,81 @@ export class Cockpit implements INodeType { }, ], }, - { - displayName: 'Resource name', - name: 'resourceName', - type: 'string', - default: '', - required: true, - description: 'Name of resource to consume.' - }, + + ...collectionOperations, ...collectionFields, ...formOperations, ...formFields, ...singletonOperations, + ...singletonFields, ], }; + + methods = { + loadOptions: { + async getCollections(this: ILoadOptionsFunctions): Promise { + const collections = await getAllCollectionNames.call(this); + + return collections.map(itemName => { + return { + name: itemName, + value: itemName, + } + }); + }, + + async getSingletons(this: ILoadOptionsFunctions): Promise { + const singletons = await getAllSingletonNames.call(this); + + return singletons.map(itemName => { + return { + name: itemName, + value: itemName, + } + }); + }, + }, + }; + async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; const length = items.length as unknown as number; const resource = this.getNodeParameter('resource', 0) as string; - const resourceName = this.getNodeParameter('resourceName', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; let responseData; for (let i = 0; i < length; i++) { if (resource === 'collections') { - if (operation === 'save') { + const collectionName = this.getNodeParameter('collection', i) as string; + if (operation === 'create') { const data = this.getNodeParameter('data', i) as IDataObject; - responseData = await saveCollectionEntry.call(this, resourceName, data); - } else if (operation === 'get') { - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + responseData = await createCollectionEntry.call(this, collectionName, data); + } else if (operation === 'getAll') { + const options = this.getNodeParameter('options', i) as IDataObject; - responseData = await getCollectionEntries.call(this, resourceName, additionalFields); + responseData = await getAllCollectionEntries.call(this, collectionName, options); } else if (operation === 'update') { const id = this.getNodeParameter('id', i) as string; const data = this.getNodeParameter('data', i) as IDataObject; - responseData = await saveCollectionEntry.call(this, resourceName, data, id); + responseData = await createCollectionEntry.call(this, collectionName, data, id); } } else if (resource === 'forms') { + const formName = this.getNodeParameter('form', i) as string; if (operation === 'submit') { const form = this.getNodeParameter('form', i) as IDataObject; - responseData = await submitForm.call(this, resourceName, form); + responseData = await submitForm.call(this, formName, form); } } else if (resource === 'singletons') { - if (operation === 'get') { - responseData = await getSingleton.call(this, resourceName); + const singletonName = this.getNodeParameter('singleton', i) as string; + if (operation === 'getAll') { + responseData = await getAllSingleton.call(this, singletonName); } } diff --git a/packages/nodes-base/nodes/Cockpit/CollectionDescription.ts b/packages/nodes-base/nodes/Cockpit/CollectionDescription.ts index 71483c3095..cdcba40d2c 100644 --- a/packages/nodes-base/nodes/Cockpit/CollectionDescription.ts +++ b/packages/nodes-base/nodes/Cockpit/CollectionDescription.ts @@ -15,12 +15,12 @@ export const collectionOperations = [ options: [ { name: 'Create an entry', - value: 'save', + value: 'create', description: 'Create a collection entry', }, { name: 'Get all entries', - value: 'get', + value: 'getAll', description: 'Get all collection entries', }, { @@ -29,13 +29,32 @@ export const collectionOperations = [ description: 'Update a collection entries', }, ], - default: 'get', + default: 'getAll', description: 'The operation to perform.', - } + }, ] as INodeProperties[]; export const collectionFields = [ - // Collections:entry:save + { + displayName: 'Collection', + name: 'collection', + type: 'options', + default: '', + typeOptions: { + loadOptionsMethod: 'getCollections', + }, + displayOptions: { + show: { + resource: [ + 'collections', + ], + }, + }, + required: true, + description: 'Name of the collection to operate on.' + }, + + // Collections:entry:create { displayName: 'Data', name: 'data', @@ -51,19 +70,19 @@ export const collectionFields = [ 'collections', ], operation: [ - 'save', + 'create', ] }, }, - description: 'The data to save.', + description: 'The data to create.', }, - // Collections:entry:get + // Collections:entry:getAll { - displayName: 'Additional fields', - name: 'additionalFields', + displayName: 'Options', + name: 'options', type: 'collection', - placeholder: 'Add field', + placeholder: 'Add Option', default: {}, displayOptions: { show: { @@ -71,7 +90,7 @@ export const collectionFields = [ 'collections', ], operation: [ - 'get', + 'getAll', ] }, }, @@ -96,6 +115,13 @@ export const collectionFields = [ }, description: 'Filter result by fields.', }, + { + displayName: 'Language', + name: 'language', + type: 'string', + default: '', + description: 'Return normalized language fields.', + }, { displayName: 'Limit', name: 'limit', @@ -103,6 +129,29 @@ export const collectionFields = [ default: '', description: 'Limit number of returned entries.', }, + { + displayName: 'Populate', + name: 'populate', + type: 'boolean', + required: true, + default: true, + description: 'Resolve linked collection items.', + }, + { + displayName: 'RAW Data', + name: 'rawData', + type: 'boolean', + default: false, + description: `Returns the data exactly in the way it got received from the API.`, + }, + { + displayName: 'Simple', + name: 'simple', + type: 'boolean', + required: true, + default: true, + description: 'Return only result entries.', + }, { displayName: 'Skip', name: 'skip', @@ -117,29 +166,6 @@ export const collectionFields = [ default: '', description: 'Sort result by fields.', }, - { - displayName: 'Populate', - name: 'populate', - type: 'boolean', - required: true, - default: true, - description: 'Resolve linked collection items.', - }, - { - displayName: 'Simple', - name: 'simple', - type: 'boolean', - required: true, - default: true, - description: 'Return only result entries.', - }, - { - displayName: 'Language', - name: 'language', - type: 'string', - default: '', - description: 'Return normalized language fields.', - }, ], }, diff --git a/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts b/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts index fff760ba02..b2b8531b0f 100644 --- a/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts +++ b/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts @@ -7,7 +7,7 @@ import { IDataObject } from 'n8n-workflow'; import { ICollection } from './CollectionInterface'; import { cockpitApiRequest } from './GenericFunctions'; -export async function saveCollectionEntry(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resourceName: string, data: IDataObject, id?: string): Promise { // tslint:disable-line:no-any +export async function createCollectionEntry(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resourceName: string, data: IDataObject, id?: string): Promise { // tslint:disable-line:no-any const body: ICollection = { data: JSON.parse(data.toString()) }; @@ -22,40 +22,52 @@ export async function saveCollectionEntry(this: IExecuteFunctions | IExecuteSing return cockpitApiRequest.call(this, 'post', `/collections/save/${resourceName}`, body); } -export async function getCollectionEntries(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resourceName: string, additionalFields: IDataObject): Promise { // tslint:disable-line:no-any + +export async function getAllCollectionEntries(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resourceName: string, options: IDataObject): Promise { // tslint:disable-line:no-any const body: ICollection = {}; - if (additionalFields.fields) { - body.fields = JSON.parse(additionalFields.fields.toString()); + if (options.fields) { + body.fields = JSON.parse(options.fields.toString()); } - if (additionalFields.filter) { - body.filter = JSON.parse(additionalFields.filter.toString()); + if (options.filter) { + body.filter = JSON.parse(options.filter.toString()); } - if (additionalFields.limit) { - body.limit = additionalFields.limit as number; + if (options.limit) { + body.limit = options.limit as number; } - if (additionalFields.skip) { - body.skip = additionalFields.skip as number; + if (options.skip) { + body.skip = options.skip as number; } - if (additionalFields.sort) { - body.sort = JSON.parse(additionalFields.sort.toString()); + if (options.sort) { + body.sort = JSON.parse(options.sort.toString()); } - if (additionalFields.populate) { - body.populate = additionalFields.populate as boolean; + if (options.populate) { + body.populate = options.populate as boolean; } - if (additionalFields.simple) { - body.simple = additionalFields.simple as boolean; + if (options.simple) { + body.simple = options.simple as boolean; } - if (additionalFields.language) { - body.lang = additionalFields.language as string; + if (options.language) { + body.lang = options.language as string; } - return cockpitApiRequest.call(this, 'post', `/collections/get/${resourceName}`, body); + const resultData = await cockpitApiRequest.call(this, 'post', `/collections/get/${resourceName}`, body); + + if (options.rawData === true) { + return resultData; + } + + return (resultData as unknown as IDataObject).entries; } + + +export async function getAllCollectionNames(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions): Promise { + return cockpitApiRequest.call(this, 'GET', `/collections/listCollections`, {}); +} \ No newline at end of file diff --git a/packages/nodes-base/nodes/Cockpit/FormDescription.ts b/packages/nodes-base/nodes/Cockpit/FormDescription.ts index cdb1266e38..8a1e6284c7 100644 --- a/packages/nodes-base/nodes/Cockpit/FormDescription.ts +++ b/packages/nodes-base/nodes/Cockpit/FormDescription.ts @@ -26,6 +26,22 @@ export const formOperations = [ ] as INodeProperties[]; export const formFields = [ + { + displayName: 'Form', + name: 'form', + type: 'string', + displayOptions: { + show: { + resource: [ + 'forms', + ], + }, + }, + default: '', + required: true, + description: 'Name of the form to operate on.' + }, + // Forms:submit { displayName: 'Form data', diff --git a/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts b/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts index db37b57ab9..ed923d3bda 100644 --- a/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts @@ -36,8 +36,11 @@ export async function cockpitApiRequest(this: IExecuteFunctions | IExecuteSingle try { return await this.helpers.request!(options); } catch (error) { - const errorMessage = error.error.message || error.error.error; + let errorMessage = error.message; + if (error.error) { + errorMessage = error.error.message || error.error.error; + } - throw new Error('Cockpit error: ' + errorMessage); + throw new Error(`Cockpit error [${error.statusCode}]: ` + errorMessage); } } diff --git a/packages/nodes-base/nodes/Cockpit/SingletonDescription.ts b/packages/nodes-base/nodes/Cockpit/SingletonDescription.ts index d10edafc30..402e23747a 100644 --- a/packages/nodes-base/nodes/Cockpit/SingletonDescription.ts +++ b/packages/nodes-base/nodes/Cockpit/SingletonDescription.ts @@ -14,12 +14,33 @@ export const singletonOperations = [ }, options: [ { - name: 'Get data', - value: 'get', - description: 'Get singleton data', + name: 'Get All', + value: 'getAll', + description: 'Get all singletons', }, ], - default: 'get', + default: 'getAll', description: 'The operation to perform.', } ] as INodeProperties[]; + +export const singletonFields = [ + { + displayName: 'Singleton', + name: 'singleton', + type: 'options', + default: '', + typeOptions: { + loadOptionsMethod: 'getSingletons', + }, + displayOptions: { + show: { + resource: [ + 'singletons', + ], + }, + }, + required: true, + description: 'Name of the singleton to operate on.' + }, +] as INodeProperties[]; \ No newline at end of file diff --git a/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts b/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts index 5d07bb4290..5a5cf2da0e 100644 --- a/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts +++ b/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts @@ -5,6 +5,10 @@ import { } from 'n8n-core'; import { cockpitApiRequest } from './GenericFunctions'; -export async function getSingleton(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resourceName: string): Promise { // tslint:disable-line:no-any +export async function getAllSingleton(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resourceName: string): Promise { // tslint:disable-line:no-any return cockpitApiRequest.call(this, 'get', `/singletons/get/${resourceName}`); } + +export async function getAllSingletonNames(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions): Promise { + return cockpitApiRequest.call(this, 'GET', `/singletons/listSingletons`, {}); +} \ No newline at end of file diff --git a/packages/nodes-base/nodes/Cockpit/cockpit.png b/packages/nodes-base/nodes/Cockpit/cockpit.png index b99e95037c62c3a222fbb5a77e668e96ded6b9af..ddbe6ead67664a8d4cf86d0fc108d589541a93a3 100644 GIT binary patch literal 834 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2Ik0(?STfi&6R|NsAg|NdoTW4m(Y z%E5yNRa8_SJ$fW2CMF~##L3C|>C>kZCr&(g@W8~xL{n3c@{CFJ8R3 zef##tjT@Vqn!LQcbaZt1`1o$zxUqZp?k!ukELpOoy}dm%GczJ0!otErQBm>y`Sa(_ zom;zhZC_tsVPT=4pPzw&!P~cQw{PFRa^=d|vuDT0$D5j(a+m&&0(wTcB*-tAfqn11 z?_WN=y}NE=w7;8$k@Uq2FAqz|S%+Qq9+fkj-(UG^>8jVKXZrgd-&V4iD}?v%ChPZxTgzDg z2V`b1U*)^G?f;6IzHe2w7cnx5PZfSMjgPbH^^DsqoW1rI9@*Q{W+cExHx^5ZO= zH&L)gdCE!IqO9D7F>5MBCd)p)!a8Y&flOX_OKHfFx58fLE=8_fRW%P6No}4eyvb#~ zGmmZ20q&xl#+=4aX36XJcfY>SB0sO{X1OiXQK1u7Mq$@o&qdFxYw-Q{m2JTbrkf7O z^4q!M8d`St+c|9RtPS94j9J>a+vdiEw^wzT@?OMCH3o^EIPt@HY2kE_JvDph^}lG< zt330*Q+u+w$-QH#g>%oI+benbzU2-b@89#>-YtK#!$y9`+raP7xq4RR9KK^}negoM zSF4rWeTufLtkd7yxpm}yzArDxRq|3ba_!EyC7@+$Du)ZU=w j{DtdIx9u=aw`eBblD&-ZTMd&mtA3NSHT zYzP2=31bzFg?y8>&#W29C-ml_6ae(=g`9ACICBle7boF)G2&<#pPD2=aD?ZbD&g@H zU^ylljunb1*q7>NEJheZ!G?J-2~3F}94B0*kiu+55Qncw;Cseky}b;{sSqNN1j~7t z)TBg_3`(V7b-EDpTe}TnF*=Gofr1TZ24nohQW)cb_rMddUWOR5G)4fiX#Vevkrf3S zCzneg5KKu)!Kb+4#nM>N)zi}xBoIL&5r;5vvNVyLmx>d~?6eZo8Z=nOmkK3vp;&~` zYVxAR$#M!7i{O}d;&7_)gStreE_@^rpcVmL@dWUXbh%LQH*_uX30)E=mWyR^V#)ge zK04w({D~owzP~m}X)0~Ob#f7Z3B9iar1Op` zjzOGr(^-9h7=C`iQn5goh+r~SfG>tY_anJ_l1MlremW4OXb@s66r|C3a)d=B5D7Sf z8;bYUTK)2K`=kf-pZFf|q_Fa=8$2!u#F(=f=@ z5ypyODaL^fOE7K(43XgK>G-j2N*s*n{(Y3-J0C&q-TU21GWbatWF6rTmld&_-b01} zGEKnu;{;i}A4sqWnL<)z++3S`mI?r<3?WjWPYMG74ttaLYMf~y=I!2soJC5ki%aC< zb$(ynzvOQ@+n&iUXnl5bRN#MePiOkteHB%q1zi)hy&$*zOd*z4Ri#=+8t~Md+Iyp} zN9H}AuJP|poP#jvqIwy&V(d#l{}>KBrP z!rhad@z0V2v#OjPqizzC9)~<0vG10ou`f^q%SSJ^U!=0}4x5Kwn51fqJoHIu; zP+(*KO5HTN{D;8GWj}H)^9@+X&&}Rd@6eGPk$?0jBGE?Jeo$H*aizY24Kmc^|FrjoO|+dbFr(+dYrCCN~cIZBYY} za&;;BGis9c3-E~I{O#&8*&Okb^Vq0lRQJl;_>tf0zMg zYabL)d}tB!n(1ZGsGPXW{u1UYJBE4tluf>a)9l4I;ovnV+iagc#Ur{wNW_@RZdasq z9*skLk~*)!A!lb#mK`cY|I4n8V#UJh@>y5fDi>1yZ|Bw9F3sC@>1BiGaXZRa^03ep z$ntI5F|hC-<90ovXCoW;eY4^r>W%(kYadj`%YxWPK(BY9jk9Jea7OP!;9>DeN28W> zpjmI09)@b9cOdLP*QyFKLKXl;zy@FISN3Xs)vVr`*W=td#=UC~%>8vA&Vj$aax?H5 z>XBqcLQ#(c|M~JmtUDSYX#T8A@!#zNXXop4O?})AxP2A|!_<+R92i3yU1l|GUlO+2 zo&FNYW&x`;B=aZ}$3CmfP(P!oduFUjj|Ymz?M;KN=90+!Uc(njm|6ipGH)CI>a~FQ zGu8e>=3lp74Y)(Ko-?zfqr={clQdtUx@>B^@ZR0KaXemTP7btk<#y|u?jg?l^{dCm z#_sp@Ftf6ov8tbj{$z#DSA1PyWz1 zlH&t5=zp;U?aVM^1_gQjwPTn6F?Rx?#i(Io5y`4$0XRH7OvumIP}M!6e?EHjbX%xj*t&JECKR~= z2lK}pR9`ihJ#6jo??1cD)=st2#@aeqB*O2}G@r_Rp2k(z*B`L?A|m3*jT>v&ygoyH zG`U5#F??mlRK(VS(O+5UqMU)@;cu2Qq7CLapE)Jio+K0=nVd`y8|zv=`t6EggiuZ){|Gcz6_Vi7=+jEWE%X)9G|~ zS68Z9tzK(wxU=nkcX!|>b?Ir)=t%SGkr=89{oV27*^3u1UR4BNyMDdzs?VYwMexDo zua`5(J{V@g$q`==hueE^FlK0qQEXwl=F)P#;`v2~L;7I&W}#^f@N-@OE+eze-TANq zH+k+oK(GBoZni~+J;8cE+EI?N-D!~xdzsFR?ngU@yy}(K(YST1ZJ#LEbX<#-%NOWq zdZd*j&>FwP@ZJ-}xYDPDrC1a;W5pg0j>OIj?AvErV$6zuz3yk*b6+b5``h9h5282L zzM7X8y)W5-i?)bF1(z>6*Zat+Yb`fNSrKdyoqj89tMd7|D$~FdH`*rX6`m(RHW+U& n-BhhNG4D;-$ZvG?*(Y02Q03B~qgCv3?H^|bJ&0E6%iZ>0GZTqi