diff --git a/packages/nodes-base/credentials/DropboxApi.credentials.ts b/packages/nodes-base/credentials/DropboxApi.credentials.ts index ba3b81e02c..a95df920b1 100644 --- a/packages/nodes-base/credentials/DropboxApi.credentials.ts +++ b/packages/nodes-base/credentials/DropboxApi.credentials.ts @@ -3,7 +3,6 @@ import { NodePropertyTypes, } from 'n8n-workflow'; - export class DropboxApi implements ICredentialType { name = 'dropboxApi'; displayName = 'Dropbox API'; diff --git a/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts b/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts new file mode 100644 index 0000000000..2da211d35e --- /dev/null +++ b/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts @@ -0,0 +1,47 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + + +export class DropboxOAuth2Api implements ICredentialType { + name = 'dropboxOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'Dropbox OAuth2 API'; + properties = [ + { + 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 20c8248c56..6e05fa5c58 100644 --- a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts +++ b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts @@ -11,8 +11,8 @@ import { } from 'n8n-workflow'; import { - OptionsWithUri -} from 'request'; + dropboxApiRequest +} from './GenericFunctions'; export class Dropbox implements INodeType { description: INodeTypeDescription = { @@ -25,7 +25,7 @@ export class Dropbox implements INodeType { description: 'Access data on Dropbox', defaults: { name: 'Dropbox', - color: '#0061FF', + color: '#0062ff', }, inputs: ['main'], outputs: ['main'], @@ -33,9 +33,44 @@ 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 service.', + }, { displayName: 'Resource', name: 'resource', @@ -443,11 +478,6 @@ export class Dropbox implements INodeType { 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; @@ -455,16 +485,13 @@ export class Dropbox implements INodeType { let endpoint = ''; let requestMethod = ''; let body: IDataObject | Buffer; - let isJson = false; - let query: IDataObject = {}; + let options; + const query: IDataObject = {}; - 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') { @@ -496,6 +523,9 @@ export class Dropbox implements INodeType { endpoint = 'https://content.dropboxapi.com/2/files/upload'; if (this.getNodeParameter('binaryData', i) === true) { + + options = { json: false }; + // Is binary file to upload const item = items[i]; @@ -523,7 +553,6 @@ export class Dropbox implements INodeType { // ---------------------------------- requestMethod = 'POST'; - isJson = true; body = { path: this.getNodeParameter('path', i) as string, }; @@ -536,7 +565,6 @@ export class Dropbox implements INodeType { // ---------------------------------- requestMethod = 'POST'; - isJson = true; body = { path: this.getNodeParameter('path', i) as string, limit: 2000, @@ -556,7 +584,6 @@ 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, @@ -570,7 +597,6 @@ export class Dropbox implements INodeType { // ---------------------------------- requestMethod = 'POST'; - isJson = true; body = { path: this.getNodeParameter('path', i) as string, }; @@ -583,7 +609,6 @@ 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, @@ -595,40 +620,15 @@ export class Dropbox implements INodeType { throw new Error(`The resource "${resource}" is not known!`); } - - const options: OptionsWithUri = { - headers, - method: requestMethod, - uri: endpoint, - qs: query, - json: isJson, - }; - - if (Object.keys(body).length) { - options.body = body; - } - if (resource === 'file' && operation === 'download') { // Return the data as a buffer - options.encoding = null; + options = { 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!'); - } + let responseData = await dropboxApiRequest.call(this, requestMethod, endpoint, body, query, headers, options); - 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; + if (resource === 'file' && operation === 'upload') { + responseData = JSON.parse(responseData); } if (resource === 'file' && operation === 'download') { @@ -650,7 +650,7 @@ export class Dropbox implements INodeType { 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), filePathDownload); } else if (resource === 'folder' && operation === 'list') { @@ -677,8 +677,6 @@ export class Dropbox implements INodeType { returnData.push(newItem as IDataObject); } - } else if (resource === 'file' && operation === 'upload') { - returnData.push(JSON.parse(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..bf24762df3 --- /dev/null +++ b/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts @@ -0,0 +1,69 @@ +import { + IExecuteFunctions, + IHookFunctions, +} from 'n8n-core'; + +import { + OptionsWithUri, +} from 'request'; + +import { + IDataObject, +} from 'n8n-workflow'; + +/** + * 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, query: IDataObject = {}, headers?: object, option: IDataObject = {}): Promise {// tslint:disable-line:no-any + + const options: OptionsWithUri = { + headers, + method, + qs: query, + body, + uri: endpoint, + json: true, + }; + + if (!Object.keys(body).length) { + delete options.body; + } + + Object.assign(options, option); + + const authenticationMethod = this.getNodeParameter('authentication', 0) as string; + + try { + if (authenticationMethod === 'accessToken') { + + const credentials = this.getCredentials('dropboxApi') as IDataObject; + + options.headers!['Authorization'] = `Bearer ${credentials.accessToken}`; + + return await this.helpers.request(options); + } else { + return await this.helpers.requestOAuth2.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 ca2d3d4cca..6b262c7605 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -52,6 +52,7 @@ "dist/credentials/DriftApi.credentials.js", "dist/credentials/DriftOAuth2Api.credentials.js", "dist/credentials/DropboxApi.credentials.js", + "dist/credentials/DropboxOAuth2Api.credentials.js", "dist/credentials/EventbriteApi.credentials.js", "dist/credentials/EventbriteOAuth2Api.credentials.js", "dist/credentials/FacebookGraphApi.credentials.js",