Skip to content

Commit b9e150a

Browse files
authored
Attachment fixes (#63)
1 parent 4cdaf10 commit b9e150a

18 files changed

+169
-300
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 2.7.0 (June 29, 2023)
2+
Added support for files attachment by providing a URL in the body for all actions where it is used
3+
14
## 2.6.0 (June 09, 2023)
25
Added `Don't emit on empty results` checkbox in `Query` trigger
36

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ Action creates a single object.
242242
* **Include referenced objects** - Multiselect dropdown list with all the related child and parent objects of the selected object type. List entries are given as `Object Name/Reference To (Relationship Name)`. Select one or more related objects, which will be join queried and included in the response from your Salesforce Organization. Please see the **Limitations** section below for use case advisories.
243243
* **Allow criteria to be omitted** - Checkbox. If checked and nothing is specified in criteria, an empty object will be returned. If not checked and nothing is found, the action will throw an error.
244244
* **Allow zero results** - Checkbox. If checked and nothing is found in your Salesforce Organization, an empty object will be returned. If not checked and nothing is found, the action will throw an error.
245-
* **Pass binary data to the next component (if found object has it)** - Checkbox. If it is checked and the found object record has a binary field (primitive type `base64`), then its data will be passed to the next component as a binary attachment.
245+
* **Pass binary data to the next component (if found object has it)** - Checkbox. If it is checked and the found object record has a binary field (primitive type `base64`), then its data will be passed to the next component as a binary attachment and link to it will be replaced to link on the platform
246246
* **Enable Cache Usage** - Flag to enable cache usage.
247247

248248
#### Expected input metadata
@@ -321,7 +321,7 @@ Empty object will be returned, if query doesn't find any data.
321321

322322
#### List of Expected Config fields
323323
* **Optional batch size** - A positive integer specifying batch size. If no batch size is specified then results of the query will be emitted one-by-one, otherwise, query results will be emitted in an array of maximum batch size.
324-
* **Allow all results to be returned in a set (overwrites 'Optional batch size feature')** - checkbox which allows emitting query results in a single array. `Optional batch size` option is ignored in this case.
324+
* **Allow all results to be returned in a set** - checkbox which allows emitting query results in a single array. `Optional batch size` and `Max Fetch Count` options are ignored in this case.
325325
* **Include deleted** - checkbox, if checked - deleted records will be included into the result list.
326326
* **Max Fetch Count** - limit for a number of messages that can be fetched. 1,000 is the default value when the variable is not set.
327327

component.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "Customer relationship management (CRM) software & cloud computing from the leader in CRM solutions for businesses large & small.",
44
"docsUrl": "https://github.com/elasticio/salesforce-component-v2",
55
"url": "http://www.salesforce.com/",
6-
"version": "2.6.0",
6+
"version": "2.7.0",
77
"authClientTypes": [
88
"oauth2"
99
],
@@ -329,7 +329,7 @@
329329
},
330330
"utilizeAttachment": {
331331
"viewClass": "CheckBoxView",
332-
"label": "Utilize data attachment from previous step (for objects with a binary field)"
332+
"label": "Utilize data attachment from previous step (for objects with a binary field) or by URL inside this field (instead of data)"
333333
}
334334
}
335335
},
@@ -514,8 +514,11 @@
514514
"placeholder": "0"
515515
},
516516
"allowResultAsSet": {
517-
"label": "Allow all results to be returned in a set (overwrites 'Optional batch size feature')",
518-
"viewClass": "CheckBoxView"
517+
"label": "Allow all results to be returned in a set",
518+
"viewClass": "CheckBoxView",
519+
"help": {
520+
"description": "`Optional batch size` and `Max Fetch Count` options are ignored in this case"
521+
}
519522
},
520523
"includeDeleted": {
521524
"viewClass": "CheckBoxView",

lib/actions/bulk_q.js

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
const { messages } = require('elasticio-node');
2-
const client = require('elasticio-rest-node')();
3-
const request = require('request');
2+
const { AttachmentProcessor } = require('@elastic.io/component-commons-library');
43
const { callJSForceMethod } = require('../helpers/wrapper');
4+
const { getUserAgent } = require('../util');
55

66
exports.process = async function bulkQuery(message, configuration) {
77
this.logger.info('Starting Bulk Query action');
8-
const signedUrl = await client.resources.storage.createSignedUrl();
9-
const out = messages.newEmptyMessage();
8+
9+
const getSfAttachment = async () => callJSForceMethod.call(this, configuration, 'bulkQuery', message.body.query);
10+
11+
const attachmentProcessor = new AttachmentProcessor(getUserAgent(), message.id);
12+
const createdAttachmentId = await attachmentProcessor.uploadAttachment(getSfAttachment);
13+
const attachmentUrl = attachmentProcessor.getMaesterAttachmentUrlById(createdAttachmentId);
14+
15+
const out = messages.newMessageWithBody({ url: attachmentUrl });
1016
out.attachments = {
1117
'bulk_query.csv': {
1218
'content-type': 'text/csv',
13-
url: signedUrl.get_url,
19+
url: attachmentUrl,
1420
},
1521
};
16-
out.body = {};
17-
const stream = await callJSForceMethod.call(this, configuration, 'bulkQuery', message.body.query);
18-
return new Promise((resolve, reject) => {
19-
stream.pipe(request.put(signedUrl.put_url, (err, resp, body) => {
20-
if (err) {
21-
this.logger.error('Error upload query results');
22-
reject(err);
23-
} else {
24-
this.logger.info('Action successfully processed');
25-
out.body = { result: body };
26-
resolve(out);
27-
}
28-
}));
29-
});
22+
return out;
3023
};

lib/actions/deleteObject.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
const { messages } = require('elasticio-node');
44
const { callJSForceMethod } = require('../helpers/wrapper');
5-
const { processMeta, getLookupFieldsModelWithTypeOfSearch, TYPES_MAP } = require('../helpers/utils');
5+
const {
6+
processMeta,
7+
getLookupFieldsModelWithTypeOfSearch,
8+
formatWhereCondition,
9+
logAndThrowError,
10+
} = require('../helpers/utils');
611

712
module.exports.objectTypes = async function objectTypes(configuration) {
813
return callJSForceMethod.call(this, configuration, 'getObjectTypes');
@@ -77,11 +82,7 @@ module.exports.process = async function process(message, configuration) {
7782

7883
const meta = await callJSForceMethod.call(this, configuration, 'describe');
7984
const field = meta.fields.find((fld) => fld.name === lookupField);
80-
const condition = (['date', 'datetime'].includes(field.type)
81-
|| TYPES_MAP[field.type] === 'number'
82-
|| TYPES_MAP[field.type] === 'boolean')
83-
? `${lookupField} = ${lookupValue}`
84-
: `${lookupField} = '${lookupValue}'`;
85+
const condition = formatWhereCondition(field.type, lookupField, lookupValue);
8586

8687
const results = await callJSForceMethod.call(this, configuration, 'selectQuery', { condition });
8788
if (results.length === 1) {
@@ -92,9 +93,7 @@ module.exports.process = async function process(message, configuration) {
9293
this.logger.info('No objects are found');
9394
return messages.newEmptyMessage();
9495
}
95-
const err = new Error('More than one object found, can only delete 1');
96-
this.logger.error(err);
97-
throw err;
96+
logAndThrowError('More than one object found, can only delete 1', this.logger);
9897
}
9998
}
10099
this.logger.debug(`Preparing to delete a ${configuration.sobject} object...`);

lib/actions/lookupObject.js

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ const { messages } = require('elasticio-node');
22
const attachmentTools = require('../helpers/attachment.js');
33
const { lookupCache } = require('../helpers/lookupCache.js');
44
const {
5-
processMeta, getLookupFieldsModelWithTypeOfSearch, getLinkedObjectTypes, TYPES_MAP,
5+
processMeta,
6+
getLookupFieldsModelWithTypeOfSearch,
7+
getLinkedObjectTypes,
8+
formatWhereCondition,
9+
logAndThrowError,
610
} = require('../helpers/utils');
711
const { callJSForceMethod } = require('../helpers/wrapper');
812

@@ -77,9 +81,7 @@ module.exports.process = async function processAction(message, configuration) {
7781

7882
const meta = await callJSForceMethod.call(this, configuration, 'describe');
7983
const field = meta.fields.find((fld) => fld.name === lookupField);
80-
const whereCondition = (['date', 'datetime'].includes(field.type) || TYPES_MAP[field.type] === 'number')
81-
? `${lookupField} = ${lookupValue}`
82-
: `${lookupField} = '${lookupValue}'`;
84+
const whereCondition = formatWhereCondition(field.type, lookupField, lookupValue);
8385

8486
lookupCache.useCache(configuration.enableCacheUsage);
8587
const queryKey = lookupCache.generateKeyFromDataArray(configuration.sobject, whereCondition);
@@ -103,35 +105,29 @@ module.exports.process = async function processAction(message, configuration) {
103105
const records = await callJSForceMethod.call(this, configuration, 'pollingSelectQuery', queryOptions);
104106

105107
if (records.length === 0) {
106-
if (allowZeroResults) {
107-
lookupCache.addRequestResponsePair(queryKey, {});
108-
await this.emit('data', messages.newMessageWithBody({}));
109-
} else {
110-
const err = new Error('No objects found');
111-
this.logger.error(err);
112-
throw (err);
113-
}
114-
} else if (records.length === 1) {
115-
try {
116-
const outputMessage = messages.newMessageWithBody(records[0]);
108+
if (!allowZeroResults) logAndThrowError('No objects found', this.logger);
109+
lookupCache.addRequestResponsePair(queryKey, {});
110+
await this.emit('data', messages.newMessageWithBody({}));
111+
return;
112+
}
117113

118-
if (configuration.passBinaryData) {
119-
const attachment = await attachmentTools.getAttachment(configuration, records[0], this);
120-
if (attachment) {
121-
outputMessage.attachments = attachment;
122-
}
123-
}
114+
if (records.length > 1) logAndThrowError('More than one object found', this.logger);
124115

125-
lookupCache.addRequestResponsePair(queryKey, records[0]);
126-
this.logger.debug('Emitting record');
127-
await this.emit('data', outputMessage);
128-
} catch (err) {
129-
this.logger.error('Lookup Object error occurred');
130-
throw (err);
116+
try {
117+
const outputMessage = messages.newMessageWithBody(records[0]);
118+
119+
if (configuration.passBinaryData) {
120+
const attachment = await attachmentTools.getAttachment(configuration, records[0], this, message.id);
121+
if (attachment) {
122+
outputMessage.attachments = attachment;
123+
}
131124
}
132-
} else {
133-
const err = new Error('More than one object found');
134-
this.logger.error(err);
125+
126+
lookupCache.addRequestResponsePair(queryKey, records[0]);
127+
this.logger.debug('Emitting record');
128+
await this.emit('data', outputMessage);
129+
} catch (err) {
130+
this.logger.error('Lookup Object error occurred');
135131
throw (err);
136132
}
137133
};

lib/actions/query.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ exports.process = async function processAction(message, configuration) {
2626
if (results.length === 0) {
2727
await this.emit('data', messages.newEmptyMessage());
2828
} else {
29-
// eslint-disable-next-line no-restricted-syntax
3029
for (const result of results) {
3130
await this.emit('data', messages.newMessageWithBody({ result }));
3231
}
@@ -37,7 +36,6 @@ exports.process = async function processAction(message, configuration) {
3736
if (results.length === 0) {
3837
await this.emit('data', messages.newEmptyMessage());
3938
} else {
40-
// eslint-disable-next-line no-restricted-syntax
4139
for (const result of results) {
4240
await this.emit('data', messages.newMessageWithBody(result));
4341
}

lib/actions/upsert_v2.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ module.exports.getLookupFieldsModel = async function getLookupFieldsModel(config
1818

1919
module.exports.getMetaModel = async function getMetaModel(configuration) {
2020
const meta = await callJSForceMethod.call(this, configuration, 'describe');
21-
const fields = meta.fields.filter((field) => !field.deprecatedAndHidden
22-
&& field.updateable
23-
&& field.createable);
21+
const fields = meta.fields.filter((field) => !field.deprecatedAndHidden && (field.updateable || field.createable));
2422

2523
const result = {
2624
in: {
@@ -58,6 +56,7 @@ module.exports.getMetaModel = async function getMetaModel(configuration) {
5856
fields.forEach((field) => {
5957
result.in.properties[field.name] = createProperty(field);
6058
if (field.type === 'base64') result.in.properties[field.name].title += ' - use URL to file';
59+
if (!field.updateable || !field.createable) result.in.properties[field.name].required = false;
6160
});
6261

6362
result.in.properties[configuration.lookupField].title = `lookup by - ${result.in.properties[configuration.lookupField].title}`;

0 commit comments

Comments
 (0)