From 5f3bed3d4e9134b96d6964ca28b5b5dfb1adc1c3 Mon Sep 17 00:00:00 2001 From: Jonathan Bennetts Date: Fri, 27 May 2022 16:15:12 +0100 Subject: [PATCH] fix(NextCloud Node): Fix folder list with Nextcloud v24 (#3386) * initial fix for v24 folder listing * implemented new credential methods * Nodelinter fixes --- .../credentials/NextCloudApi.credentials.ts | 18 +++++- .../nodes/NextCloud/GenericFunctions.ts | 54 ++++++++---------- .../nodes/NextCloud/NextCloud.node.ts | 28 +++++---- .../nodes-base/nodes/NextCloud/nextcloud.png | Bin 828 -> 0 bytes .../nodes-base/nodes/NextCloud/nextcloud.svg | 1 + 5 files changed, 59 insertions(+), 42 deletions(-) delete mode 100644 packages/nodes-base/nodes/NextCloud/nextcloud.png create mode 100644 packages/nodes-base/nodes/NextCloud/nextcloud.svg diff --git a/packages/nodes-base/credentials/NextCloudApi.credentials.ts b/packages/nodes-base/credentials/NextCloudApi.credentials.ts index a6b8946c79..6bf5ddcf66 100644 --- a/packages/nodes-base/credentials/NextCloudApi.credentials.ts +++ b/packages/nodes-base/credentials/NextCloudApi.credentials.ts @@ -1,9 +1,11 @@ import { + ICredentialDataDecryptedObject, + ICredentialTestRequest, ICredentialType, + IHttpRequestOptions, INodeProperties, } from 'n8n-workflow'; - export class NextCloudApi implements ICredentialType { name = 'nextCloudApi'; displayName = 'NextCloud API'; @@ -29,4 +31,18 @@ export class NextCloudApi implements ICredentialType { default: '', }, ]; + async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise { + requestOptions.auth = { + username: credentials.user as string, + password: credentials.password as string, + }; + return requestOptions; + } + test: ICredentialTestRequest = { + request: { + baseURL: '={{$credentials.webDavUrl.replace(\'/remote.php/webdav\', \'\')}}', + url: '/ocs/v1.php/cloud/capabilities', + headers: {'OCS-APIRequest': true}, + }, + }; } diff --git a/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts b/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts index dcf91887a1..ff8226e375 100644 --- a/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts +++ b/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts @@ -2,7 +2,11 @@ import { IExecuteFunctions, IHookFunctions, } from 'n8n-core'; -import { NodeApiError, NodeOperationError, } from 'n8n-workflow'; + +import { + JsonObject, + NodeApiError, + } from 'n8n-workflow'; import { OptionsWithUri, @@ -20,8 +24,17 @@ import { export async function nextCloudApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: object | string | Buffer, headers?: object, encoding?: null | undefined, query?: object): Promise { // tslint:disable-line:no-any const resource = this.getNodeParameter('resource', 0); const operation = this.getNodeParameter('operation', 0); + const authenticationMethod = this.getNodeParameter('authentication', 0); - const options: OptionsWithUri = { + let credentials; + + if (authenticationMethod === 'accessToken') { + credentials = await this.getCredentials('nextCloudApi') as { webDavUrl: string }; + } else { + credentials = await this.getCredentials('nextCloudOAuth2Api') as { webDavUrl: string }; + } + + let options: OptionsWithUri = { headers, method, body, @@ -34,35 +47,16 @@ export async function nextCloudApiRequest(this: IHookFunctions | IExecuteFunctio options.encoding = null; } - const authenticationMethod = this.getNodeParameter('authentication', 0); + options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`; + if (resource === 'user' && operation === 'create') { + options.uri = options.uri.replace('/remote.php/webdav', ''); + } + + const credentialType = authenticationMethod === 'accessToken' ? 'nextCloudApi' : 'nextCloudOAuth2Api'; try { - if (authenticationMethod === 'accessToken') { - const credentials = await this.getCredentials('nextCloudApi'); - - options.auth = { - user: credentials.user as string, - pass: credentials.password as string, - }; - - options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`; - - if (resource === 'user' || operation === 'share') { - options.uri = options.uri.replace('/remote.php/webdav', ''); - } - return await this.helpers.request(options); - } else { - const credentials = await this.getCredentials('nextCloudOAuth2Api'); - - options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`; - - if (resource === 'user' && operation === 'create') { - options.uri = options.uri.replace('/remote.php/webdav', ''); - } - - return await this.helpers.requestOAuth2!.call(this, 'nextCloudOAuth2Api', options); - } - } catch (error) { - throw new NodeApiError(this.getNode(), error); + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); + } catch(error) { + throw new NodeApiError(this.getNode(), error as JsonObject); } } diff --git a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts index f7354e867a..cacee6f41a 100644 --- a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts +++ b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts @@ -1,4 +1,5 @@ import { IExecuteFunctions } from 'n8n-core'; +import { NodeApiError } from 'n8n-workflow'; import { IDataObject, @@ -22,7 +23,7 @@ export class NextCloud implements INodeType { description: INodeTypeDescription = { displayName: 'Nextcloud', name: 'nextCloud', - icon: 'file:nextcloud.png', + icon: 'file:nextcloud.svg', group: ['input'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', @@ -305,7 +306,7 @@ export class NextCloud implements INodeType { }, }, placeholder: '/invoices/2019/invoice_1.pdf', - description: 'The path to delete. Can be a single file or a whole folder. The path should start with "/"', + description: 'The path to delete. Can be a single file or a whole folder. The path should start with "/".', }, // ---------------------------------- @@ -372,7 +373,7 @@ export class NextCloud implements INodeType { }, }, placeholder: '/invoices/2019/invoice_1.pdf', - description: 'The file path of the file to download. Has to contain the full path. The path should start with "/"', + description: 'The file path of the file to download. Has to contain the full path. The path should start with "/".', }, { displayName: 'Binary Property', @@ -499,7 +500,7 @@ export class NextCloud implements INodeType { }, }, placeholder: '/invoices/2019/invoice_1.pdf', - description: 'The file path of the file to share. Has to contain the full path. The path should start with "/"', + description: 'The file path of the file to share. Has to contain the full path. The path should start with "/".', }, { displayName: 'Share Type', @@ -720,7 +721,7 @@ export class NextCloud implements INodeType { }, }, placeholder: '/invoices/2019', - description: 'The folder to create. The parent folder has to exist. The path should start with "/"', + description: 'The folder to create. The parent folder has to exist. The path should start with "/".', }, // ---------------------------------- @@ -808,7 +809,7 @@ export class NextCloud implements INodeType { }, options: [ { - displayName: 'Display name', + displayName: 'Display Name', name: 'displayName', type: 'string', default: '', @@ -1287,7 +1288,7 @@ export class NextCloud implements INodeType { } if (data.ocs.meta.status !== 'ok') { - return reject(new Error(data.ocs.meta.message || data.ocs.meta.status)); + return reject(new NodeApiError(this.getNode(), data.ocs.meta.message || data.ocs.meta.status)); } resolve(data.ocs.data as IDataObject); @@ -1307,7 +1308,7 @@ export class NextCloud implements INodeType { } if (data.ocs.meta.status !== 'ok') { - return reject(new Error(data.ocs.meta.message || data.ocs.meta.status)); + return reject(new NodeApiError(this.getNode(), data.ocs.meta.message || data.ocs.meta.status)); } if (operation === 'delete' || operation === 'update') { @@ -1328,7 +1329,7 @@ export class NextCloud implements INodeType { } if (data.ocs.meta.status !== 'ok') { - return reject(new Error(data.ocs.meta.message)); + return reject(new NodeApiError(this.getNode(), data.ocs.meta.message)); } if (typeof (data.ocs.data.users.element) === 'string') { @@ -1366,7 +1367,6 @@ export class NextCloud implements INodeType { (jsonResponseData['d:multistatus'] as IDataObject)['d:response'] !== undefined && (jsonResponseData['d:multistatus'] as IDataObject)['d:response'] !== null) { let skippedFirst = false; - // @ts-ignore if (Array.isArray(jsonResponseData['d:multistatus']['d:response'])) { // @ts-ignore @@ -1379,7 +1379,12 @@ export class NextCloud implements INodeType { newItem.path = item['d:href'].slice(19); - const props = item['d:propstat'][0]['d:prop']; + let props: IDataObject = {}; + if (Array.isArray(item['d:propstat'])) { + props = item['d:propstat'][0]['d:prop'] as IDataObject; + } else { + props = item['d:propstat']['d:prop'] as IDataObject; + } // Get the props and save them under a proper name for (const propName of Object.keys(propNames)) { @@ -1393,6 +1398,7 @@ export class NextCloud implements INodeType { } else { newItem.type = 'folder'; } + // @ts-ignore newItem.eTag = props['d:getetag'].slice(1, -1); returnData.push(newItem as IDataObject); diff --git a/packages/nodes-base/nodes/NextCloud/nextcloud.png b/packages/nodes-base/nodes/NextCloud/nextcloud.png deleted file mode 100644 index 4921ae98cbb89ac32a168904130371a8c78f8ce0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 828 zcmV-C1H=4@P)s*%+&Y%&glBe z?)S#%_QB=!yx;M--SM&7@SW1@htcSHz~XDT;asxbU9sIYozyFu)G3wH7mm&si_HE0 z|KIfe>h}BJ^7`29`nBWp#NzXk+3%{>?uX3kkIdPO9K0005wNkl_cM#zZc?schc!?u=eGV)y z?`c;;SoA(ri#s@BsUpKQa@_lTfCxGr0C`2Vj|0~Nv4!()4XXAw^JzeYA{r2DqJZXL zqz7US|FwY8%-@<=At5XvgnyV+%U;nk&^~%Xmm-ZbViRH*Y(9BduDQ5?9=|Gyn|T4poRz$)Tbx(P zo;R(;Rl(1&Nu-BiF3}{199<)HC!2&g3%cid7i>)U7<`Uyu)GQ1%E1C{h+(gkflGN2@bbBt3p~TM`zAbb@Y=eWMpJyydAGD$0Ws4c0iH<0000 \ No newline at end of file