rflib-plugin 0.18.1 → 0.19.3
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.
- package/README.md +116 -0
- package/lib/commands/rflib/debug/applicationevents/get.d.ts +17 -0
- package/lib/commands/rflib/debug/applicationevents/get.js +63 -0
- package/lib/commands/rflib/debug/applicationevents/get.js.map +1 -0
- package/lib/commands/rflib/debug/logarchives/get.d.ts +14 -0
- package/lib/commands/rflib/debug/logarchives/get.js +43 -0
- package/lib/commands/rflib/debug/logarchives/get.js.map +1 -0
- package/lib/commands/rflib/debug/loggersettings/get.d.ts +12 -0
- package/lib/commands/rflib/debug/loggersettings/get.js +30 -0
- package/lib/commands/rflib/debug/loggersettings/get.js.map +1 -0
- package/lib/commands/rflib/debug/loggersettings/update.d.ts +16 -0
- package/lib/commands/rflib/debug/loggersettings/update.js +57 -0
- package/lib/commands/rflib/debug/loggersettings/update.js.map +1 -0
- package/lib/commands/rflib/debug/userpermissions/get.d.ts +14 -0
- package/lib/commands/rflib/debug/userpermissions/get.js +52 -0
- package/lib/commands/rflib/debug/userpermissions/get.js.map +1 -0
- package/lib/shared/loggerSettingsRules.d.ts +29 -0
- package/lib/shared/loggerSettingsRules.js +157 -0
- package/lib/shared/loggerSettingsRules.js.map +1 -0
- package/lib/shared/orgClient.d.ts +78 -0
- package/lib/shared/orgClient.js +272 -0
- package/lib/shared/orgClient.js.map +1 -0
- package/lib/shared/permissionAggregator.d.ts +8 -0
- package/lib/shared/permissionAggregator.js +143 -0
- package/lib/shared/permissionAggregator.js.map +1 -0
- package/messages/rflib.debug.applicationevents.get.md +74 -0
- package/messages/rflib.debug.logarchives.get.md +45 -0
- package/messages/rflib.debug.loggersettings.get.md +27 -0
- package/messages/rflib.debug.loggersettings.update.md +63 -0
- package/messages/rflib.debug.userpermissions.get.md +63 -0
- package/oclif.lock +502 -1543
- package/oclif.manifest.json +548 -1
- package/package.json +21 -4
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const QUERY_LIMIT = 24_995;
|
|
2
|
+
// Query for one extra row beyond the cap so we can tell when Salesforce had more to
|
|
3
|
+
// give and our LIMIT clipped it. Without the sentinel, a result set whose true size is
|
|
4
|
+
// > QUERY_LIMIT comes back with exactly QUERY_LIMIT rows and `done: true`, so the
|
|
5
|
+
// truncation check would silently report `false`. The sentinel row is dropped before
|
|
6
|
+
// returning; its presence flips `truncated` to true.
|
|
7
|
+
const FETCH_LIMIT = QUERY_LIMIT + 1;
|
|
8
|
+
const FLS_FIELDS = 'SELECT Parent.Label, Parent.Profile.Name, Parent.IsOwnedByProfile, Parent.PermissionSetGroupId, ' +
|
|
9
|
+
'SobjectType, Field, PermissionsEdit, PermissionsRead';
|
|
10
|
+
const OBJ_FIELDS = 'SELECT Parent.Label, Parent.Profile.Name, Parent.IsOwnedByProfile, Parent.PermissionSetGroupId, ' +
|
|
11
|
+
'SobjectType, PermissionsRead, PermissionsCreate, PermissionsEdit, PermissionsDelete, ' +
|
|
12
|
+
'PermissionsViewAllFields, PermissionsViewAllRecords, PermissionsModifyAllRecords';
|
|
13
|
+
const APEX_FIELDS = 'SELECT Parent.Label, Parent.Profile.Name, Parent.IsOwnedByProfile, Parent.PermissionSetGroupId, ' +
|
|
14
|
+
'SetupEntityType, SetupEntityId';
|
|
15
|
+
const FLS_TABLE = ' FROM FieldPermissions';
|
|
16
|
+
const OBJ_TABLE = ' FROM ObjectPermissions';
|
|
17
|
+
const APEX_TABLE = ' FROM SetupEntityAccess';
|
|
18
|
+
const FLS_ORDER = ' ORDER BY Parent.Profile.Name, Parent.Label, SobjectType, Field';
|
|
19
|
+
const OBJ_ORDER = ' ORDER BY Parent.Profile.Name, Parent.Label, SobjectType';
|
|
20
|
+
const APEX_ORDER = ' ORDER BY Parent.Profile.Name, Parent.Label';
|
|
21
|
+
const APEX_CONDITIONS = " AND (SetupEntityType = 'ApexClass' OR SetupEntityType = 'ApexPage')";
|
|
22
|
+
const ID_PATTERN = /^(?:[a-zA-Z0-9]{15}|[a-zA-Z0-9]{18})$/;
|
|
23
|
+
const SOBJECT_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
24
|
+
function validateId(value, label) {
|
|
25
|
+
if (!ID_PATTERN.test(value)) {
|
|
26
|
+
throw new Error(`Invalid ${label} "${value}". Expected a 15 or 18 character Salesforce ID.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function escapeSingleQuotes(value) {
|
|
30
|
+
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
31
|
+
}
|
|
32
|
+
async function queryAll(conn, soql) {
|
|
33
|
+
const first = await conn.query(soql);
|
|
34
|
+
let records = [...first.records];
|
|
35
|
+
let done = first.done;
|
|
36
|
+
let nextUrl = first.nextRecordsUrl;
|
|
37
|
+
while (!done && nextUrl && records.length < QUERY_LIMIT) {
|
|
38
|
+
// eslint-disable-next-line no-await-in-loop
|
|
39
|
+
const more = await conn.queryMore(nextUrl);
|
|
40
|
+
records = records.concat(more.records);
|
|
41
|
+
done = more.done;
|
|
42
|
+
nextUrl = more.nextRecordsUrl;
|
|
43
|
+
}
|
|
44
|
+
// Truncation: either we still had pages left but bailed out of the loop because we
|
|
45
|
+
// hit the cap, or the total accumulated rows already exceeded the cap and the
|
|
46
|
+
// slice() below is about to drop some. Either way the caller is losing data.
|
|
47
|
+
const truncated = !done || records.length > QUERY_LIMIT;
|
|
48
|
+
return { records: records.slice(0, QUERY_LIMIT), truncated };
|
|
49
|
+
}
|
|
50
|
+
async function getUserPermDetails(conn, userId) {
|
|
51
|
+
const userRows = await conn.query(`SELECT Id, ProfileId, Profile.Name FROM User WHERE Id = '${escapeSingleQuotes(userId)}' LIMIT 1`);
|
|
52
|
+
if (userRows.records.length === 0) {
|
|
53
|
+
throw new Error(`User with Id "${userId}" was not found.`);
|
|
54
|
+
}
|
|
55
|
+
const userRow = userRows.records[0];
|
|
56
|
+
const assignmentRows = (await queryAll(conn, 'SELECT PermissionSetId, PermissionSet.Name, PermissionSet.IsOwnedByProfile, ' +
|
|
57
|
+
'PermissionSetGroupId, PermissionSet.Label ' +
|
|
58
|
+
`FROM PermissionSetAssignment WHERE AssigneeId = '${escapeSingleQuotes(userId)}'`)).records;
|
|
59
|
+
const permissionSetIds = [];
|
|
60
|
+
const permissionSetGroupIds = [];
|
|
61
|
+
const permSetLabels = [];
|
|
62
|
+
for (const psa of assignmentRows) {
|
|
63
|
+
if (psa.PermissionSet?.IsOwnedByProfile)
|
|
64
|
+
continue;
|
|
65
|
+
if (psa.PermissionSetGroupId) {
|
|
66
|
+
permissionSetGroupIds.push(psa.PermissionSetGroupId);
|
|
67
|
+
}
|
|
68
|
+
else if (psa.PermissionSetId) {
|
|
69
|
+
permissionSetIds.push(psa.PermissionSetId);
|
|
70
|
+
if (psa.PermissionSet?.Label)
|
|
71
|
+
permSetLabels.push(psa.PermissionSet.Label);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
profileId: userRow.ProfileId,
|
|
76
|
+
profileName: userRow.Profile?.Name ?? '',
|
|
77
|
+
permissionSetNames: permSetLabels.join(', '),
|
|
78
|
+
permissionSetIds,
|
|
79
|
+
permissionSetGroupIds,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function buildIdList(ids) {
|
|
83
|
+
return "('" + ids.map(escapeSingleQuotes).join("','") + "')";
|
|
84
|
+
}
|
|
85
|
+
function buildUserCondition(details) {
|
|
86
|
+
// FieldPermissions / ObjectPermissions / SetupEntityAccess rows are always keyed by
|
|
87
|
+
// ParentId = PermissionSet. Permission Set Groups don't show up under ParentId — the
|
|
88
|
+
// group's effective grants live on an auto-generated PermissionSet whose
|
|
89
|
+
// Parent.PermissionSetGroupId points back at the group. Filter accordingly.
|
|
90
|
+
const clauses = [`Parent.ProfileId = '${escapeSingleQuotes(details.profileId)}'`];
|
|
91
|
+
if (details.permissionSetIds.length > 0) {
|
|
92
|
+
clauses.push(`ParentId IN ${buildIdList(details.permissionSetIds)}`);
|
|
93
|
+
}
|
|
94
|
+
if (details.permissionSetGroupIds.length > 0) {
|
|
95
|
+
clauses.push(`Parent.PermissionSetGroupId IN ${buildIdList(details.permissionSetGroupIds)}`);
|
|
96
|
+
}
|
|
97
|
+
return ` WHERE (${clauses.join(' OR ')})`;
|
|
98
|
+
}
|
|
99
|
+
function buildSObjectFilter(sobjectType) {
|
|
100
|
+
if (!sobjectType)
|
|
101
|
+
return '';
|
|
102
|
+
if (!SOBJECT_PATTERN.test(sobjectType)) {
|
|
103
|
+
throw new Error(`Invalid sobjectType "${sobjectType}". Use a standard SObject API name (e.g. Account, Order__c).`);
|
|
104
|
+
}
|
|
105
|
+
return ` AND SobjectType = '${escapeSingleQuotes(sobjectType)}'`;
|
|
106
|
+
}
|
|
107
|
+
export async function getUserPermissions(conn, args) {
|
|
108
|
+
if (!args.userId) {
|
|
109
|
+
throw new Error('userId is required');
|
|
110
|
+
}
|
|
111
|
+
validateId(args.userId, 'userId');
|
|
112
|
+
const validTypes = ['FLS', 'OLS', 'APEX', 'ALL'];
|
|
113
|
+
if (!validTypes.includes(args.permissionType)) {
|
|
114
|
+
throw new Error('permissionType must be one of: FLS, OLS, APEX, ALL');
|
|
115
|
+
}
|
|
116
|
+
const details = await getUserPermDetails(conn, args.userId);
|
|
117
|
+
const condition = buildUserCondition(details);
|
|
118
|
+
const result = {
|
|
119
|
+
userId: args.userId,
|
|
120
|
+
profileName: details.profileName,
|
|
121
|
+
permissionSetNames: details.permissionSetNames,
|
|
122
|
+
permissionType: args.permissionType,
|
|
123
|
+
};
|
|
124
|
+
if (args.permissionType === 'FLS' || args.permissionType === 'ALL') {
|
|
125
|
+
const objectFilter = buildSObjectFilter(args.sobjectType);
|
|
126
|
+
const { records, truncated } = await queryAll(conn, `${FLS_FIELDS}${FLS_TABLE}${condition}${objectFilter}${FLS_ORDER} LIMIT ${FETCH_LIMIT}`);
|
|
127
|
+
result.flsPermissions = records;
|
|
128
|
+
result.flsTruncated = truncated;
|
|
129
|
+
}
|
|
130
|
+
if (args.permissionType === 'OLS' || args.permissionType === 'ALL') {
|
|
131
|
+
const objectFilter = buildSObjectFilter(args.sobjectType);
|
|
132
|
+
const { records, truncated } = await queryAll(conn, `${OBJ_FIELDS}${OBJ_TABLE}${condition}${objectFilter}${OBJ_ORDER} LIMIT ${FETCH_LIMIT}`);
|
|
133
|
+
result.olsPermissions = records;
|
|
134
|
+
result.olsTruncated = truncated;
|
|
135
|
+
}
|
|
136
|
+
if (args.permissionType === 'APEX' || args.permissionType === 'ALL') {
|
|
137
|
+
const { records, truncated } = await queryAll(conn, `${APEX_FIELDS}${APEX_TABLE}${condition}${APEX_CONDITIONS}${APEX_ORDER} LIMIT ${FETCH_LIMIT}`);
|
|
138
|
+
result.apexPermissions = records;
|
|
139
|
+
result.apexTruncated = truncated;
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=permissionAggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissionAggregator.js","sourceRoot":"","sources":["../../src/shared/permissionAggregator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Connection } from '@salesforce/core';\n\nconst QUERY_LIMIT = 24_995;\n// Query for one extra row beyond the cap so we can tell when Salesforce had more to\n// give and our LIMIT clipped it. Without the sentinel, a result set whose true size is\n// > QUERY_LIMIT comes back with exactly QUERY_LIMIT rows and `done: true`, so the\n// truncation check would silently report `false`. The sentinel row is dropped before\n// returning; its presence flips `truncated` to true.\nconst FETCH_LIMIT = QUERY_LIMIT + 1;\n\nconst FLS_FIELDS =\n 'SELECT Parent.Label, Parent.Profile.Name, Parent.IsOwnedByProfile, Parent.PermissionSetGroupId, ' +\n 'SobjectType, Field, PermissionsEdit, PermissionsRead';\nconst OBJ_FIELDS =\n 'SELECT Parent.Label, Parent.Profile.Name, Parent.IsOwnedByProfile, Parent.PermissionSetGroupId, ' +\n 'SobjectType, PermissionsRead, PermissionsCreate, PermissionsEdit, PermissionsDelete, ' +\n 'PermissionsViewAllFields, PermissionsViewAllRecords, PermissionsModifyAllRecords';\nconst APEX_FIELDS =\n 'SELECT Parent.Label, Parent.Profile.Name, Parent.IsOwnedByProfile, Parent.PermissionSetGroupId, ' +\n 'SetupEntityType, SetupEntityId';\n\nconst FLS_TABLE = ' FROM FieldPermissions';\nconst OBJ_TABLE = ' FROM ObjectPermissions';\nconst APEX_TABLE = ' FROM SetupEntityAccess';\n\nconst FLS_ORDER = ' ORDER BY Parent.Profile.Name, Parent.Label, SobjectType, Field';\nconst OBJ_ORDER = ' ORDER BY Parent.Profile.Name, Parent.Label, SobjectType';\nconst APEX_ORDER = ' ORDER BY Parent.Profile.Name, Parent.Label';\n\nconst APEX_CONDITIONS = \" AND (SetupEntityType = 'ApexClass' OR SetupEntityType = 'ApexPage')\";\n\nconst ID_PATTERN = /^(?:[a-zA-Z0-9]{15}|[a-zA-Z0-9]{18})$/;\nconst SOBJECT_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;\n\nexport type PermissionType = 'FLS' | 'OLS' | 'APEX' | 'ALL';\n\nexport type GetUserPermissionsArgs = {\n userId: string;\n permissionType: PermissionType;\n sobjectType?: string;\n};\n\ntype UserPermDetails = {\n profileId: string;\n profileName: string;\n permissionSetNames: string;\n permissionSetIds: string[];\n permissionSetGroupIds: string[];\n};\n\ntype UserRow = { Id: string; ProfileId: string; Profile?: { Name?: string } } & Record<string, unknown>;\ntype PsaRow = {\n PermissionSetId: string | null;\n PermissionSetGroupId: string | null;\n PermissionSet?: { Name?: string; Label?: string; IsOwnedByProfile?: boolean };\n} & Record<string, unknown>;\n\nfunction validateId(value: string, label: string): void {\n if (!ID_PATTERN.test(value)) {\n throw new Error(`Invalid ${label} \"${value}\". Expected a 15 or 18 character Salesforce ID.`);\n }\n}\n\nfunction escapeSingleQuotes(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\ntype QueryAllResult<T> = { records: T[]; truncated: boolean };\n\nasync function queryAll<T extends Record<string, unknown>>(\n conn: Connection,\n soql: string,\n): Promise<QueryAllResult<T>> {\n const first = await conn.query<T>(soql);\n let records: T[] = [...first.records];\n let done = first.done;\n let nextUrl = first.nextRecordsUrl;\n while (!done && nextUrl && records.length < QUERY_LIMIT) {\n // eslint-disable-next-line no-await-in-loop\n const more = await conn.queryMore<T>(nextUrl);\n records = records.concat(more.records);\n done = more.done;\n nextUrl = more.nextRecordsUrl;\n }\n // Truncation: either we still had pages left but bailed out of the loop because we\n // hit the cap, or the total accumulated rows already exceeded the cap and the\n // slice() below is about to drop some. Either way the caller is losing data.\n const truncated = !done || records.length > QUERY_LIMIT;\n return { records: records.slice(0, QUERY_LIMIT), truncated };\n}\n\nasync function getUserPermDetails(conn: Connection, userId: string): Promise<UserPermDetails> {\n const userRows = await conn.query<UserRow>(\n `SELECT Id, ProfileId, Profile.Name FROM User WHERE Id = '${escapeSingleQuotes(userId)}' LIMIT 1`,\n );\n if (userRows.records.length === 0) {\n throw new Error(`User with Id \"${userId}\" was not found.`);\n }\n const userRow = userRows.records[0];\n\n const assignmentRows = (\n await queryAll<PsaRow>(\n conn,\n 'SELECT PermissionSetId, PermissionSet.Name, PermissionSet.IsOwnedByProfile, ' +\n 'PermissionSetGroupId, PermissionSet.Label ' +\n `FROM PermissionSetAssignment WHERE AssigneeId = '${escapeSingleQuotes(userId)}'`,\n )\n ).records;\n\n const permissionSetIds: string[] = [];\n const permissionSetGroupIds: string[] = [];\n const permSetLabels: string[] = [];\n\n for (const psa of assignmentRows) {\n if (psa.PermissionSet?.IsOwnedByProfile) continue;\n if (psa.PermissionSetGroupId) {\n permissionSetGroupIds.push(psa.PermissionSetGroupId);\n } else if (psa.PermissionSetId) {\n permissionSetIds.push(psa.PermissionSetId);\n if (psa.PermissionSet?.Label) permSetLabels.push(psa.PermissionSet.Label);\n }\n }\n\n return {\n profileId: userRow.ProfileId,\n profileName: userRow.Profile?.Name ?? '',\n permissionSetNames: permSetLabels.join(', '),\n permissionSetIds,\n permissionSetGroupIds,\n };\n}\n\nfunction buildIdList(ids: readonly string[]): string {\n return \"('\" + ids.map(escapeSingleQuotes).join(\"','\") + \"')\";\n}\n\nfunction buildUserCondition(details: UserPermDetails): string {\n // FieldPermissions / ObjectPermissions / SetupEntityAccess rows are always keyed by\n // ParentId = PermissionSet. Permission Set Groups don't show up under ParentId — the\n // group's effective grants live on an auto-generated PermissionSet whose\n // Parent.PermissionSetGroupId points back at the group. Filter accordingly.\n const clauses: string[] = [`Parent.ProfileId = '${escapeSingleQuotes(details.profileId)}'`];\n if (details.permissionSetIds.length > 0) {\n clauses.push(`ParentId IN ${buildIdList(details.permissionSetIds)}`);\n }\n if (details.permissionSetGroupIds.length > 0) {\n clauses.push(`Parent.PermissionSetGroupId IN ${buildIdList(details.permissionSetGroupIds)}`);\n }\n return ` WHERE (${clauses.join(' OR ')})`;\n}\n\nfunction buildSObjectFilter(sobjectType: string | undefined): string {\n if (!sobjectType) return '';\n if (!SOBJECT_PATTERN.test(sobjectType)) {\n throw new Error(`Invalid sobjectType \"${sobjectType}\". Use a standard SObject API name (e.g. Account, Order__c).`);\n }\n return ` AND SobjectType = '${escapeSingleQuotes(sobjectType)}'`;\n}\n\nexport async function getUserPermissions(\n conn: Connection,\n args: GetUserPermissionsArgs,\n): Promise<Record<string, unknown>> {\n if (!args.userId) {\n throw new Error('userId is required');\n }\n validateId(args.userId, 'userId');\n const validTypes: PermissionType[] = ['FLS', 'OLS', 'APEX', 'ALL'];\n if (!validTypes.includes(args.permissionType)) {\n throw new Error('permissionType must be one of: FLS, OLS, APEX, ALL');\n }\n\n const details = await getUserPermDetails(conn, args.userId);\n const condition = buildUserCondition(details);\n\n const result: Record<string, unknown> = {\n userId: args.userId,\n profileName: details.profileName,\n permissionSetNames: details.permissionSetNames,\n permissionType: args.permissionType,\n };\n\n if (args.permissionType === 'FLS' || args.permissionType === 'ALL') {\n const objectFilter = buildSObjectFilter(args.sobjectType);\n const { records, truncated } = await queryAll(\n conn,\n `${FLS_FIELDS}${FLS_TABLE}${condition}${objectFilter}${FLS_ORDER} LIMIT ${FETCH_LIMIT}`,\n );\n result.flsPermissions = records;\n result.flsTruncated = truncated;\n }\n if (args.permissionType === 'OLS' || args.permissionType === 'ALL') {\n const objectFilter = buildSObjectFilter(args.sobjectType);\n const { records, truncated } = await queryAll(\n conn,\n `${OBJ_FIELDS}${OBJ_TABLE}${condition}${objectFilter}${OBJ_ORDER} LIMIT ${FETCH_LIMIT}`,\n );\n result.olsPermissions = records;\n result.olsTruncated = truncated;\n }\n if (args.permissionType === 'APEX' || args.permissionType === 'ALL') {\n const { records, truncated } = await queryAll(\n conn,\n `${APEX_FIELDS}${APEX_TABLE}${condition}${APEX_CONDITIONS}${APEX_ORDER} LIMIT ${FETCH_LIMIT}`,\n );\n result.apexPermissions = records;\n result.apexTruncated = truncated;\n }\n\n return result;\n}\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# summary
|
|
2
|
+
|
|
3
|
+
Query RFLIB Application Events from a Salesforce org.
|
|
4
|
+
|
|
5
|
+
# description
|
|
6
|
+
|
|
7
|
+
Retrieves rflib_Application_Event__c records from the target org via the Salesforce REST API.
|
|
8
|
+
Application Events are business-level events used to track feature adoption, user actions, and domain-specific milestones.
|
|
9
|
+
Results are ordered by Occurred_On__c descending (most recent first).
|
|
10
|
+
|
|
11
|
+
Requires the RFLIB base package to be installed in the target org and the running user to be assigned the rflib_Ops_Center_Access permission set (or have equivalent read access to rflib_Application_Event__c).
|
|
12
|
+
For installation instructions, visit: https://github.com/j-fischer/rflib
|
|
13
|
+
|
|
14
|
+
# flags.target-org.summary
|
|
15
|
+
|
|
16
|
+
Username or alias of the target org.
|
|
17
|
+
|
|
18
|
+
# flags.target-org.description
|
|
19
|
+
|
|
20
|
+
The username or alias of the Salesforce org containing the RFLIB base package.
|
|
21
|
+
|
|
22
|
+
# flags.event-name.summary
|
|
23
|
+
|
|
24
|
+
Filter by event name. Use % as a wildcard.
|
|
25
|
+
|
|
26
|
+
# flags.event-name.description
|
|
27
|
+
|
|
28
|
+
Filter Application Events by their Event_Name__c field. Use the % character as a wildcard for partial matches, for example "order-%".
|
|
29
|
+
|
|
30
|
+
# flags.start-date.summary
|
|
31
|
+
|
|
32
|
+
Filter events on or after this ISO 8601 date.
|
|
33
|
+
|
|
34
|
+
# flags.start-date.description
|
|
35
|
+
|
|
36
|
+
Only return events where Occurred_On__c is on or after this date. Must be in ISO 8601 format, e.g. 2024-01-01T00:00:00Z.
|
|
37
|
+
|
|
38
|
+
# flags.end-date.summary
|
|
39
|
+
|
|
40
|
+
Filter events on or before this ISO 8601 date.
|
|
41
|
+
|
|
42
|
+
# flags.end-date.description
|
|
43
|
+
|
|
44
|
+
Only return events where Occurred_On__c is on or before this date. Must be in ISO 8601 format, e.g. 2024-12-31T23:59:59Z.
|
|
45
|
+
|
|
46
|
+
# flags.related-record-id.summary
|
|
47
|
+
|
|
48
|
+
Filter by Related_Record_ID__c (exact match).
|
|
49
|
+
|
|
50
|
+
# flags.related-record-id.description
|
|
51
|
+
|
|
52
|
+
Only return events associated with this specific record ID.
|
|
53
|
+
|
|
54
|
+
# flags.record-limit.summary
|
|
55
|
+
|
|
56
|
+
Maximum number of records to return (default 200, max 2000).
|
|
57
|
+
|
|
58
|
+
# flags.record-limit.description
|
|
59
|
+
|
|
60
|
+
Controls how many Application Event records are returned. Defaults to 200. Maximum allowed value is 2000.
|
|
61
|
+
|
|
62
|
+
# examples
|
|
63
|
+
|
|
64
|
+
- Get all application events from the default org:
|
|
65
|
+
|
|
66
|
+
$ sf rflib debug applicationevents get --target-org myOrg
|
|
67
|
+
|
|
68
|
+
- Get events filtered by name and date range:
|
|
69
|
+
|
|
70
|
+
$ sf rflib debug applicationevents get --target-org myOrg --event-name "order-%" --start-date 2024-01-01T00:00:00Z
|
|
71
|
+
|
|
72
|
+
- Get events related to a specific record with a custom limit:
|
|
73
|
+
|
|
74
|
+
$ sf rflib debug applicationevents get --target-org myOrg --related-record-id 0017000000XXXXXX --record-limit 50
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# summary
|
|
2
|
+
|
|
3
|
+
Query RFLIB log archives from a Salesforce org.
|
|
4
|
+
|
|
5
|
+
# description
|
|
6
|
+
|
|
7
|
+
Retrieves rflib_Logs_Archive__b records from the target org's big object store via the Salesforce REST API.
|
|
8
|
+
Each log record contains log level, context, request ID, and full log messages in the format: [timestamp]|[LEVEL]|[TRACE_ID]|[CONTEXT]|[MESSAGE].
|
|
9
|
+
|
|
10
|
+
Requires the RFLIB base package to be installed in the target org and the running user to be assigned the rflib_Ops_Center_Access permission set (or have equivalent read access to rflib_Logs_Archive__b).
|
|
11
|
+
For installation instructions, visit: https://github.com/j-fischer/rflib
|
|
12
|
+
|
|
13
|
+
# flags.target-org.summary
|
|
14
|
+
|
|
15
|
+
Username or alias of the target org.
|
|
16
|
+
|
|
17
|
+
# flags.target-org.description
|
|
18
|
+
|
|
19
|
+
The username or alias of the Salesforce org containing the RFLIB base package.
|
|
20
|
+
|
|
21
|
+
# flags.start-date.summary
|
|
22
|
+
|
|
23
|
+
Start of the date range in ISO 8601 format. Defaults to 24 hours ago.
|
|
24
|
+
|
|
25
|
+
# flags.start-date.description
|
|
26
|
+
|
|
27
|
+
Only return log archives created on or after this date. Must be in ISO 8601 format, e.g. 2024-01-01T00:00:00Z. Defaults to 24 hours ago if omitted.
|
|
28
|
+
|
|
29
|
+
# flags.end-date.summary
|
|
30
|
+
|
|
31
|
+
End of the date range in ISO 8601 format. Defaults to now.
|
|
32
|
+
|
|
33
|
+
# flags.end-date.description
|
|
34
|
+
|
|
35
|
+
Only return log archives created on or before this date. Must be in ISO 8601 format, e.g. 2024-12-31T23:59:59Z. Defaults to the current time if omitted.
|
|
36
|
+
|
|
37
|
+
# examples
|
|
38
|
+
|
|
39
|
+
- Get log archives from the last 24 hours:
|
|
40
|
+
|
|
41
|
+
$ sf rflib debug logarchives get --target-org myOrg
|
|
42
|
+
|
|
43
|
+
- Get log archives for a specific date range:
|
|
44
|
+
|
|
45
|
+
$ sf rflib debug logarchives get --target-org myOrg --start-date 2024-01-01T00:00:00Z --end-date 2024-01-02T00:00:00Z
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# summary
|
|
2
|
+
|
|
3
|
+
Read all RFLIB Logger Settings from a Salesforce org.
|
|
4
|
+
|
|
5
|
+
# description
|
|
6
|
+
|
|
7
|
+
Retrieves all rflib_Logger_Settings__c hierarchy custom setting records from the target org via the Salesforce REST API.
|
|
8
|
+
Returns settings across org-wide defaults, profile overrides, and user overrides, along with embedded best-practice recommendations.
|
|
9
|
+
|
|
10
|
+
Use "sf rflib debug loggersettings update" to apply changes.
|
|
11
|
+
|
|
12
|
+
Requires the RFLIB base package to be installed in the target org and the running user to be assigned the rflib_Ops_Center_Access permission set (or have equivalent read access to rflib_Logger_Settings__c).
|
|
13
|
+
For installation instructions, visit: https://github.com/j-fischer/rflib
|
|
14
|
+
|
|
15
|
+
# flags.target-org.summary
|
|
16
|
+
|
|
17
|
+
Username or alias of the target org.
|
|
18
|
+
|
|
19
|
+
# flags.target-org.description
|
|
20
|
+
|
|
21
|
+
The username or alias of the Salesforce org containing the RFLIB base package.
|
|
22
|
+
|
|
23
|
+
# examples
|
|
24
|
+
|
|
25
|
+
- Read all logger settings from the target org:
|
|
26
|
+
|
|
27
|
+
$ sf rflib debug loggersettings get --target-org myOrg
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# summary
|
|
2
|
+
|
|
3
|
+
Create or update an RFLIB Logger Setting in a Salesforce org.
|
|
4
|
+
|
|
5
|
+
# description
|
|
6
|
+
|
|
7
|
+
Creates or updates a single field on an rflib_Logger_Settings__c record in the target org via the Salesforce REST API.
|
|
8
|
+
Validates field values and warns about best-practice violations.
|
|
9
|
+
|
|
10
|
+
To update an existing record, provide --record-id. To create a new record, provide --setup-owner-id with an org ID (00D...), profile ID (00E...), or user ID.
|
|
11
|
+
|
|
12
|
+
Requires the RFLIB base package to be installed in the target org and the running user to be assigned the rflib_Ops_Center_Access permission set (or have equivalent update access to rflib_Logger_Settings__c).
|
|
13
|
+
For installation instructions, visit: https://github.com/j-fischer/rflib
|
|
14
|
+
|
|
15
|
+
# flags.target-org.summary
|
|
16
|
+
|
|
17
|
+
Username or alias of the target org.
|
|
18
|
+
|
|
19
|
+
# flags.target-org.description
|
|
20
|
+
|
|
21
|
+
The username or alias of the Salesforce org containing the RFLIB base package.
|
|
22
|
+
|
|
23
|
+
# flags.field-name.summary
|
|
24
|
+
|
|
25
|
+
API name of the rflib_Logger_Settings__c field to update.
|
|
26
|
+
|
|
27
|
+
# flags.field-name.description
|
|
28
|
+
|
|
29
|
+
The API name of the field to create or update, e.g. Log_Event_Reporting_Level__c. For log level fields, valid values are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, NONE.
|
|
30
|
+
|
|
31
|
+
# flags.field-value.summary
|
|
32
|
+
|
|
33
|
+
New value for the specified field.
|
|
34
|
+
|
|
35
|
+
# flags.field-value.description
|
|
36
|
+
|
|
37
|
+
The new value to set on the specified field. For log level fields, valid values are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, NONE.
|
|
38
|
+
|
|
39
|
+
# flags.record-id.summary
|
|
40
|
+
|
|
41
|
+
ID of an existing rflib_Logger_Settings__c record to update.
|
|
42
|
+
|
|
43
|
+
# flags.record-id.description
|
|
44
|
+
|
|
45
|
+
The Salesforce record ID of an existing rflib_Logger_Settings__c record to update. Omit this flag to create a new record (requires --setup-owner-id).
|
|
46
|
+
|
|
47
|
+
# flags.setup-owner-id.summary
|
|
48
|
+
|
|
49
|
+
Setup owner ID for creating a new Logger Setting record.
|
|
50
|
+
|
|
51
|
+
# flags.setup-owner-id.description
|
|
52
|
+
|
|
53
|
+
Required when creating a new Logger Setting record. Accepts an org ID (00D...), profile ID (00E...), or user ID. Read existing record IDs using "sf rflib debug loggersettings get".
|
|
54
|
+
|
|
55
|
+
# examples
|
|
56
|
+
|
|
57
|
+
- Update the Log_Event_Reporting_Level__c field on an existing record:
|
|
58
|
+
|
|
59
|
+
$ sf rflib debug loggersettings update --target-org myOrg --record-id a0A000000001234 --field-name Log_Event_Reporting_Level__c --field-value WARN
|
|
60
|
+
|
|
61
|
+
- Create a new org-wide Logger Setting:
|
|
62
|
+
|
|
63
|
+
$ sf rflib debug loggersettings update --target-org myOrg --setup-owner-id 00D000000000001 --field-name Log_Event_Reporting_Level__c --field-value WARN
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# summary
|
|
2
|
+
|
|
3
|
+
Check Salesforce permissions for a user in the target org.
|
|
4
|
+
|
|
5
|
+
# description
|
|
6
|
+
|
|
7
|
+
Retrieves FLS (Field-Level Security), OLS (Object-Level Security), and Apex class/page permissions for a specific user
|
|
8
|
+
via the Salesforce REST API. Permissions are aggregated across the user's profile, permission sets, and permission set groups.
|
|
9
|
+
|
|
10
|
+
Use --sobject-type to narrow FLS or OLS results to a specific SObject.
|
|
11
|
+
|
|
12
|
+
Requires the RFLIB base package to be installed in the target org and the running user to be assigned the rflib_Ops_Center_Access permission set (or have equivalent read access to the relevant permission objects).
|
|
13
|
+
For installation instructions, visit: https://github.com/j-fischer/rflib
|
|
14
|
+
|
|
15
|
+
# flags.target-org.summary
|
|
16
|
+
|
|
17
|
+
Username or alias of the target org.
|
|
18
|
+
|
|
19
|
+
# flags.target-org.description
|
|
20
|
+
|
|
21
|
+
The username or alias of the Salesforce org containing the RFLIB base package.
|
|
22
|
+
|
|
23
|
+
# flags.user-id.summary
|
|
24
|
+
|
|
25
|
+
Salesforce User ID (15 or 18 character) to check permissions for.
|
|
26
|
+
|
|
27
|
+
# flags.user-id.description
|
|
28
|
+
|
|
29
|
+
The Salesforce User ID (15 or 18 characters) of the user whose permissions should be retrieved.
|
|
30
|
+
|
|
31
|
+
# flags.permission-type.summary
|
|
32
|
+
|
|
33
|
+
Type of permissions to retrieve: FLS, OLS, APEX, or ALL.
|
|
34
|
+
|
|
35
|
+
# flags.permission-type.description
|
|
36
|
+
|
|
37
|
+
Controls which permission types are returned:
|
|
38
|
+
- FLS: Field-Level Security only
|
|
39
|
+
- OLS: Object-Level Security only
|
|
40
|
+
- APEX: Apex class and page access only
|
|
41
|
+
- ALL: All three permission types
|
|
42
|
+
|
|
43
|
+
# flags.sobject-type.summary
|
|
44
|
+
|
|
45
|
+
Optional SObject API name to filter FLS or OLS results.
|
|
46
|
+
|
|
47
|
+
# flags.sobject-type.description
|
|
48
|
+
|
|
49
|
+
Filters Field-Level Security or Object-Level Security results to a specific SObject type, e.g. Account, Contact, Opportunity.
|
|
50
|
+
|
|
51
|
+
# examples
|
|
52
|
+
|
|
53
|
+
- Check all permissions for a user:
|
|
54
|
+
|
|
55
|
+
$ sf rflib debug userpermissions get --target-org myOrg --user-id 0057000000XXXXXX --permission-type ALL
|
|
56
|
+
|
|
57
|
+
- Check FLS for a specific object:
|
|
58
|
+
|
|
59
|
+
$ sf rflib debug userpermissions get --target-org myOrg --user-id 0057000000XXXXXX --permission-type FLS --sobject-type Account
|
|
60
|
+
|
|
61
|
+
- Check Apex access:
|
|
62
|
+
|
|
63
|
+
$ sf rflib debug userpermissions get --target-org myOrg --user-id 0057000000XXXXXX --permission-type APEX
|