mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
⚡ Add document:upload operation to Salesforce Node (#2030)
This commit is contained in:
parent
1faaef1171
commit
9a7c25aacd
|
@ -1,9 +1,9 @@
|
|||
export interface IAttachment {
|
||||
ParentId?: string;
|
||||
Name?: string;
|
||||
Body?: string;
|
||||
OwnerId?: string;
|
||||
IsPrivate?: boolean;
|
||||
ContentType?: string;
|
||||
Description?: string;
|
||||
Body?: string;
|
||||
}
|
||||
|
|
107
packages/nodes-base/nodes/Salesforce/DocumentDescription.ts
Normal file
107
packages/nodes-base/nodes/Salesforce/DocumentDescription.ts
Normal file
|
@ -0,0 +1,107 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const documentOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'document',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Upload',
|
||||
value: 'upload',
|
||||
description: 'Upload a document',
|
||||
},
|
||||
],
|
||||
default: 'upload',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const documentFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* document:upload */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'document',
|
||||
],
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the file',
|
||||
},
|
||||
{
|
||||
displayName: 'Binary Property',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'document',
|
||||
],
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'document',
|
||||
],
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Link To Object ID',
|
||||
name: 'linkToObjectId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'ID of the object you want to link this document to',
|
||||
},
|
||||
{
|
||||
displayName: 'Owner ID',
|
||||
name: 'ownerId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the owner of this document',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
|
@ -24,7 +24,6 @@ import {
|
|||
|
||||
export async function salesforceApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2') as string;
|
||||
|
||||
try {
|
||||
if (authenticationMethod === 'jwt') {
|
||||
// https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=5
|
||||
|
@ -35,6 +34,7 @@ export async function salesforceApiRequest(this: IExecuteFunctions | IExecuteSin
|
|||
const options = getOptions.call(this, method, (uri || endpoint), body, qs, instance_url as string);
|
||||
Logger.debug(`Authentication for "Salesforce" node is using "jwt". Invoking URI ${options.uri}`);
|
||||
options.headers!.Authorization = `Bearer ${access_token}`;
|
||||
Object.assign(options, option);
|
||||
//@ts-ignore
|
||||
return await this.helpers.request(options);
|
||||
} else {
|
||||
|
@ -43,6 +43,7 @@ export async function salesforceApiRequest(this: IExecuteFunctions | IExecuteSin
|
|||
const credentials = this.getCredentials(credentialsType) as { oauthTokenData: { instance_url: string } };
|
||||
const options = getOptions.call(this, method, (uri || endpoint), body, qs, credentials.oauthTokenData.instance_url);
|
||||
Logger.debug(`Authentication for "Salesforce" node is using "OAuth2". Invoking URI ${options.uri}`);
|
||||
Object.assign(options, option);
|
||||
//@ts-ignore
|
||||
return await this.helpers.requestOAuth2.call(this, credentialsType, options);
|
||||
}
|
||||
|
@ -90,12 +91,16 @@ function getOptions(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOpt
|
|||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
body: method === 'GET' ? undefined : body,
|
||||
body,
|
||||
qs,
|
||||
uri: `${instanceUrl}/services/data/v39.0${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(options.body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
return options;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
BINARY_ENCODING,
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
|
@ -112,6 +113,11 @@ import {
|
|||
userOperations,
|
||||
} from './UserDescription';
|
||||
|
||||
import {
|
||||
documentFields,
|
||||
documentOperations,
|
||||
} from './DocumentDescription';
|
||||
|
||||
import {
|
||||
LoggerProxy as Logger,
|
||||
} from 'n8n-workflow';
|
||||
|
@ -203,6 +209,11 @@ export class Salesforce implements INodeType {
|
|||
value: 'customObject',
|
||||
description: 'Represents a custom object.',
|
||||
},
|
||||
{
|
||||
name: 'Document',
|
||||
value: 'document',
|
||||
description: 'Represents a document.',
|
||||
},
|
||||
{
|
||||
name: 'Flow',
|
||||
value: 'flow',
|
||||
|
@ -243,6 +254,8 @@ export class Salesforce implements INodeType {
|
|||
...contactFields,
|
||||
...customObjectOperations,
|
||||
...customObjectFields,
|
||||
...documentOperations,
|
||||
...documentFields,
|
||||
...opportunityOperations,
|
||||
...opportunityFields,
|
||||
...accountOperations,
|
||||
|
@ -936,6 +949,27 @@ export class Salesforce implements INodeType {
|
|||
sortOptions(returnData);
|
||||
return returnData;
|
||||
},
|
||||
// // Get all folders to display them to user so that he can
|
||||
// // select them easily
|
||||
// async getFolders(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
// const returnData: INodePropertyOptions[] = [];
|
||||
// const fields = await salesforceApiRequestAllItems.call(this, 'records', 'GET', '/sobjects/folder/describe');
|
||||
// console.log(JSON.stringify(fields, undefined, 2))
|
||||
// const qs = {
|
||||
// //ContentFolderItem ContentWorkspace ContentFolder
|
||||
// q: `SELECT Id, Title FROM ContentVersion`,
|
||||
// //q: `SELECT Id FROM Folder where Type = 'Document'`,
|
||||
|
||||
// };
|
||||
// const folders = await salesforceApiRequestAllItems.call(this, 'records', 'GET', '/query', {}, qs);
|
||||
// for (const folder of folders) {
|
||||
// returnData.push({
|
||||
// name: folder.Name,
|
||||
// value: folder.Id,
|
||||
// });
|
||||
// }
|
||||
// return returnData;
|
||||
// },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1588,6 +1622,49 @@ export class Salesforce implements INodeType {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (resource === 'document') {
|
||||
//https://developer.salesforce.com/docs/atlas.en-us.206.0.api_rest.meta/api_rest/dome_sobject_insert_update_blob.htm
|
||||
if (operation === 'upload') {
|
||||
const title = this.getNodeParameter('title', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||
let data;
|
||||
const body: { entity_content: { [key: string]: string } } = {
|
||||
entity_content: {
|
||||
Title: title,
|
||||
ContentLocation: 'S',
|
||||
},
|
||||
};
|
||||
if (additionalFields.ownerId) {
|
||||
body.entity_content['ownerId'] = additionalFields.ownerId as string;
|
||||
}
|
||||
if (additionalFields.linkToObjectId) {
|
||||
body.entity_content['FirstPublishLocationId'] = additionalFields.linkToObjectId as string;
|
||||
}
|
||||
if (items[i].binary && items[i].binary![binaryPropertyName]) {
|
||||
const binaryData = items[i].binary![binaryPropertyName];
|
||||
body.entity_content['PathOnClient'] = `${title}.${binaryData.fileExtension}`;
|
||||
data = {
|
||||
entity_content: {
|
||||
value: JSON.stringify(body.entity_content),
|
||||
options: {
|
||||
contentType: 'application/json',
|
||||
},
|
||||
},
|
||||
VersionData: {
|
||||
value: Buffer.from(binaryData.data, BINARY_ENCODING),
|
||||
options: {
|
||||
filename: binaryData.fileName,
|
||||
contentType: binaryData.mimeType,
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), `The property ${binaryPropertyName} does not exist`);
|
||||
}
|
||||
responseData = await salesforceApiRequest.call(this, 'POST', '/sobjects/ContentVersion', {}, {}, undefined, { formData: data });
|
||||
}
|
||||
}
|
||||
if (resource === 'opportunity') {
|
||||
//https://developer.salesforce.com/docs/api-explorer/sobject/Opportunity/post-opportunity
|
||||
if (operation === 'create' || operation === 'upsert') {
|
||||
|
|
Loading…
Reference in a new issue