diff --git a/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts b/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts
new file mode 100644
index 0000000000..aebc7d1392
--- /dev/null
+++ b/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts
@@ -0,0 +1,54 @@
+import {
+ ICredentialType,
+ NodePropertyTypes,
+} from 'n8n-workflow';
+
+
+export class DropboxOAuth2Api implements ICredentialType {
+ name = 'dropboxOAuth2Api';
+ extends = [
+ 'oAuth2Api',
+ ];
+ displayName = 'Dropbox OAuth2 API';
+ properties = [
+ {
+ displayName: 'Dropbox Server',
+ name: 'server',
+ type: 'string' as NodePropertyTypes,
+ default: 'https://api.dropboxapi.com',
+ description: 'The server to connect to. Does only have to get changed if Dropbox Enterprise gets used.',
+ },
+ {
+ displayName: 'Authorization URL',
+ name: 'authUrl',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'https://www.dropbox.com/oauth2/authorize',
+ required: true,
+ },
+ {
+ displayName: 'Access Token URL',
+ name: 'accessTokenUrl',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'https://api.dropboxapi.com/oauth2/token',
+ required: true,
+ },
+ {
+ displayName: 'Scope',
+ name: 'scope',
+ type: 'hidden' as NodePropertyTypes,
+ default: '',
+ },
+ {
+ displayName: 'Auth URI Query Parameters',
+ name: 'authQueryParameters',
+ type: 'hidden' as NodePropertyTypes,
+ default: '',
+ },
+ {
+ displayName: 'Authentication',
+ name: 'authentication',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'header',
+ },
+ ];
+}
diff --git a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts
index 6140568bc2..6f575bd640 100644
--- a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts
+++ b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts
@@ -1,16 +1,11 @@
-import {
- BINARY_ENCODING,
- IExecuteFunctions,
-} from 'n8n-core';
+import { BINARY_ENCODING, IExecuteFunctions } from 'n8n-core';
import {
IDataObject,
- INodeTypeDescription,
INodeExecutionData,
INodeType,
+ INodeTypeDescription
} from 'n8n-workflow';
-
-import { OptionsWithUri } from 'request';
-
+import { dropboxApiRequest } from './GenericFunctions';
export class Dropbox implements INodeType {
description: INodeTypeDescription = {
@@ -23,7 +18,7 @@ export class Dropbox implements INodeType {
description: 'Access data on Dropbox',
defaults: {
name: 'Dropbox',
- color: '#22BB44',
+ color: '#22BB44'
},
inputs: ['main'],
outputs: ['main'],
@@ -31,9 +26,40 @@ export class Dropbox implements INodeType {
{
name: 'dropboxApi',
required: true,
+ displayOptions: {
+ show: {
+ authentication: ['accessToken']
+ }
+ }
+ },
+ {
+ name: 'dropboxOAuth2Api',
+ required: true,
+ displayOptions: {
+ show: {
+ authentication: ['oAuth2']
+ }
+ }
}
],
properties: [
+ {
+ displayName: 'Authentication',
+ name: 'authentication',
+ type: 'options',
+ options: [
+ {
+ name: 'Access Token',
+ value: 'accessToken'
+ },
+ {
+ name: 'OAuth2',
+ value: 'oAuth2'
+ }
+ ],
+ default: 'accessToken',
+ description: 'Means of authenticating with the serivce.'
+ },
{
displayName: 'Resource',
name: 'resource',
@@ -41,15 +67,15 @@ export class Dropbox implements INodeType {
options: [
{
name: 'File',
- value: 'file',
+ value: 'file'
},
{
name: 'Folder',
- value: 'folder',
- },
+ value: 'folder'
+ }
],
default: 'file',
- description: 'The resource to operate on.',
+ description: 'The resource to operate on.'
},
// ----------------------------------
@@ -61,40 +87,38 @@ export class Dropbox implements INodeType {
type: 'options',
displayOptions: {
show: {
- resource: [
- 'file',
- ],
- },
+ resource: ['file']
+ }
},
options: [
{
name: 'Copy',
value: 'copy',
- description: 'Copy a file',
+ description: 'Copy a file'
},
{
name: 'Delete',
value: 'delete',
- description: 'Delete a file',
+ description: 'Delete a file'
},
{
name: 'Download',
value: 'download',
- description: 'Download a file',
+ description: 'Download a file'
},
{
name: 'Move',
value: 'move',
- description: 'Move a file',
+ description: 'Move a file'
},
{
name: 'Upload',
value: 'upload',
- description: 'Upload a file',
- },
+ description: 'Upload a file'
+ }
],
default: 'upload',
- description: 'The operation to perform.',
+ description: 'The operation to perform.'
},
{
@@ -103,40 +127,38 @@ export class Dropbox implements INodeType {
type: 'options',
displayOptions: {
show: {
- resource: [
- 'folder',
- ],
- },
+ resource: ['folder']
+ }
},
options: [
{
name: 'Copy',
value: 'copy',
- description: 'Copy a folder',
+ description: 'Copy a folder'
},
{
name: 'Create',
value: 'create',
- description: 'Create a folder',
+ description: 'Create a folder'
},
{
name: 'Delete',
value: 'delete',
- description: 'Delete a folder',
+ description: 'Delete a folder'
},
{
name: 'List',
value: 'list',
- description: 'Return the files and folders in a given folder',
+ description: 'Return the files and folders in a given folder'
},
{
name: 'Move',
value: 'move',
- description: 'Move a folder',
- },
+ description: 'Move a folder'
+ }
],
default: 'create',
- description: 'The operation to perform.',
+ description: 'The operation to perform.'
},
// ----------------------------------
@@ -154,17 +176,12 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'copy'
- ],
- resource: [
- 'file',
- 'folder',
- ],
- },
+ operation: ['copy'],
+ resource: ['file', 'folder']
+ }
},
placeholder: '/invoices/original.txt',
- description: 'The path of file or folder to copy.',
+ description: 'The path of file or folder to copy.'
},
{
displayName: 'To Path',
@@ -174,17 +191,12 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'copy'
- ],
- resource: [
- 'file',
- 'folder',
- ],
- },
+ operation: ['copy'],
+ resource: ['file', 'folder']
+ }
},
placeholder: '/invoices/copy.txt',
- description: 'The destination path of file or folder.',
+ description: 'The destination path of file or folder.'
},
// ----------------------------------
@@ -198,20 +210,15 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'delete'
- ],
- resource: [
- 'file',
- 'folder',
- ],
- },
+ operation: ['delete'],
+ resource: ['file', 'folder']
+ }
},
placeholder: '/invoices/2019/invoice_1.pdf',
- description: 'The path to delete. Can be a single file or a whole folder.',
+ description:
+ 'The path to delete. Can be a single file or a whole folder.'
},
-
// ----------------------------------
// file/folder:move
// ----------------------------------
@@ -223,17 +230,12 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'move'
- ],
- resource: [
- 'file',
- 'folder',
- ],
- },
+ operation: ['move'],
+ resource: ['file', 'folder']
+ }
},
placeholder: '/invoices/old_name.txt',
- description: 'The path of file or folder to move.',
+ description: 'The path of file or folder to move.'
},
{
displayName: 'To Path',
@@ -243,17 +245,12 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'move'
- ],
- resource: [
- 'file',
- 'folder',
- ],
- },
+ operation: ['move'],
+ resource: ['file', 'folder']
+ }
},
placeholder: '/invoices/new_name.txt',
- description: 'The new path of file or folder.',
+ description: 'The new path of file or folder.'
},
// ----------------------------------
@@ -267,16 +264,13 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'download'
- ],
- resource: [
- 'file',
- ],
- },
+ operation: ['download'],
+ resource: ['file']
+ }
},
placeholder: '/invoices/2019/invoice_1.pdf',
- description: 'The file path of the file to download. Has to contain the full path.',
+ description:
+ 'The file path of the file to download. Has to contain the full path.'
},
{
displayName: 'Binary Property',
@@ -286,15 +280,12 @@ export class Dropbox implements INodeType {
default: 'data',
displayOptions: {
show: {
- operation: [
- 'download'
- ],
- resource: [
- 'file',
- ],
- },
+ operation: ['download'],
+ resource: ['file']
+ }
},
- description: 'Name of the binary property to which to
write the data of the read file.',
+ description:
+ 'Name of the binary property to which to
write the data of the read file.'
},
// ----------------------------------
@@ -308,16 +299,13 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'upload'
- ],
- resource: [
- 'file',
- ],
- },
+ operation: ['upload'],
+ resource: ['file']
+ }
},
placeholder: '/invoices/2019/invoice_1.pdf',
- description: 'The file path of the file to upload. Has to contain the full path. The parent folder has to exist. Existing files get overwritten.',
+ description:
+ 'The file path of the file to upload. Has to contain the full path. The parent folder has to exist. Existing files get overwritten.'
},
{
displayName: 'Binary Data',
@@ -326,15 +314,11 @@ export class Dropbox implements INodeType {
default: false,
displayOptions: {
show: {
- operation: [
- 'upload'
- ],
- resource: [
- 'file',
- ],
- },
+ operation: ['upload'],
+ resource: ['file']
+ }
},
- description: 'If the data to upload should be taken from binary field.',
+ description: 'If the data to upload should be taken from binary field.'
},
{
displayName: 'File Content',
@@ -343,20 +327,13 @@ export class Dropbox implements INodeType {
default: '',
displayOptions: {
show: {
- operation: [
- 'upload'
- ],
- resource: [
- 'file',
- ],
- binaryData: [
- false
- ],
- },
-
+ operation: ['upload'],
+ resource: ['file'],
+ binaryData: [false]
+ }
},
placeholder: '',
- description: 'The text content of the file to upload.',
+ description: 'The text content of the file to upload.'
},
{
displayName: 'Binary Property',
@@ -366,24 +343,16 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'upload'
- ],
- resource: [
- 'file',
- ],
- binaryData: [
- true
- ],
- },
-
+ operation: ['upload'],
+ resource: ['file'],
+ binaryData: [true]
+ }
},
placeholder: '',
- description: 'Name of the binary property which contains
the data for the file to be uploaded.',
+ description:
+ 'Name of the binary property which contains
the data for the file to be uploaded.'
},
-
-
// ----------------------------------
// folder
// ----------------------------------
@@ -399,16 +368,12 @@ export class Dropbox implements INodeType {
required: true,
displayOptions: {
show: {
- operation: [
- 'create'
- ],
- resource: [
- 'folder',
- ],
- },
+ operation: ['create'],
+ resource: ['folder']
+ }
},
placeholder: '/invoices/2019',
- description: 'The folder to create. The parent folder has to exist.',
+ description: 'The folder to create. The parent folder has to exist.'
},
// ----------------------------------
@@ -421,47 +386,31 @@ export class Dropbox implements INodeType {
default: '',
displayOptions: {
show: {
- operation: [
- 'list'
- ],
- resource: [
- 'folder',
- ],
- },
+ operation: ['list'],
+ resource: ['folder']
+ }
},
placeholder: '/invoices/2019/',
- description: 'The path of which to list the content.',
- },
-
- ],
+ description: 'The path of which to list the content.'
+ }
+ ]
};
-
async execute(this: IExecuteFunctions): Promise {
const items = this.getInputData();
const returnData: IDataObject[] = [];
- const credentials = this.getCredentials('dropboxApi');
-
- if (credentials === undefined) {
- throw new Error('No credentials got returned!');
- }
-
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
let endpoint = '';
let requestMethod = '';
let body: IDataObject | Buffer;
- let isJson = false;
- let headers: IDataObject;
+ const headers: IDataObject = {};
for (let i = 0; i < items.length; i++) {
body = {};
- headers = {
- 'Authorization': `Bearer ${credentials.accessToken}`,
- };
if (resource === 'file') {
if (operation === 'download') {
@@ -471,11 +420,10 @@ export class Dropbox implements INodeType {
requestMethod = 'POST';
headers['Dropbox-API-Arg'] = JSON.stringify({
- path: this.getNodeParameter('path', i) as string,
+ path: this.getNodeParameter('path', i) as string
});
endpoint = 'https://content.dropboxapi.com/2/files/download';
-
} else if (operation === 'upload') {
// ----------------------------------
// upload
@@ -485,7 +433,7 @@ export class Dropbox implements INodeType {
headers['Content-Type'] = 'application/octet-stream';
headers['Dropbox-API-Arg'] = JSON.stringify({
mode: 'overwrite',
- path: this.getNodeParameter('path', i) as string,
+ path: this.getNodeParameter('path', i) as string
});
endpoint = 'https://content.dropboxapi.com/2/files/upload';
@@ -498,19 +446,29 @@ export class Dropbox implements INodeType {
throw new Error('No binary data exists on item!');
}
- const propertyNameUpload = this.getNodeParameter('binaryPropertyName', i) as string;
+ const propertyNameUpload = this.getNodeParameter(
+ 'binaryPropertyName',
+ i
+ ) as string;
if (item.binary[propertyNameUpload] === undefined) {
- throw new Error(`No binary data property "${propertyNameUpload}" does not exists on item!`);
+ throw new Error(
+ `No binary data property "${propertyNameUpload}" does not exists on item!`
+ );
}
- body = Buffer.from(item.binary[propertyNameUpload].data, BINARY_ENCODING);
+ body = Buffer.from(
+ item.binary[propertyNameUpload].data,
+ BINARY_ENCODING
+ );
} else {
// Is text file
- body = Buffer.from(this.getNodeParameter('fileContent', i) as string, 'utf8');
+ body = Buffer.from(
+ this.getNodeParameter('fileContent', i) as string,
+ 'utf8'
+ );
}
}
-
} else if (resource === 'folder') {
if (operation === 'create') {
// ----------------------------------
@@ -518,30 +476,26 @@ export class Dropbox implements INodeType {
// ----------------------------------
requestMethod = 'POST';
- isJson = true;
body = {
- path: this.getNodeParameter('path', i) as string,
+ path: this.getNodeParameter('path', i) as string
};
endpoint = 'https://api.dropboxapi.com/2/files/create_folder_v2';
-
} else if (operation === 'list') {
// ----------------------------------
// list
// ----------------------------------
requestMethod = 'POST';
- isJson = true;
body = {
path: this.getNodeParameter('path', i) as string,
- limit: 2000,
+ limit: 2000
};
// TODO: If more files than the max-amount exist it has to be possible to
// also request them.
endpoint = 'https://api.dropboxapi.com/2/files/list_folder';
-
}
}
if (['file', 'folder'].includes(resource)) {
@@ -551,37 +505,32 @@ export class Dropbox implements INodeType {
// ----------------------------------
requestMethod = 'POST';
- isJson = true;
body = {
from_path: this.getNodeParameter('path', i) as string,
- to_path: this.getNodeParameter('toPath', i) as string,
+ to_path: this.getNodeParameter('toPath', i) as string
};
endpoint = 'https://api.dropboxapi.com/2/files/copy_v2';
-
} else if (operation === 'delete') {
// ----------------------------------
// delete
// ----------------------------------
requestMethod = 'POST';
- isJson = true;
body = {
- path: this.getNodeParameter('path', i) as string,
+ path: this.getNodeParameter('path', i) as string
};
endpoint = 'https://api.dropboxapi.com/2/files/delete_v2';
-
} else if (operation === 'move') {
// ----------------------------------
// move
// ----------------------------------
requestMethod = 'POST';
- isJson = true;
body = {
from_path: this.getNodeParameter('path', i) as string,
- to_path: this.getNodeParameter('toPath', i) as string,
+ to_path: this.getNodeParameter('toPath', i) as string
};
endpoint = 'https://api.dropboxapi.com/2/files/move_v2';
@@ -590,47 +539,25 @@ export class Dropbox implements INodeType {
throw new Error(`The resource "${resource}" is not known!`);
}
-
- const options: OptionsWithUri = {
- headers,
- method: requestMethod,
- qs: {},
- uri: endpoint,
- json: isJson,
- };
-
- if (Object.keys(body).length) {
- options.body = body;
- }
-
+ let encoding: string | null = '';
if (resource === 'file' && operation === 'download') {
// Return the data as a buffer
- options.encoding = null;
+ encoding = null;
}
- let responseData;
- try {
- responseData = await this.helpers.request(options);
- } catch (error) {
- if (error.statusCode === 401) {
- // Return a clear error
- throw new Error('The Dropbox credentials are not valid!');
- }
-
- if (error.error && error.error.error_summary) {
- // Try to return the error prettier
- throw new Error(`Dropbox error response [${error.statusCode}]: ${error.error.error_summary}`);
- }
-
- // If that data does not exist for some reason return the actual error
- throw error;
- }
+ const responseData = await dropboxApiRequest.call(
+ this,
+ requestMethod,
+ endpoint,
+ body,
+ headers,
+ encoding
+ );
if (resource === 'file' && operation === 'download') {
-
const newItem: INodeExecutionData = {
json: items[i].json,
- binary: {},
+ binary: {}
};
if (items[i].binary !== undefined) {
@@ -642,22 +569,28 @@ export class Dropbox implements INodeType {
items[i] = newItem;
- const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i) as string;
+ const dataPropertyNameDownload = this.getNodeParameter(
+ 'binaryPropertyName',
+ i
+ ) as string;
const filePathDownload = this.getNodeParameter('path', i) as string;
- items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(responseData, filePathDownload);
-
+ items[i].binary![
+ dataPropertyNameDownload
+ ] = await this.helpers.prepareBinaryData(
+ Buffer.from(responseData.data),
+ filePathDownload
+ );
} else if (resource === 'folder' && operation === 'list') {
-
const propNames: { [key: string]: string } = {
- 'id': 'id',
- 'name': 'name',
- 'client_modified': 'lastModifiedClient',
- 'server_modified': 'lastModifiedServer',
- 'rev': 'rev',
- 'size': 'contentSize',
+ id: 'id',
+ name: 'name',
+ client_modified: 'lastModifiedClient',
+ server_modified: 'lastModifiedServer',
+ rev: 'rev',
+ size: 'contentSize',
'.tag': 'type',
- 'content_hash': 'contentHash',
+ content_hash: 'contentHash'
};
for (const item of responseData.entries) {
@@ -673,7 +606,7 @@ export class Dropbox implements INodeType {
returnData.push(newItem as IDataObject);
}
} else if (resource === 'file' && operation === 'upload') {
- returnData.push(JSON.parse(responseData) as IDataObject);
+ returnData.push(responseData as IDataObject);
} else {
returnData.push(responseData as IDataObject);
}
diff --git a/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts b/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts
new file mode 100644
index 0000000000..486d9439aa
--- /dev/null
+++ b/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts
@@ -0,0 +1,71 @@
+import { IExecuteFunctions, IHookFunctions } from 'n8n-core';
+import { OptionsWithUri } from 'request';
+
+/**
+ * Make an API request to Dropbox
+ *
+ * @param {IHookFunctions} this
+ * @param {string} method
+ * @param {string} url
+ * @param {object} body
+ * @returns {Promise}
+ */
+export async function dropboxApiRequest(
+ this: IHookFunctions | IExecuteFunctions,
+ method: string,
+ endpoint: string,
+ body: object,
+ headers?: object,
+ encoding?: string | null
+): Promise {
+ // tslint:disable-line:no-any
+
+ const options: OptionsWithUri = {
+ headers,
+ method,
+ body,
+ uri: endpoint,
+ json: true,
+ encoding
+ };
+
+ if (!Object.keys(body).length) {
+ delete options.body;
+ }
+
+ if (encoding !== null) {
+ delete options.encoding;
+ }
+
+ const authenticationMethod = this.getNodeParameter(
+ 'authentication',
+ 0
+ ) as string;
+
+ try {
+ if (authenticationMethod === 'accessToken') {
+ return await this.helpers.request(options);
+ } else {
+ return await this.helpers.requestOAuth.call(
+ this,
+ 'dropboxOAuth2Api',
+ options
+ );
+ }
+ } catch (error) {
+ if (error.statusCode === 401) {
+ // Return a clear error
+ throw new Error('The Dropbox credentials are not valid!');
+ }
+
+ if (error.error && error.error.error_summary) {
+ // Try to return the error prettier
+ throw new Error(
+ `Dropbox error response [${error.statusCode}]: ${error.error.error_summary}`
+ );
+ }
+
+ // If that data does not exist for some reason return the actual error
+ throw error;
+ }
+}
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index 01bcd091cb..e86d2041a9 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -48,6 +48,7 @@
"dist/credentials/DisqusApi.credentials.js",
"dist/credentials/DriftApi.credentials.js",
"dist/credentials/DropboxApi.credentials.js",
+ "dist/credentials/DropboxOAuth2Api.credentials.js",
"dist/credentials/EventbriteApi.credentials.js",
"dist/credentials/FacebookGraphApi.credentials.js",
"dist/credentials/FreshdeskApi.credentials.js",