@pipedream/salesforce_rest_api 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +92 -0
  2. package/actions/add-contact-to-campaign/add-contact-to-campaign.mjs +25 -25
  3. package/actions/add-lead-to-campaign/add-lead-to-campaign.mjs +25 -24
  4. package/actions/common/base-create-update.mjs +108 -0
  5. package/actions/convert-soap-xml-to-json/convert-soap-xml-to-json.mjs +11 -3
  6. package/actions/create-account/create-account.mjs +33 -36
  7. package/actions/create-attachment/create-attachment.mjs +36 -50
  8. package/actions/create-campaign/create-campaign.mjs +41 -37
  9. package/actions/create-case/create-case.mjs +41 -37
  10. package/actions/create-casecomment/create-casecomment.mjs +26 -38
  11. package/actions/create-contact/create-contact.mjs +38 -35
  12. package/actions/create-event/create-event.mjs +57 -66
  13. package/actions/create-lead/create-lead.mjs +34 -42
  14. package/actions/create-note/create-note.mjs +24 -43
  15. package/actions/create-opportunity/create-opportunity.mjs +38 -47
  16. package/actions/create-record/create-record.mjs +49 -15
  17. package/actions/create-task/create-task.mjs +50 -42
  18. package/actions/create-user/create-user.mjs +33 -196
  19. package/actions/delete-opportunity/delete-opportunity.mjs +17 -13
  20. package/actions/delete-record/delete-record.mjs +18 -16
  21. package/actions/find-records/find-records.mjs +41 -26
  22. package/actions/insert-blob-data/insert-blob-data.mjs +3 -7
  23. package/actions/post-feed-to-chatter/post-feed-to-chatter.mjs +45 -20
  24. package/actions/search-string/search-string.mjs +22 -20
  25. package/actions/soql-search/soql-search.mjs +13 -8
  26. package/actions/sosl-search/sosl-search.mjs +19 -9
  27. package/actions/update-account/update-account.mjs +54 -41
  28. package/actions/update-contact/update-contact.mjs +59 -40
  29. package/actions/update-opportunity/update-opportunity.mjs +56 -54
  30. package/actions/update-record/update-record.mjs +67 -20
  31. package/common/all-sobjects.mjs +3812 -0
  32. package/common/constants-props.mjs +1539 -0
  33. package/common/props-async-options.mjs +154 -0
  34. package/common/props-utils.mjs +88 -31
  35. package/common/sobjects/account.mjs +349 -22
  36. package/common/sobjects/attachment.mjs +56 -17
  37. package/common/sobjects/campaign.mjs +125 -23
  38. package/common/sobjects/case.mjs +193 -13
  39. package/common/sobjects/caseComment.mjs +28 -4
  40. package/common/sobjects/contact.mjs +207 -43
  41. package/common/sobjects/event.mjs +218 -18
  42. package/common/sobjects/lead.mjs +245 -22
  43. package/common/sobjects/note.mjs +37 -14
  44. package/common/sobjects/opportunity.mjs +148 -22
  45. package/common/sobjects/task.mjs +240 -19
  46. package/common/sobjects/user.mjs +965 -0
  47. package/package.json +2 -2
  48. package/salesforce_rest_api.app.mjs +72 -249
  49. package/sources/common-webhook-methods.mjs +71 -0
  50. package/sources/common.mjs +85 -22
  51. package/sources/new-outbound-message/new-outbound-message.mjs +11 -3
  52. package/sources/new-record-instant/new-record-instant.mjs +95 -7
  53. package/sources/record-deleted-instant/record-deleted-instant.mjs +40 -5
  54. package/sources/record-updated-instant/record-updated-instant.mjs +137 -5
  55. package/actions/common/base.mjs +0 -18
  56. package/actions/find-create-record/find-create-record.mjs +0 -89
  57. package/actions/get-sobject-fields-values/get-sobject-fields-values.mjs +0 -57
  58. package/common/utils.mjs +0 -51
  59. package/sources/common-instant.mjs +0 -146
  60. package/sources/new-record/new-record.mjs +0 -91
  61. package/sources/record-deleted/record-deleted.mjs +0 -51
  62. package/sources/record-updated/record-updated.mjs +0 -94
  63. package/sources/updated-field-on-record/updated-field-on-record.mjs +0 -161
  64. package/sources/updated-field-on-record-instant/updated-field-on-record-instant.mjs +0 -76
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/salesforce_rest_api",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "description": "Pipedream Salesforce (REST API) Components",
5
5
  "main": "salesforce_rest_api.app.mjs",
