Skip to content

Commit 006ce53

Browse files
committed
fix: fix automation control in orgs with namespace
For automation control, ensure that orgs with a namespace can manage components in the orgs managed package
1 parent 14042a5 commit 006ce53

File tree

3 files changed

+73
-57
lines changed

3 files changed

+73
-57
lines changed

libs/features/automation-control/src/automation-control-data-utils.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
genericRequest,
55
getCacheItemNonHttp,
66
query,
7+
queryAll,
78
retrieveMetadataFromListMetadata,
89
saveCacheItemNonHttp,
910
} from '@jetstream/shared/data';
@@ -227,7 +228,7 @@ export async function getApexTriggersMetadata(selectedOrg: SalesforceOrgUi, sobj
227228
const apexClassRecords = (
228229
await Promise.all(
229230
splitArrayToMaxSize(sobjects, 300).map((currSobjects) =>
230-
query<ToolingApexTriggerRecord>(selectedOrg, getApexTriggersQuery(currSobjects), true),
231+
query<ToolingApexTriggerRecord>(selectedOrg, getApexTriggersQuery(currSobjects, selectedOrg.orgNamespacePrefix), true),
231232
),
232233
)
233234
).flatMap(({ queryResults }) => queryResults.records);
@@ -239,7 +240,7 @@ export async function getDuplicateRules(selectedOrg: SalesforceOrgUi, sobjects:
239240
const apexClassRecords = (
240241
await Promise.all(
241242
splitArrayToMaxSize(sobjects, 300).map((currSobjects) =>
242-
query<DuplicateRuleRecord>(selectedOrg, getDuplicateRuleQuery(currSobjects), false),
243+
query<DuplicateRuleRecord>(selectedOrg, getDuplicateRuleQuery(currSobjects, selectedOrg.orgNamespacePrefix), false),
243244
),
244245
)
245246
).flatMap(({ queryResults }) => queryResults.records);
@@ -260,7 +261,7 @@ export async function getValidationRulesMetadata(
260261
const validationRuleRecords = (
261262
await Promise.all(
262263
splitArrayToMaxSize(sobjects, 300).map((currSobjects) =>
263-
query<ToolingValidationRuleRecord>(selectedOrg, getValidationRulesQuery(currSobjects), true),
264+
query<ToolingValidationRuleRecord>(selectedOrg, getValidationRulesQuery(currSobjects, selectedOrg.orgNamespacePrefix), true),
264265
),
265266
)
266267
).flatMap(({ queryResults }) => queryResults.records);
@@ -300,7 +301,7 @@ export async function getWorkflowRulesMetadata(
300301
const workflowRuleRecords = (
301302
await Promise.all(
302303
splitArrayToMaxSize(sobjects, 300).map((currSobjects) =>
303-
query<ToolingWorkflowRuleRecord>(selectedOrg, getWorkflowRulesQuery(currSobjects), true),
304+
query<ToolingWorkflowRuleRecord>(selectedOrg, getWorkflowRulesQuery(currSobjects, selectedOrg.orgNamespacePrefix), true),
304305
),
305306
)
306307
).flatMap(({ queryResults }) => queryResults.records);
@@ -331,11 +332,21 @@ export async function getFlowsMetadata(selectedOrg: SalesforceOrgUi, sobjects: s
331332
// NOTE: this is NOT tooling
332333
const flowMetadataRecords = (
333334
await Promise.all(
334-
splitArrayToMaxSize(sobjects, 300).map((currSobjects) => query<FlowViewRecord>(selectedOrg, getFlowsQuery(currSobjects), false)),
335+
splitArrayToMaxSize(sobjects, 300).map((currSobjects) =>
336+
queryAll<FlowViewRecord>(selectedOrg, getFlowsQuery(currSobjects, selectedOrg.orgNamespacePrefix), false),
337+
),
335338
)
336339
)
337340
.flatMap(({ queryResults }) => queryResults.records)
338-
.filter((record) => record.ManageableState === 'unmanaged' || record.ManageableState === 'installedEditable' || record.IsTemplate);
341+
.filter(
342+
(record) =>
343+
// WHERE clause filtering does not work correctly for the ManageableState field -0 this is a salesforce bug
344+
(record.NamespacePrefix && record.NamespacePrefix === selectedOrg.orgNamespacePrefix) ||
345+
record.ManageableState === 'unmanaged' ||
346+
record.ManageableState === 'deprecatedEditable' ||
347+
record.ManageableState === 'installedEditable' ||
348+
record.IsTemplate,
349+
);
339350
return flowMetadataRecords;
340351
}
341352

@@ -360,7 +371,8 @@ export async function getProcessBuildersMetadata(
360371
) {
361372
// this list will be filtered based on the sobject and will artificially have TriggerObjectOrEvent.QualifiedApiName added
362373
const sobjectSet = new Set(sobjects);
363-
let workflowRuleRecords = (await query<FlowViewRecord>(selectedOrg, getProcessBuildersQuery(), false)).queryResults.records;
374+
let workflowRuleRecords = (await query<FlowViewRecord>(selectedOrg, getProcessBuildersQuery(selectedOrg.orgNamespacePrefix), false))
375+
.queryResults.records;
364376

365377
let flowIdToSobject: Record<string, string>;
366378
const flowIdToSobjectCache = skipCache ? null : await getCacheItemNonHttp<Record<string, string>>(selectedOrg, PROCESS_BUILDER_CACHE_ID);

libs/features/automation-control/src/automation-control-soql-utils.tsx

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,47 @@
11
import { logger } from '@jetstream/shared/client-logger';
2-
import { composeQuery, getField } from '@jetstreamapp/soql-parser-js';
2+
import { Maybe } from '@jetstream/types';
3+
import { composeQuery, getField, WhereClause } from '@jetstreamapp/soql-parser-js';
34

45
/**
56
* SOQL QUERIES
67
*/
78

8-
export function getApexTriggersQuery(sobjects: string[]) {
9+
/**
10+
* filtering is broken on Salesforce for some ManageableState values (e.g. released)
11+
*/
12+
function getNamespaceWhereClause(orgNamespacePrefix: Maybe<string>): WhereClause {
13+
if (!orgNamespacePrefix) {
14+
return {
15+
left: {
16+
field: 'NamespacePrefix',
17+
operator: '=',
18+
value: 'NULL',
19+
literalType: 'NULL',
20+
},
21+
} satisfies WhereClause;
22+
}
23+
return {
24+
left: {
25+
openParen: 1,
26+
field: 'NamespacePrefix',
27+
operator: '=',
28+
value: 'NULL',
29+
literalType: 'NULL',
30+
},
31+
operator: 'OR',
32+
right: {
33+
left: {
34+
closeParen: 1,
35+
field: 'NamespacePrefix',
36+
operator: '=',
37+
value: orgNamespacePrefix,
38+
literalType: 'STRING',
39+
},
40+
},
41+
} satisfies WhereClause;
42+
}
43+
44+
export function getApexTriggersQuery(sobjects: string[], orgNamespacePrefix: Maybe<string>) {
945
const soql = composeQuery({
1046
fields: [
1147
getField('Id'),
@@ -32,14 +68,7 @@ export function getApexTriggersQuery(sobjects: string[]) {
3268
literalType: 'STRING',
3369
},
3470
operator: 'AND',
35-
right: {
36-
left: {
37-
field: 'ManageableState',
38-
operator: '=',
39-
value: 'unmanaged',
40-
literalType: 'STRING',
41-
},
42-
},
71+
right: getNamespaceWhereClause(orgNamespacePrefix),
4372
},
4473
orderBy: [
4574
{
@@ -54,7 +83,7 @@ export function getApexTriggersQuery(sobjects: string[]) {
5483
return soql;
5584
}
5685

57-
export function getDuplicateRuleQuery(sobjects: string[]) {
86+
export function getDuplicateRuleQuery(sobjects: string[], orgNamespacePrefix: Maybe<string>) {
5887
const soql = composeQuery({
5988
fields: [
6089
getField('Id'),
@@ -82,14 +111,7 @@ export function getDuplicateRuleQuery(sobjects: string[]) {
82111
literalType: 'STRING',
83112
},
84113
operator: 'AND',
85-
right: {
86-
left: {
87-
field: 'NamespacePrefix',
88-
operator: '=',
89-
value: 'NULL',
90-
literalType: 'NULL',
91-
},
92-
},
114+
right: getNamespaceWhereClause(orgNamespacePrefix),
93115
},
94116
orderBy: [
95117
{
@@ -104,7 +126,7 @@ export function getDuplicateRuleQuery(sobjects: string[]) {
104126
return soql;
105127
}
106128

107-
export function getValidationRulesQuery(sobjects: string[]) {
129+
export function getValidationRulesQuery(sobjects: string[], orgNamespacePrefix: Maybe<string>) {
108130
const soql = composeQuery({
109131
fields: [
110132
getField('Id'),
@@ -134,14 +156,7 @@ export function getValidationRulesQuery(sobjects: string[]) {
134156
literalType: 'STRING',
135157
},
136158
operator: 'AND',
137-
right: {
138-
left: {
139-
field: 'ManageableState',
140-
operator: '=',
141-
value: 'unmanaged',
142-
literalType: 'STRING',
143-
},
144-
},
159+
right: getNamespaceWhereClause(orgNamespacePrefix),
145160
},
146161
orderBy: [
147162
{
@@ -157,7 +172,7 @@ export function getValidationRulesQuery(sobjects: string[]) {
157172
}
158173

159174
// TODO: there is no active flag for these without getting all metadata - WTF
160-
export function getWorkflowRulesQuery(sobjects: string[]) {
175+
export function getWorkflowRulesQuery(sobjects: string[], orgNamespacePrefix: Maybe<string>) {
161176
const soql = composeQuery({
162177
fields: [
163178
getField('Id'),
@@ -182,14 +197,7 @@ export function getWorkflowRulesQuery(sobjects: string[]) {
182197
literalType: 'STRING',
183198
},
184199
operator: 'AND',
185-
right: {
186-
left: {
187-
field: 'ManageableState',
188-
operator: '=',
189-
value: 'unmanaged',
190-
literalType: 'STRING',
191-
},
192-
},
200+
right: getNamespaceWhereClause(orgNamespacePrefix),
193201
},
194202
orderBy: [
195203
{
@@ -204,7 +212,7 @@ export function getWorkflowRulesQuery(sobjects: string[]) {
204212
return soql;
205213
}
206214

207-
export function getFlowsQuery(sobjects: string[]) {
215+
export function getFlowsQuery(sobjects: string[], orgNamespacePrefix: Maybe<string>) {
208216
const soql = composeQuery({
209217
fields: [
210218
getField('Id'),
@@ -219,6 +227,7 @@ export function getFlowsQuery(sobjects: string[]) {
219227
getField('LastModifiedBy'),
220228
getField('FORMAT(LastModifiedDate)'),
221229
getField('LatestVersionId'),
230+
getField('NamespacePrefix'),
222231
getField('ProcessType'),
223232
getField('TriggerObjectOrEventId'),
224233
getField('TriggerObjectOrEvent.QualifiedApiName'),
@@ -240,20 +249,12 @@ export function getFlowsQuery(sobjects: string[]) {
240249
operator: 'AND',
241250
right: {
242251
left: {
243-
field: 'ManageableState',
252+
field: 'TriggerType',
244253
operator: 'IN',
245-
value: ['unmanaged', 'installedEditable', 'installed'],
254+
value: ['RecordBeforeSave', 'RecordBeforeDelete', 'RecordAfterSave'],
246255
literalType: 'STRING',
247256
},
248-
operator: 'AND',
249-
right: {
250-
left: {
251-
field: 'TriggerType',
252-
operator: 'IN',
253-
value: ['RecordBeforeSave', 'RecordBeforeDelete', 'RecordAfterSave'],
254-
literalType: 'STRING',
255-
},
256-
},
257+
// these records are filtered after being queried
257258
},
258259
},
259260
orderBy: [
@@ -269,7 +270,7 @@ export function getFlowsQuery(sobjects: string[]) {
269270
return soql;
270271
}
271272

272-
export function getProcessBuildersQuery() {
273+
export function getProcessBuildersQuery(orgNamespacePrefix: Maybe<string>) {
273274
const soql = composeQuery({
274275
fields: [
275276
getField('Id'),
@@ -300,6 +301,8 @@ export function getProcessBuildersQuery() {
300301
value: 'Workflow',
301302
literalType: 'STRING',
302303
},
304+
operator: 'AND',
305+
right: getNamespaceWhereClause(orgNamespacePrefix),
303306
},
304307
orderBy: [
305308
{

libs/features/automation-control/src/automation-control-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ export interface FlowViewRecord extends Omit<SystemFields, 'CreatedDate' | 'Crea
203203
IsTemplate: boolean;
204204
LatestVersionId: string;
205205
LastModifiedBy: string;
206+
NamespacePrefix: string | null;
206207
ProcessType: 'AutoLaunchedFlow' | 'Workflow' | 'InvocableProcess';
207208
// ID of the object or platform event that triggers this flow. This field is available in API version 53.0 and later.
208209
TriggerObjectOrEventId?: string;

0 commit comments

Comments
 (0)