Skip to content

Commit aa04172

Browse files
authored
[21758] fix(salesforce_rest_api): enforce parentObjectType on instant deliveries (#21774)
* fix(salesforce_rest_api): enforce parentObjectType on instant deliveries * fix(salesforce_rest_api): enforce parentObjectType on instant deliveries * chore(salesforce): bump patch versions
1 parent 4b012aa commit aa04172

4 files changed

Lines changed: 71 additions & 12 deletions

File tree

components/salesforce_rest_api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/salesforce_rest_api",
3-
"version": "2.0.0",
3+
"version": "2.0.1",
44
"description": "Pipedream Salesforce (REST API) Components",
55
"main": "salesforce_rest_api.app.mjs",
66
"keywords": [

components/salesforce_rest_api/sources/common/common-feed.mjs

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default {
2626
parentObjectType: {
2727
type: "string",
2828
label: "Parent Object Type",
29-
description: "Optional. The Salesforce SObject API name of the parent record to filter by, e.g. `Case` or `Opportunity`. When set, appends `AND Parent.Type = '<value>'` to the SOQL WHERE clause (traversal of the polymorphic `ParentId`). Leave blank to emit events on any parent object.",
29+
description: "Optional. Only emit events whose parent record is of this Salesforce SObject API name, e.g. `Case` or `Opportunity`. Enforced on both delivery paths: `AND Parent.Type = '<value>'` in the polling SOQL, and the parent's Salesforce ID key prefix on instant (webhook) deliveries. Leave blank to emit events on any parent object.",
3030
optional: true,
3131
},
3232
excludeSelf: {
@@ -89,6 +89,62 @@ export default {
8989
}
9090
return userId;
9191
},
92+
_getParentKeyPrefix() {
93+
return this.db.get("parentKeyPrefix");
94+
},
95+
_setParentKeyPrefix(parentKeyPrefix) {
96+
this.db.set("parentKeyPrefix", parentKeyPrefix);
97+
},
98+
async _resolveParentKeyPrefix() {
99+
const objectType = this.parentObjectType;
100+
101+
// The cache records which object type it was resolved for, so editing the
102+
// prop on a deployed source re-resolves instead of reusing a stale prefix.
103+
const cached = this._getParentKeyPrefix();
104+
if (cached?.objectType === objectType) {
105+
return cached.keyPrefix;
106+
}
107+
108+
let keyPrefix;
109+
try {
110+
({ keyPrefix } = await this.getObjectTypeDescription(objectType));
111+
} catch (err) {
112+
console.log(`Error describing ${objectType} to resolve its ID key prefix:`, err);
113+
}
114+
115+
if (!keyPrefix) {
116+
// Not cached, so a transient describe failure is retried on the next event.
117+
console.log(`No ID key prefix available for ${objectType}, falling back to a per-event Parent.Type lookup.`);
118+
return null;
119+
}
120+
121+
this._setParentKeyPrefix({
122+
objectType,
123+
keyPrefix,
124+
});
125+
return keyPrefix;
126+
},
127+
async _parentTypeMatches(record) {
128+
const parentId = record?.ParentId;
129+
if (!parentId) {
130+
return false;
131+
}
132+
133+
// Salesforce ID key prefixes are stable per SObject (Case = `500`,
134+
// Account = `001`, ...), so the parent's type is read straight off the
135+
// pushed ParentId with no extra API call per event.
136+
const keyPrefix = await this._resolveParentKeyPrefix();
137+
if (keyPrefix) {
138+
return parentId.startsWith(keyPrefix);
139+
}
140+
141+
// Fallback for an object with no key prefix: one SOQL lookup per event,
142+
// reusing the same polymorphic traversal as the polling query.
143+
const { records } = await this.query({
144+
query: `SELECT Id FROM ${this.getObjectType()} WHERE Id = '${record.Id}' AND Parent.Type = '${this.parentObjectType}'`,
145+
});
146+
return !!records?.length;
147+
},
92148
async _buildExtraConditions() {
93149
const conditions = [];
94150
if (this.parentObjectType) {
@@ -101,13 +157,16 @@ export default {
101157
return conditions.join(" ");
102158
},
103159
async processWebhookEvent(event) {
104-
// Instant/webhook deliveries can't filter on Parent.Type (the pushed
105-
// payload has ParentId but not the parent's object type), so
106-
// parentObjectType is only enforced on the polling/deploy SOQL queries.
107-
// excludeSelf is enforced here to prevent self-trigger loops.
160+
// Instant/webhook deliveries can't filter on Parent.Type in SOQL (the
161+
// pushed payload carries ParentId but not the parent's object type), so
162+
// both props are enforced here to match the polling/deploy queries.
163+
const record = event.body?.New;
164+
if (this.parentObjectType && !(await this._parentTypeMatches(record))) {
165+
return;
166+
}
108167
if (this.excludeSelf) {
109168
const userId = await this._resolveAuthenticatedUserId();
110-
if (event.body?.New?.CreatedById === userId) {
169+
if (record?.CreatedById === userId) {
111170
return;
112171
}
113172
}

components/salesforce_rest_api/sources/new-feed-comment/new-feed-comment.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export default {
1111
type: "source",
1212
name: "New Chatter Feed Comment (Instant or Polling)",
1313
key: "salesforce_rest_api-new-feed-comment",
14-
description: "Emit new events for each Chatter FeedComment (reply) created in Salesforce, polling `FeedComment` via SOQL on `CreatedDate`. Use this to react to comments on Chatter posts, since Chatter activity does not update the parent record's `LastModifiedDate`. The payload includes both `ParentId` (a polymorphic reference to the feed's parent - either a record feed, e.g. a Case ID starting with `500`, or a User feed) and `FeedItemId` (the ID of the FeedItem the comment belongs to) - these are distinct fields; do not confuse them. Set `parentObjectType` to a parent object API name (e.g. `Case`) to append `AND Parent.Type = '<value>'` to the SOQL WHERE clause. Set `excludeSelf` to `true` to drop comments authored by the connected integration user. Note: querying FeedComment without a parent filter requires the `View All Data` permission on the connected user. Attempts instant delivery via webhook and falls back to timer polling automatically. [See the documentation](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_feedcomment.htm)",
15-
version: "0.0.4",
14+
description: "Emit new events for each Chatter FeedComment (reply) created in Salesforce, polling `FeedComment` via SOQL on `CreatedDate`. Use this to react to comments on Chatter posts, since Chatter activity does not update the parent record's `LastModifiedDate`. The payload includes both `ParentId` (a polymorphic reference to the feed's parent - either a record feed, e.g. a Case ID starting with `500`, or a User feed) and `FeedItemId` (the ID of the FeedItem the comment belongs to) - these are distinct fields; do not confuse them. Set `parentObjectType` to a parent object API name (e.g. `Case`) to only emit comments whose parent record is of that type. Set `excludeSelf` to `true` to drop comments authored by the connected integration user. Note: querying FeedComment without a parent filter requires the `View All Data` permission on the connected user. Attempts instant delivery via webhook and falls back to timer polling automatically. [See the documentation](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_feedcomment.htm)",
15+
version: "0.0.5",
1616
props: {
1717
...common.props,
1818
parentObjectType: {
1919
...common.props.parentObjectType,
20-
description: "Optional. The Salesforce SObject API name of the parent business record to filter by, e.g. `Case`. When set, appends `AND Parent.Type = '<value>'` to the SOQL WHERE clause (traversal of the polymorphic `FeedComment.ParentId`). Leave blank to emit comments on any parent object (requires `View All Data` permission).",
20+
description: "Optional. Only emit comments whose parent record is of this Salesforce SObject API name, e.g. `Case` (traversal of the polymorphic `FeedComment.ParentId`). Enforced on both delivery paths: `AND Parent.Type = '<value>'` in the polling SOQL, and the parent's Salesforce ID key prefix on instant (webhook) deliveries. Leave blank to emit comments on any parent object (requires `View All Data` permission).",
2121
},
2222
excludeSelf: {
2323
...common.props.excludeSelf,

components/salesforce_rest_api/sources/new-feed-item/new-feed-item.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ export default {
1212
name: "New Chatter Feed Item (Instant or Polling)",
1313
key: "salesforce_rest_api-new-feed-item",
1414
description: "Emit new events for each Chatter FeedItem (post) created in Salesforce, polling `FeedItem` via SOQL on `CreatedDate`. Use this to react to Chatter posts on Cases and other records, since Chatter activity does not update the parent record's `LastModifiedDate` (so **New Record (Instant, of Selectable Type)** and **New Case (Instant, of Selectable Type)** never emit for it). Set `parentObjectType` to the parent object's API name (e.g. `Case`, `Opportunity`) to only emit posts whose parent record is of that type. Set `excludeSelf` to `true` to drop posts authored by the connected integration user. Note: `Body` is null for system-generated post types (e.g. `TrackedChange`). Attempts instant delivery via webhook and falls back to timer polling automatically when the Streaming API does not support this object. [See the documentation](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_feeditem.htm)",
15-
version: "0.0.4",
15+
version: "0.0.5",
1616
props: {
1717
...common.props,
1818
parentObjectType: {
1919
...common.props.parentObjectType,
20-
description: "Optional. The Salesforce SObject API name of the parent record to filter by, e.g. `Case` or `Opportunity`. When set, appends `AND Parent.Type = '<value>'` to the SOQL WHERE clause (traversal of the polymorphic `FeedItem.ParentId`). Leave blank to emit posts on any parent object.",
20+
description: "Optional. Only emit posts whose parent record is of this Salesforce SObject API name, e.g. `Case` or `Opportunity` (traversal of the polymorphic `FeedItem.ParentId`). Enforced on both delivery paths: `AND Parent.Type = '<value>'` in the polling SOQL, and the parent's Salesforce ID key prefix on instant (webhook) deliveries. Leave blank to emit posts on any parent object.",
2121
},
2222
excludeSelf: {
2323
...common.props.excludeSelf,

0 commit comments

Comments
 (0)