6
6
  "keywords": [
@@ -10,7 +10,7 @@
10
10
  "homepage": "https://pipedream.com/apps/salesforce_rest_api",
11
11
  "author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
12
12
  "dependencies": {
13
- "@pipedream/platform": "^1.2.0",
13
+ "@pipedream/platform": "^3.0.0",
14
14
  "fast-xml-parser": "^4.3.2",
15
15
  "handlebars": "^4.7.7",
16
16
  "lodash": "^4.17.21",
@@ -4,19 +4,6 @@ export default {
4
4
  type: "app",
5
5
  app: "salesforce_rest_api",
6
6
  propDefinitions: {
7
- sobjectId: {
8
- type: "string",
9
- label: "SObject ID",
10
- description: "ID of the Standard object to get field values from",
11
- async options(context) {
12
- const { objectType } = context;
13
- const { recentItems } = await this.listSObjectTypeIds(objectType);
14
- return recentItems.map((item) => ({
15
- label: item.Name,
16
- value: item.Id,
17
- }));
18
- },
19
- },
20
7
  objectType: {
21
8
  type: "string",
22
9
  label: "SObject Type",
@@ -38,6 +25,21 @@ export default {
38
25
  return sobjects.filter(filter).map(mapper);
39
26
  },
40
27
  },
28
+ recordId: {
29
+ type: "string",
30
+ label: "Record ID",
31
+ description: "The ID of the record of the selected object type.",
32
+ async options({
33
+ objType,
34
+ nameField,
35
+ }) {
36
+ if (!nameField) nameField = await this.getNameFieldForObjectType(objType);
37
+ return this.listRecordOptions({
38
+ objType,
39
+ nameField,
40
+ });
41
+ },
42
+ },
41
43
  field: {
42
44
  type: "string",
43
45
  label: "Field",
@@ -53,37 +55,30 @@ export default {
53
55
  return fields.filter(filter).map(({ name }) => name);
54
56
  },
55
57
  },
56
- fieldUpdatedTo: {
57
- type: "string",
58
- label: "Field Updated to",
59
- description:
60
- "If provided, the trigger will only fire when the updated field is an EXACT MATCH (including spacing and casing) to the value you provide in this field",
61
- optional: true,
62
- },
63
- fieldSelector: {
58
+ fieldsToUpdate: {
64
59
  type: "string[]",
65
- label: "Field Selector",
66
- description: "Select fields for the Standard Object",
67
- options: () => [], // override options for each object, e.g., () => Object.keys(account)
60
+ label: "Fields to Update",
61
+ description: "Select the field(s) you want to update for this record.",
62
+ async options({ objType }) {
63
+ const fields = await this.getFieldsForObjectType(objType);
64
+ return fields.filter((field) => field.updateable).map(({ name }) => name);
65
+ },
68
66
  },
69
- AcceptedEventInviteeIds: {
67
+ fieldsToObtain: {
70
68
  type: "string[]",
71
- label: "Accepted Event Invitee IDs",
72
- async options() {
73
- const { recentItems: contacts } = await this.listSObjectTypeIds("Contact");
74
- const { recentItems: leads } = await this.listSObjectTypeIds("Lead");
75
- const allContacts = [
76
- ...contacts,
77
- ...leads,
78
- ];
79
- return allContacts.map(({
80
- Name, Id,
81
- }) => ({
82
- label: Name,
83
- value: Id,
84
- }));
69
+ label: "Fields to Obtain",
70
+ description: "Select the field(s) to obtain for the selected record(s) (or all records).",
71
+ async options({ objType }) {
72
+ const fields = await this.getFieldsForObjectType(objType);
73
+ return fields.map(({ name }) => name);
85
74
  },
86
- description: "A string array of contact or lead IDs who accepted this event. This JunctionIdList is linked to the AcceptedEventRelation child relationship. Warning Adding a JunctionIdList field name to the fieldsToNull property deletes all related junction records. This action can't be undone.",
75
+ },
76
+ useAdvancedProps: {
77
+ type: "boolean",
78
+ label: "See All Props",
79
+ description: "Set to true to see all available props for this object.",
80
+ optional: true,
81
+ reloadProps: true,
87
82
  },
88
83
  },
89
84
  methods: {
@@ -138,10 +133,6 @@ export default {
138
133
  const baseUrl = this._sObjectTypeApiUrl(sObjectType);
139
134
  return `${baseUrl}/describe`;
140
135
  },
141
- _sObjectTypeUpdatedApiUrl(sObjectType) {
142
- const baseUrl = this._sObjectTypeApiUrl(sObjectType);
143
- return `${baseUrl}/updated`;
144
- },
145
136
  _sObjectTypeDeletedApiUrl(sObjectType) {
146
137
  const baseUrl = this._sObjectTypeApiUrl(sObjectType);
147
138
  return `${baseUrl}/deleted`;
@@ -223,35 +214,32 @@ export default {
223
214
  });
224
215
  return data.fields;
225
216
  },
226
- async getHistorySObjectForObjectType(objectType) {
227
- const { sobjects } = await this.listSObjectTypes();
228
- const historyObject = sobjects.find(
229
- (sobject) =>
230
- sobject.associateParentEntity === objectType &&
231
- this.isHistorySObject(sobject),
232
- );
233
- return historyObject;
234
- },
235
- async createObject(objectType, data) {
236
- const url = `${this._sObjectsApiUrl()}/${objectType}`;
217
+ async createObject({
218
+ objectType, ...args
219
+ }) {
237
220
  return this._makeRequest({
238
- url,
239
- data,
221
+ url: `${this._sObjectsApiUrl()}/${objectType}`,
240
222
  method: "POST",
223
+ ...args,
241
224
  });
242
225
  },
243
- async deleteObject(objectType, sobjectId) {
244
- const url = `${this._sObjectsApiUrl()}/${objectType}/${sobjectId}`;
226
+ async deleteRecord({
227
+ sobjectType, recordId, ...args
228
+ }) {
229
+ const url = `${this._sObjectsApiUrl()}/${sobjectType}/${recordId}`;
245
230
  return this._makeRequest({
246
231
  url,
247
232
  method: "DELETE",
233
+ ...args,
248
234
  });
249
235
  },
250
- async getRecords(objectType, params) {
236
+ async getRecords({
237
+ objectType, ...args
238
+ }) {
251
239
  const url = `${this._sCompositeApiUrl()}/${objectType}`;
252
240
  return this._makeRequest({
253
241
  url,
254
- params,
242
+ ...args,
255
243
  });
256
244
  },
257
245
  async getSObject(objectType, id, params = null) {
@@ -261,17 +249,6 @@ export default {
261
249
  params,
262
250
  });
263
251
  },
264
- async getUpdatedForObjectType(objectType, start, end) {
265
- const url = this._sObjectTypeUpdatedApiUrl(objectType);
266
- const params = {
267
- start: this._formatDateString(start),
268
- end: this._formatDateString(end),
269
- };
270
- return this._makeRequest({
271
- url,
272
- params,
273
- });
274
- },
275
252
  async getDeletedForObjectType(objectType, start, end) {
276
253
  const url = this._sObjectTypeDeletedApiUrl(objectType);
277
254
  const params = {
@@ -293,159 +270,6 @@ export default {
293
270
  },
294
271
  });
295
272
  },
296
- async createAccount({
297
- $, data,
298
- }) {
299
- const url = this._sObjectTypeApiUrl("Account");
300
- return this._makeRequest({
301
- $,
302
- url,
303
- method: "POST",
304
- data,
305
- });
306
- },
307
- async updateAccount({
308
- $, id, data,
309
- }) {
310
- const url = this._sObjectDetailsApiUrl("Account", id);
311
- return this._makeRequest({
312
- $,
313
- url,
314
- method: "PATCH",
315
- data,
316
- });
317
- },
318
- async createAttachment({
319
- $, data,
320
- }) {
321
- const url = this._sObjectTypeApiUrl("Attachment");
322
- return this._makeRequest({
323
- $,
324
- url,
325
- method: "POST",
326
- data,
327
- });
328
- },
329
- async createCampaign({
330
- $, data,
331
- }) {
332
- const url = this._sObjectTypeApiUrl("Campaign");
333
- return this._makeRequest({
334
- $,
335
- url,
336
- method: "POST",
337
- data,
338
- });
339
- },
340
- async createCase({
341
- $, data,
342
- }) {
343
- const url = this._sObjectTypeApiUrl("Case");
344
- return this._makeRequest({
345
- $,
346
- url,
347
- method: "POST",
348
- data,
349
- });
350
- },
351
- async createCaseComment({
352
- $, data,
353
- }) {
354
- const url = this._sObjectTypeApiUrl("CaseComment");
355
- return this._makeRequest({
356
- $,
357
- url,
358
- method: "POST",
359
- data,
360
- });
361
- },
362
- async createContact({
363
- $, data,
364
- }) {
365
- const url = this._sObjectTypeApiUrl("Contact");
366
- return this._makeRequest({
367
- $,
368
- url,
369
- method: "POST",
370
- data,
371
- });
372
- },
373
- async updateContact({
374
- $, id, data,
375
- }) {
376
- const url = this._sObjectDetailsApiUrl("Contact", id);
377
- return this._makeRequest({
378
- $,
379
- url,
380
- method: "PATCH",
381
- data,
382
- });
383
- },
384
- async createEvent({
385
- $, data,
386
- }) {
387
- const url = this._sObjectTypeApiUrl("Event");
388
- return this._makeRequest({
389
- $,
390
- url,
391
- method: "POST",
392
- data,
393
- });
394
- },
395
- async createLead({
396
- $, data,
397
- }) {
398
- const url = this._sObjectTypeApiUrl("Lead");
399
- return this._makeRequest({
400
- $,
401
- url,
402
- method: "POST",
403
- data,
404
- });
405
- },
406
- async createNote({
407
- $, data,
408
- }) {
409
- const url = this._sObjectTypeApiUrl("Note");
410
- return this._makeRequest({
411
- $,
412
- url,
413
- method: "POST",
414
- data,
415
- });
416
- },
417
- async createOpportunity({
418
- $, data,
419
- }) {
420
- const url = this._sObjectTypeApiUrl("Opportunity");
421
- return this._makeRequest({
422
- $,
423
- url,
424
- method: "POST",
425
- data,
426
- });
427
- },
428
- async updateOpportunity({
429
- $, id, data,
430
- }) {
431
- const url = this._sObjectDetailsApiUrl("Opportunity", id);
432
- return this._makeRequest({
433
- $,
434
- url,
435
- method: "PATCH",
436
- data,
437
- });
438
- },
439
- async getRecordFieldValues(sobjectName, {
440
- $, id, params,
441
- }) {
442
- const url = this._sObjectDetailsApiUrl(sobjectName, id);
443
- return this._makeRequest({
444
- $,
445
- url,
446
- params,
447
- });
448
- },
449
273
  async createRecord(sobjectName, {
450
274
  $, data,
451
275
  }) {
@@ -468,27 +292,6 @@ export default {
468
292
  data,
469
293
  });
470
294
  },
471
- async createTask({
472
- $, data,
473
- }) {
474
- const url = this._sObjectTypeApiUrl("Task");
475
- return this._makeRequest({
476
- $,
477
- url,
478
- method: "POST",
479
- data,
480
- });
481
- },
482
- async deleteOpportunity({
483
- $, id,
484
- }) {
485
- const url = this._sObjectDetailsApiUrl("Opportunity", id);
486
- return this._makeRequest({
487
- $,
488
- url,
489
- method: "DELETE",
490
- });
491
- },
492
295
  async query({
493
296
  $, query,
494
297
  }) {
@@ -499,6 +302,26 @@ export default {
499
302
  url,
500
303
  });
501
304
  },
305
+ async listRecordOptions({
306
+ objType,
307
+ nameField = "Id",
308
+ }) {
309
+ const fields = [
310
+ "Id",
311
+ ...nameField === "Id"
312
+ ? []
313
+ : [
314
+ nameField,
315
+ ],
316
+ ];
317
+ const { records } = await this.query({
318
+ query: `SELECT ${fields.join(", ")} FROM ${objType}`,
319
+ });
320
+ return records?.map?.((item) => ({
321
+ label: item[nameField],
322
+ value: item.Id,
323
+ })) ?? [];
324
+ },
502
325
  async search({
503
326
  $, search,
504
327
  }) {
@@ -509,14 +332,14 @@ export default {
509
332
  url,
510
333
  });
511
334
  },
512
- async parameterizedSearch(params) {
335
+ async parameterizedSearch(args) {
513
336
  const baseUrl = this._baseApiVersionUrl();
514
337
  const url = `${baseUrl}/parameterizedSearch/`;
515
338
 
516
339
  return this._makeRequest({
517
340
  url,
518
341
  method: "GET",
519
- params,
342
+ ...args,
520
343
  });
521
344
  },
522
345
  async insertBlobData(sobjectName, {
@@ -534,7 +357,7 @@ export default {
534
357
  };
535
358
  return axios($ ?? this, requestConfig);
536
359
  },
537
- async postFeed(args = {}) {
360
+ async postFeed(args) {
538
361
  const baseUrl = this._baseApiVersionUrl();
539
362
  const url = `${baseUrl}/chatter/feed-elements`;
540
363
  return this._makeRequest({
@@ -0,0 +1,71 @@
1
+ import salesforceWebhooks from "salesforce-webhooks";
2
+
3
+ const { SalesforceClient } = salesforceWebhooks;
4
+
5
+ export default {
6
+ getClient() {
7
+ const { salesforce } = this;
8
+ return new SalesforceClient({
9
+ apiVersion: salesforce._apiVersion(),
10
+ authToken: salesforce._authToken(),
11
+ instance: salesforce._subdomain(),
12
+ });
13
+ },
14
+ createWebhook(args = {}) {
15
+ const client = this.getClient();
16
+ return client.createWebhook(args);
17
+ },
18
+ deleteWebhook(args = {}) {
19
+ const client = this.getClient();
20
+ return client.deleteWebhook(args);
21
+ },
22
+ _getSecretToken() {
23
+ return this.db.get("secretToken");
24
+ },
25
+ _setSecretToken(secretToken) {
26
+ this.db.set("secretToken", secretToken);
27
+ },
28
+ _getWebhookData() {
29
+ return this.db.get("webhookData");
30
+ },
31
+ _setWebhookData(webhookData) {
32
+ this.db.set("webhookData", webhookData);
33
+ },
34
+ _isValidSource(event) {
35
+ const webhookToken = event.headers["x-webhook-token"];
36
+ const secretToken = this._getSecretToken();
37
+ return webhookToken === secretToken;
38
+ },
39
+ processWebhookEvent(event) {
40
+ const { body } = event;
41
+ const meta = this.generateWebhookMeta(event);
42
+ this.$emit(body, meta);
43
+ },
44
+ generateWebhookMeta() {
45
+ throw new Error("generateWebhookMeta is not implemented");
46
+ },
47
+ getEventType() {
48
+ throw new Error("getEventType is not implemented");
49
+ },
50
+ /**
51
+ * This method returns the fields in the SObject type (e.g. Account, Lead, etc.) that the event
52
+ * source should listen for updates to. This base implementation returns `undefined`, to not
53
+ * necessitate any specific fields to be updated.
54
+ *
55
+ * @returns the fields in the SObject type for which to receive updates
56
+ */
57
+ getFieldsToCheck() {
58
+ return undefined;
59
+ },
60
+ /**
61
+ * This method returns whether the event source should listen for updates where `all` the fields
62
+ * in the SObject are updated, or when `any` of them are. This base implementation returns
63
+ * `undefined` to use to client's default `fieldToCheckMode` (`any`).
64
+ *
65
+ * @returns whether the webhook should receive events when `all` the fields to check are
66
+ * updated, or when `any` of them are
67
+ */
68
+ getFieldsToCheckMode() {
69
+ return undefined;
70
+ },
71
+ };
@@ -1,21 +1,26 @@
1
1
  import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
2
2
  import salesforce from "../salesforce_rest_api.app.mjs";
3
3
  import constants from "../common/constants.mjs";
4
+ import { v4 as uuidv4 } from "uuid";
5
+ import commonWebhookMethods from "./common-webhook-methods.mjs";
4
6
 
5
7
  export default {
6
8
  dedupe: "unique",
7
9
  props: {
8
10
  salesforce,
9
11
  db: "$.service.db",
10
- // eslint-disable-next-line pipedream/props-label,pipedream/props-description
12
+ http: {
13
+ type: "$.interface.http",
14
+ customResponse: true,
15
+ },
11
16
  timer: {
12
17
  type: "$.interface.timer",
18
+ description: "The timer is only used as a fallback if instant event delivery (webhook) is not available.",
13
19
  default: {
14
20
  intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
15
21
  },
16
22
  },
17
23
  objectType: {
18
- type: "string",
19
24
  label: "Object Type",
20
25
  description: "The type of object for which to monitor events",
21
26
  propDefinition: [
@@ -26,17 +31,51 @@ export default {
26
31
  },
27
32
  hooks: {
28
33
  async activate() {
29
- const latestDateCovered = this.getLatestDateCovered();
30
- if (!latestDateCovered) {
31
- const now = new Date().toISOString();
32
- this.setLatestDateCovered(now);
34
+ // Attempt to create the webhook
35
+ const secretToken = uuidv4();
36
+ let webhookData;
37
+ try {
38
+ webhookData = await this.createWebhook({
39
+ endpointUrl: this.http.endpoint,
40
+ sObjectType: this.objectType,
41
+ event: this.getEventType(),
42
+ secretToken,
43
+ fieldsToCheck: this.getFieldsToCheck(),
44
+ fieldsToCheckMode: this.getFieldsToCheckMode(),
45
+ skipValidation: true, // neccessary for custom objects
46
+ });
47
+ console.log("Webhook created successfully");
48
+ } catch (err) {
49
+ console.log("Error creating webhook:", err);
50
+ console.log("The source will operate on the polling schedule instead.");
51
+
52
+ const latestDateCovered = this.getLatestDateCovered();
53
+ if (!latestDateCovered) {
54
+ const now = new Date().toISOString();
55
+ this.setLatestDateCovered(now);
56
+ }
57
+
58
+ await this.timerActivateHook?.();
33
59
  }
60
+ this._setSecretToken(secretToken);
61
+ this._setWebhookData(webhookData);
34
62
 
35
63
  const nameField = await this.salesforce.getNameFieldForObjectType(this.objectType);
36
64
  this.setNameField(nameField);
37
65
  },
66
+ async deactivate() {
67
+ // Delete the webhook, if it exists
68
+ const webhookData = this._getWebhookData();
69
+ if (webhookData) {
70
+ await this.deleteWebhook(webhookData);
71
+ }
72
+ },
38
73
  },
39
74
  methods: {
75
+ ...commonWebhookMethods,
76
+ timerActivateHook() {
77
+ return null;
78
+ },
40
79
  getObjectTypeColumns() {
41
80
  return this.db.get("columns") ?? [];
42
81
  },
@@ -55,8 +94,8 @@ export default {
55
94
  setNameField(nameField) {
56
95
  this.db.set("nameField", nameField);
57
96
  },
58
- processEvent() {
59
- throw new Error("processEvent is not implemented");
97
+ processTimerEvent() {
98
+ throw new Error("processTimerEvent is not implemented");
60
99
  },
61
100
  getObjectTypeDescription(objectType) {
62
101
  const { salesforce } = this;
@@ -120,21 +159,45 @@ export default {
120
159
  },
121
160
  },
122
161
  async run(event) {
123
- const startTimestamp = this.getLatestDateCovered();
124
- const endTimestamp = new Date(event.timestamp * 1000).toISOString();
125
- const timeDiffSec = Math.floor(
126
- (Date.parse(endTimestamp) - Date.parse(startTimestamp)) / 1000,
127
- );
128
- if (timeDiffSec < 60) {
129
- console.log(`
130
- Skipping execution since the last one happened approximately ${timeDiffSec} seconds ago
131
- `);
132
- return;
162
+ // Timer event
163
+ if (event.timestamp) {
164
+ if (this._getWebhookData()) {
165
+ console.log("Ignoring timer event (webhook active)");
166
+ return;
167
+ }
168
+ const startTimestamp = this.getLatestDateCovered();
169
+ const endTimestamp = new Date(event.timestamp * 1000).toISOString();
170
+ const timeDiffSec = Math.floor(
171
+ (Date.parse(endTimestamp) - Date.parse(startTimestamp)) / 1000,
172
+ );
173
+ if (timeDiffSec < 60) {
174
+ console.log(`
175
+ Skipping execution (already executed less than 60 seconds ago)
176
+ `);
177
+ return;
178
+ }
179
+
180
+ await this.processTimerEvent({
181
+ startTimestamp,
182
+ endTimestamp,
183
+ });
133
184
  }
134
185
 
135
- await this.processEvent({
136
- startTimestamp,
137
- endTimestamp,
138
- });
186
+ // Webhook event
187
+ else {
188
+ if (!this._isValidSource(event)) {
189
+ this.http.respond({
190
+ statusCode: 404,
191
+ });
192
+ console.log("Skipping event from unrecognized source");
193
+ return;
194
+ }
195
+
196
+ this.http.respond({
197
+ statusCode: 200,
198
+ });
199
+
200
+ await this.processWebhookEvent(event);
201
+ }
139
202
  },
140
203
  };
@@ -5,17 +5,25 @@ export default {
5
5
  type: "source",
6
6
  name: "New Outbound Message (Instant)",
7
7
  key: "salesforce_rest_api-new-outbound-message",
8
- description: "Emit new event when a new outbound message is received in Salesforce. See Salesforce's guide on setting up [Outbound Messaging](https://sforce.co/3JbZJom). Set the Outbound Message's Endpoint URL to the endpoint of the created source. The \"Send Session ID\" option must be enabled for validating outbound messages from Salesforce.",
9
- version: "0.1.6",
8
+ description: "Emit new event when a new outbound message is received in Salesforce.",
9
+ version: "0.1.7",
10
10
  dedupe: "unique",
11
11
  props: {
12
12
  db: "$.service.db",
13
- // eslint-disable-next-line pipedream/props-label,pipedream/props-description
14
13
  http: {
15
14
  type: "$.interface.http",
16
15
  customResponse: true,
17
16
  },
18
17
  salesforce,
18
+ infoBox: {
19
+ type: "alert",
20
+ alertType: "info",
21
+ content: `See Salesforce's guide on [setting up Outbound Messaging](https://sforce.co/3JbZJom).
22
+ \\
23
+ Set the Outbound Message's \`Endpoint URL\` to the endpoint of this source, which you can view after it is created.
24
+ \\
25
+ The \`Send Session ID\` option must be enabled in Salesforce for validating outbound messages.`,
26
+ },
19
27
  },
20
28
  methods: {
21
29
  _unwrapMessage(message) {