Skip to content

Commit

Permalink
fix(NextCloud Node): Fix folder list with Nextcloud v24 (#3386)
Browse files Browse the repository at this point in the history
* initial fix for v24 folder listing

* implemented new credential methods

* Nodelinter fixes
  • Loading branch information
Joffcom authored May 27, 2022
1 parent ed69c3c commit 5f3bed3
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 42 deletions.
18 changes: 17 additions & 1 deletion packages/nodes-base/credentials/NextCloudApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';


export class NextCloudApi implements ICredentialType {
name = 'nextCloudApi';
displayName = 'NextCloud API';
Expand All @@ -29,4 +31,18 @@ export class NextCloudApi implements ICredentialType {
default: '',
},
];
async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
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},
},
};
}
54 changes: 24 additions & 30 deletions packages/nodes-base/nodes/NextCloud/GenericFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import {
IExecuteFunctions,
IHookFunctions,
} from 'n8n-core';
import { NodeApiError, NodeOperationError, } from 'n8n-workflow';

import {
JsonObject,
NodeApiError,
} from 'n8n-workflow';

import {
OptionsWithUri,
Expand All @@ -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<any> { // tslint:disable-line:no-any
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
const authenticationMethod = this.getNodeParameter('authentication', 0);

let credentials;

const options: OptionsWithUri = {
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,
Expand All @@ -34,35 +47,16 @@ export async function nextCloudApiRequest(this: IHookFunctions | IExecuteFunctio
options.encoding = null;
}

const authenticationMethod = this.getNodeParameter('authentication', 0);

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)}`;
options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`;
if (resource === 'user' && operation === 'create') {
options.uri = options.uri.replace('/remote.php/webdav', '');
}

if (resource === 'user' && operation === 'create') {
options.uri = options.uri.replace('/remote.php/webdav', '');
}
const credentialType = authenticationMethod === 'accessToken' ? 'nextCloudApi' : 'nextCloudOAuth2Api';

return await this.helpers.requestOAuth2!.call(this, 'nextCloudOAuth2Api', options);
}
} catch (error) {
throw new NodeApiError(this.getNode(), error);
try {
return await this.helpers.requestWithAuthentication.call(this, credentialType, options);
} catch(error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
28 changes: 17 additions & 11 deletions packages/nodes-base/nodes/NextCloud/NextCloud.node.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IExecuteFunctions } from 'n8n-core';
import { NodeApiError } from 'n8n-workflow';

import {
IDataObject,
Expand All @@ -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"]}}',
Expand Down Expand Up @@ -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 "/".',
},

// ----------------------------------
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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 "/".',
},

// ----------------------------------
Expand Down Expand Up @@ -808,7 +809,7 @@ export class NextCloud implements INodeType {
},
options: [
{
displayName: 'Display name',
displayName: 'Display Name',
name: 'displayName',
type: 'string',
default: '',
Expand Down Expand Up @@ -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);
Expand All @@ -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') {
Expand All @@ -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') {
Expand Down Expand Up @@ -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
Expand All @@ -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)) {
Expand All @@ -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);
Expand Down
Binary file removed packages/nodes-base/nodes/NextCloud/nextcloud.png
Binary file not shown.
1 change: 1 addition & 0 deletions packages/nodes-base/nodes/NextCloud/nextcloud.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5f3bed3

Please sign in to comment.