Skip to content

Commit 1666804

Browse files
authored
New action: Raw Request (#23)
1 parent 91387b2 commit 1666804

File tree

10 files changed

+4770
-27
lines changed

10 files changed

+4770
-27
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.1.0 (August 10, 2021)
2+
3+
* New `Raw Request` Action
4+
15
## 2.0.4 (February 12, 2021)
26

37
* Update sailor version to 2.6.24

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* [Lookup Object (at most 1)](#lookup-object-at-most-1)
2121
* [Lookup Objects](#lookup-objects)
2222
* [Query Action](#query-action)
23+
* [Raw Request](#raw-request)
2324
* [Upsert Object](#upsert-object)
2425
* [Known Limitations](#known-limitations)
2526

@@ -291,6 +292,25 @@ Empty object will be returned, if query doesn't find any data.
291292
#### Expected input metadata
292293
* **SOQL Query** - Input field where you should type the SOQL query. E.g. `"SELECT ID, Name from Contact where Name like 'John Smi%'"`
293294

295+
### Raw Request
296+
297+
#### Input Metadata
298+
* HTTP Verb - Allowed values GET, POST, PUT, PATCH, DELETE, HEAD, Required. HTTP verb to use in the request.
299+
* Path - String, Required. Path to make request to (without `/services/data/v{SALESFORCE_API_VERSION}`, e.g. to list sobjects - type here not `https://{INSTANCE_NAME}.salesforce.com/services/data/v{SALESFORCE_API_VERSION}/sobjects` but just type `sobjects` instead)
300+
* Request Body - Object, Optional. Body to attach to the HTTP Request
301+
302+
#### Output Metadata
303+
* Response Object (Object, optional): HTTP response body
304+
305+
#### Resources List
306+
* More information about available resources you can find [here](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_list.htm)
307+
308+
#### Request Examples
309+
* Examples of using REST API resources can be found [here](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_user_tasks.htm)
310+
311+
#### Known limitations
312+
For the methods PUT and HEAD you need to specify the whole path (e.g. `services/OpportunityLineItem/00kR0000001WJJAIA4/OpportunityLineItemSchedules`) which have conflicts with `/services/data/v{SALESFORCE_API_VERSION}/{RESOURCE}` path, so Raw Request does not work for these two methods (PUT and HEAD) just for now.
313+
294314
### Upsert Object
295315
Creates or Updates Selected Object.
296316
Action creates a single object.

component.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"title": "Salesforce v2",
3+
"version": "2.1.0",
34
"description": "Customer relationship management (CRM) software & cloud computing from the leader in CRM solutions for businesses large & small.",
45
"docsUrl": "https://github.com/elasticio/salesforce-component-v2",
56
"url": "http://www.salesforce.com/",
@@ -435,6 +436,19 @@
435436
"out": {}
436437
}
437438
},
439+
"rawRequest": {
440+
"title": "Raw Request",
441+
"main": "./lib/actions/rawRequest.js",
442+
"order": 91,
443+
"help": {
444+
"description": "Make Raw Request",
445+
"link": "/components/salesforce/actions#make-raw-request"
446+
},
447+
"metadata": {
448+
"in": "./lib/schemas/rawRequest.in.json",
449+
"out": "./lib/schemas/rawRequest.out.json"
450+
}
451+
},
438452
"upsert": {
439453
"title": "Upsert Object",
440454
"main": "./lib/actions/upsert.js",

lib/actions/rawRequest.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const axios = require('axios');
2+
const fs = require('fs');
3+
const { messages } = require('elasticio-node');
4+
const { Logger } = require('@elastic.io/component-commons-library');
5+
const common = require('../common.js');
6+
const { getSecret, refreshToken } = require('../util');
7+
8+
if (fs.existsSync('.env')) {
9+
// eslint-disable-next-line global-require
10+
require('dotenv').config();
11+
}
12+
13+
const logger = Logger.getLogger();
14+
15+
const SALESFORCE_VERSION = common.globalConsts.SALESFORCE_API_VERSION;
16+
const { REFRESH_TOKEN_RETRIES } = common.globalConsts;
17+
18+
async function processAction(msg, cfg) {
19+
const { method, path, body } = msg.body;
20+
const { secretId } = cfg;
21+
let accessToken;
22+
let instanceUrl;
23+
if (secretId) {
24+
logger.debug('Trying to get access token');
25+
try {
26+
this.logger.debug('Fetching credentials by secretId');
27+
const { credentials } = await getSecret(this, secretId);
28+
accessToken = credentials.access_token;
29+
instanceUrl = credentials.undefined_params.instance_url;
30+
logger.debug('Access token has been received');
31+
} catch (e) {
32+
logger.error('Got error %s while request token', e.name || '');
33+
}
34+
}
35+
let result;
36+
let isSuccess = false;
37+
let iteration = REFRESH_TOKEN_RETRIES;
38+
39+
do {
40+
iteration -= 1;
41+
try {
42+
logger.debug('Iteration: %s', REFRESH_TOKEN_RETRIES - iteration);
43+
logger.info('Trying to call method %s', method);
44+
const basePath = `/services/data/v${SALESFORCE_VERSION}/`;
45+
const resourcePath = (path.trim().charAt(0) === '/') ? path.trim().slice(1) : path.trim();
46+
const rawRequestPath = `${instanceUrl}${basePath}${resourcePath}`;
47+
const lowerCasedMethod = method.toLowerCase();
48+
const rawRequestConfig = {
49+
method: lowerCasedMethod,
50+
url: rawRequestPath,
51+
headers: {
52+
Authorization: `Bearer ${accessToken && accessToken.replace(/"|'/g, '')}`,
53+
'Content-Type': 'application/json',
54+
},
55+
data: body || '',
56+
};
57+
logger.info('Raw request started');
58+
const rawRequest = await axios(rawRequestConfig);
59+
logger.info('Raw request complete');
60+
result = messages.newMessageWithBody(rawRequest.data);
61+
isSuccess = true;
62+
logger.info('Method %s was successfully executed', method);
63+
break;
64+
} catch (e) {
65+
logger.error('Got error %s', e.name || '');
66+
if (e.name === 'INVALID_SESSION_ID') {
67+
try {
68+
logger.debug('Session is expired, trying to refresh token...');
69+
accessToken = await refreshToken(this, secretId);
70+
this.logger.debug('Token is successfully refreshed');
71+
} catch (err) {
72+
logger.error('Failed to refresh token');
73+
}
74+
} else {
75+
throw e;
76+
}
77+
}
78+
} while (iteration > 0);
79+
80+
if (!isSuccess) {
81+
throw new Error('Failed to fetch and/or refresh token, retries exceeded');
82+
}
83+
return result;
84+
}
85+
86+
module.exports.process = processAction;

lib/schemas/rawRequest.in.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"method": {
5+
"type": "string",
6+
"required": true,
7+
"title": "HTTP Verb",
8+
"enum": [
9+
"GET",
10+
"POST",
11+
"PUT",
12+
"PATCH",
13+
"DELETE",
14+
"HEAD"
15+
]
16+
},
17+
"path": {
18+
"type": "string",
19+
"required": true,
20+
"title": "Path"
21+
},
22+
"body": {
23+
"type": "object",
24+
"title": "Body to attach to the HTTP Request",
25+
"properties": {}
26+
}
27+
}
28+
}

lib/schemas/rawRequest.out.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"response": {
5+
"type": "string",
6+
"description": "Response object"
7+
}
8+
}
9+
}

0 commit comments

Comments
 (0)