sf-plugin-permission-sets 0.0.0-dev.16 → 0.0.0-dev.18
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 +8 -6
- package/lib/adapters/connection-org-client.d.ts +11 -0
- package/lib/adapters/connection-org-client.js +35 -0
- package/lib/adapters/connection-org-client.js.map +1 -0
- package/lib/commands/ps/validate.js +3 -1
- package/lib/commands/ps/validate.js.map +1 -1
- package/lib/core/model.d.ts +5 -0
- package/lib/core/normalize.js +2 -2
- package/lib/core/normalize.js.map +1 -1
- package/lib/core/report.js +3 -4
- package/lib/core/report.js.map +1 -1
- package/lib/core/resolve.d.ts +12 -10
- package/lib/core/resolve.js +34 -60
- package/lib/core/resolve.js.map +1 -1
- package/lib/services/org-client.d.ts +11 -0
- package/lib/services/org-client.js +2 -0
- package/lib/services/org-client.js.map +1 -0
- package/lib/services/validate.d.ts +5 -3
- package/lib/services/validate.js +22 -10
- package/lib/services/validate.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -164,7 +164,7 @@ Every run checks the files first. `check` runs the offline checks with no org, a
|
|
|
164
164
|
| Situation | Checked by | Severity | Result |
|
|
165
165
|
| --- | --- | :---: | --- |
|
|
166
166
|
| Same username key appears twice in one file | `check` (offline) | ❌ error | Rejected, the intent is ambiguous |
|
|
167
|
-
| Same target listed twice for a user
|
|
167
|
+
| Same target listed twice for a user | `check` (offline) | ⚠️ warning | Deduped |
|
|
168
168
|
| A user with no scopes, or an empty list | `check` (offline) | ⚠️ warning | Ignored as a no-op |
|
|
169
169
|
| Same user in two files with different targets | `check` (offline) | ✅ ok | Merged into one model, the point of slicing |
|
|
170
170
|
| Declared user, permission set, group, or license missing or not unique | `validate` (online) | ❌ error | Run fails before any change |
|
|
@@ -343,11 +343,12 @@ The `next` tag is selected whenever the version contains a hyphen, not by GitHub
|
|
|
343
343
|
|
|
344
344
|
## Architecture
|
|
345
345
|
|
|
346
|
-
The plugin is layered so every command reuses the same core. Commands stay thin, services hold the orchestration,
|
|
346
|
+
The plugin is layered so every command reuses the same core. Commands stay thin, services hold the orchestration, core holds the reusable primitives, and a thin adapter layer isolates the Salesforce SDK.
|
|
347
347
|
|
|
348
|
-
- **Commands** (`src/commands/ps/`): oclif only.
|
|
349
|
-
- **Services** (`src/services/`): one per command (`check` today, then `
|
|
350
|
-
- **Core** (`src/core/`): the reusable building blocks.
|
|
348
|
+
- **Commands** (`src/commands/ps/`): oclif only. They parse flags, construct the service (wiring in the org adapter when the command needs one), render output, and set the exit code.
|
|
349
|
+
- **Services** (`src/services/`): one per command (`check` and `validate` today, then `plan`, `apply`, `export`). Each is a class built from its dependencies and inputs, with a parameterless `run()` that turns the core into a command's behavior. A service also declares the ports it needs from the outside, like the `OrgClient` interface its adapter implements.
|
|
350
|
+
- **Core** (`src/core/`): the reusable building blocks. Pure, with no `@salesforce/*` imports, so every piece is unit-testable on its own.
|
|
351
|
+
- **Adapters** (`src/adapters/`): the boundary to the outside world. `ConnectionOrgClient` implements the `OrgClient` port (declared in services) with a Salesforce `Connection`, and owns all the SOQL and SObject detail. Services depend on the port, not the SDK, so they test against a fake and stay free of connection detail.
|
|
351
352
|
|
|
352
353
|
| Core module | Responsibility |
|
|
353
354
|
| --- | --- |
|
|
@@ -356,9 +357,10 @@ The plugin is layered so every command reuses the same core. Commands stay thin,
|
|
|
356
357
|
| `parse` | File text to an object, with YAML and duplicate-key errors. |
|
|
357
358
|
| `normalize` | A validated file to canonical `(assignee, kind, target)` tuples, plus structural findings. |
|
|
358
359
|
| `load` | Expand globs, run parse then validate then normalize per file, and merge by union. |
|
|
360
|
+
| `resolve` | Pure rules that turn declared references and the org's answers into findings. No SOQL: the adapter owns that. |
|
|
359
361
|
| `report` | Format and count findings. |
|
|
360
362
|
|
|
361
|
-
Commands are slices of one pipeline. `check` runs the offline **load** stage only.
|
|
363
|
+
Commands are slices of one pipeline. `check` runs the offline **load** stage only. `validate` adds **resolve**: it looks the declared references up through the `OrgClient` port (the adapter builds the SOQL) and evaluates the org's answers with resolve's pure rules. The remaining online commands layer **fetch** (current org state), **diff** (desired vs actual), and **apply** (DML) on top, reusing load, resolve, report, model, and schema unchanged.
|
|
362
364
|
|
|
363
365
|
## License
|
|
364
366
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Connection } from '@salesforce/core';
|
|
2
|
+
import { Kind, OrgUser } from '../core/model.js';
|
|
3
|
+
import { OrgClient } from '../services/org-client.js';
|
|
4
|
+
/** Adapter backing OrgClient with a Salesforce Connection. autoFetchQuery pages past 2000 rows. */
|
|
5
|
+
export declare class ConnectionOrgClient implements OrgClient {
|
|
6
|
+
private readonly connection;
|
|
7
|
+
constructor(connection: Connection);
|
|
8
|
+
findUsers(usernames: string[]): Promise<OrgUser[]>;
|
|
9
|
+
findTargets(kind: Kind, names: string[]): Promise<string[]>;
|
|
10
|
+
private query;
|
|
11
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** SObject + naming field per kind. The Salesforce schema knowledge lives here, not in core. */
|
|
2
|
+
const targetObjects = {
|
|
3
|
+
permissionSet: { sobject: 'PermissionSet', field: 'Name' },
|
|
4
|
+
permissionSetGroup: { sobject: 'PermissionSetGroup', field: 'DeveloperName' },
|
|
5
|
+
permissionSetLicense: { sobject: 'PermissionSetLicense', field: 'DeveloperName' },
|
|
6
|
+
};
|
|
7
|
+
/** Escape a value for safe inclusion in a SOQL string literal. */
|
|
8
|
+
function soqlLiteral(value) {
|
|
9
|
+
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
10
|
+
}
|
|
11
|
+
/** Build a comma-separated, quoted IN list from the values. */
|
|
12
|
+
function inList(values) {
|
|
13
|
+
return values.map((value) => `'${soqlLiteral(value)}'`).join(', ');
|
|
14
|
+
}
|
|
15
|
+
/** Adapter backing OrgClient with a Salesforce Connection. autoFetchQuery pages past 2000 rows. */
|
|
16
|
+
export class ConnectionOrgClient {
|
|
17
|
+
connection;
|
|
18
|
+
constructor(connection) {
|
|
19
|
+
this.connection = connection;
|
|
20
|
+
}
|
|
21
|
+
async findUsers(usernames) {
|
|
22
|
+
const records = await this.query(`SELECT Username, IsActive FROM User WHERE Username IN (${inList(usernames)})`);
|
|
23
|
+
return records.map((record) => ({ username: record.Username, isActive: record.IsActive }));
|
|
24
|
+
}
|
|
25
|
+
async findTargets(kind, names) {
|
|
26
|
+
const { sobject, field } = targetObjects[kind];
|
|
27
|
+
const records = await this.query(`SELECT ${field} FROM ${sobject} WHERE ${field} IN (${inList(names)})`);
|
|
28
|
+
return records.map((record) => record[field]);
|
|
29
|
+
}
|
|
30
|
+
async query(soql) {
|
|
31
|
+
const result = await this.connection.autoFetchQuery(soql);
|
|
32
|
+
return result.records;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=connection-org-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-org-client.js","sourceRoot":"","sources":["../../src/adapters/connection-org-client.ts"],"names":[],"mappings":"AAMA,gGAAgG;AAChG,MAAM,aAAa,GAA+B;IAC9C,aAAa,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE;IAC1D,kBAAkB,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,eAAe,EAAE;IAC7E,oBAAoB,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,KAAK,EAAE,eAAe,EAAE;CACpF,CAAC;AAEF,kEAAkE;AAClE,SAAS,WAAW,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,+DAA+D;AAC/D,SAAS,MAAM,CAAC,MAAgB;IAC5B,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvE,CAAC;AAED,mGAAmG;AACnG,MAAM,OAAO,mBAAmB;IACQ;IAApC,YAAoC,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAEvD,KAAK,CAAC,SAAS,CAAC,SAAmB;QACtC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAC5B,0DAA0D,MAAM,CAAC,SAAS,CAAC,GAAG,CACjF,CAAC;QAEF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC/F,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,IAAU,EAAE,KAAe;QAChD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAC5B,UAAU,KAAK,SAAS,OAAO,UAAU,KAAK,QAAQ,MAAM,CAAC,KAAK,CAAC,GAAG,CACzE,CAAC;QAEF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAClD,CAAC;IAEO,KAAK,CAAC,KAAK,CAAI,IAAY;QAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC,OAAyB,CAAC;IAC5C,CAAC;CACJ"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
2
2
|
import { Messages } from '@salesforce/core';
|
|
3
|
+
import { ConnectionOrgClient } from '../../adapters/connection-org-client.js';
|
|
3
4
|
import { ValidateService } from '../../services/validate.js';
|
|
4
5
|
import { formatFindings } from '../../core/report.js';
|
|
5
6
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
@@ -21,7 +22,8 @@ export default class Validate extends SfCommand {
|
|
|
21
22
|
async run() {
|
|
22
23
|
const { flags } = await this.parse(Validate);
|
|
23
24
|
const connection = flags['target-org'].getConnection(flags['api-version']);
|
|
24
|
-
const
|
|
25
|
+
const orgClient = new ConnectionOrgClient(connection);
|
|
26
|
+
const service = new ValidateService(orgClient, flags.file);
|
|
25
27
|
const result = await service.run();
|
|
26
28
|
for (const line of formatFindings(result.findings)) {
|
|
27
29
|
this.log(line);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/commands/ps/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,2BAA2B,EAAE,aAAa,CAAC,CAAC;AASnF,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,SAA2B;IACtD,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,MAAM,CAAU,KAAK,GAAG;QAC3B,YAAY,EAAE,KAAK,CAAC,WAAW,EAAE;QACjC,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE;QACpC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC;YACf,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,oBAAoB,CAAC;YAClD,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,IAAI;SACjB,CAAC;KACL,CAAC;IAEK,KAAK,CAAC,GAAG;QACZ,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE7C,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/commands/ps/validate.ts"],"names":[],"mappings":"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"}
|
package/lib/core/model.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ export type DesiredAssignment = {
|
|
|
4
4
|
kind: Kind;
|
|
5
5
|
target: string;
|
|
6
6
|
};
|
|
7
|
+
/** A user as it exists in the org, in domain terms (no SObject field names). */
|
|
8
|
+
export type OrgUser = {
|
|
9
|
+
username: string;
|
|
10
|
+
isActive: boolean;
|
|
11
|
+
};
|
|
7
12
|
export type FindingLevel = 'error' | 'warning';
|
|
8
13
|
export type Finding = {
|
|
9
14
|
level: FindingLevel;
|
package/lib/core/normalize.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const kindKeys = [
|
|
2
2
|
['permissionSet', 'permissionSets'],
|
|
3
3
|
['permissionSetGroup', 'permissionSetGroups'],
|
|
4
4
|
['permissionSetLicense', 'permissionSetLicenses'],
|
|
@@ -12,7 +12,7 @@ export function normalize(data, file) {
|
|
|
12
12
|
const findings = [];
|
|
13
13
|
for (const [username, entry] of Object.entries(data.users)) {
|
|
14
14
|
let scopeCount = 0;
|
|
15
|
-
for (const [kind, key] of
|
|
15
|
+
for (const [kind, key] of kindKeys) {
|
|
16
16
|
const list = entry[key];
|
|
17
17
|
if (list === undefined)
|
|
18
18
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/core/normalize.ts"],"names":[],"mappings":"AAKA,MAAM,
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/core/normalize.ts"],"names":[],"mappings":"AAKA,MAAM,QAAQ,GAA4B;IACtC,CAAC,eAAe,EAAE,gBAAgB,CAAC;IACnC,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;IAC7C,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;CACpD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAe,EAAE,IAAY;IACnD,MAAM,WAAW,GAAwB,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,QAAQ,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAS;YACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,QAAQ,KAAK,GAAG,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvG,SAAS;YACb,CAAC;YAED,UAAU,IAAI,CAAC,CAAC;YAChB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnB,QAAQ,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,SAAS;wBAChB,IAAI,EAAE,YAAY;wBAClB,OAAO,EAAE,GAAG,QAAQ,KAAK,MAAM,0BAA0B,GAAG,EAAE;wBAC9D,IAAI;qBACP,CAAC,CAAC;oBACH,SAAS;gBACb,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACjB,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3D,CAAC;QACL,CAAC;QAED,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,QAAQ,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9G,CAAC;IACL,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AACrC,CAAC"}
|
package/lib/core/report.js
CHANGED
|
@@ -7,9 +7,8 @@ export function formatFindings(findings) {
|
|
|
7
7
|
}
|
|
8
8
|
/** Count findings by level. */
|
|
9
9
|
export function countFindings(findings) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
10
|
+
const errors = findings.filter((finding) => finding.level === 'error');
|
|
11
|
+
const warnings = findings.filter((finding) => finding.level === 'warning');
|
|
12
|
+
return { errors: errors.length, warnings: warnings.length };
|
|
14
13
|
}
|
|
15
14
|
//# sourceMappingURL=report.js.map
|
package/lib/core/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/core/report.ts"],"names":[],"mappings":"AAEA,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,QAAmB;IAC9C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,OAAO,GAAG,OAAO,CAAC,KAAK,KAAK,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,aAAa,CAAC,QAAmB;IAC7C,
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/core/report.ts"],"names":[],"mappings":"AAEA,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,QAAmB;IAC9C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,OAAO,GAAG,OAAO,CAAC,KAAK,KAAK,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,aAAa,CAAC,QAAmB;IAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IAE3E,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;AAChE,CAAC"}
|
package/lib/core/resolve.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { DesiredAssignment, Finding } from './model.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { DesiredAssignment, Finding, Kind, OrgUser } from './model.js';
|
|
2
|
+
export declare const kinds: Kind[];
|
|
3
|
+
/** The distinct usernames assigned across all assignments. */
|
|
4
|
+
export declare function distinctAssignees(assignments: DesiredAssignment[]): string[];
|
|
5
|
+
/** The distinct targets of one kind across all assignments. */
|
|
6
|
+
export declare function distinctTargets(assignments: DesiredAssignment[], kind: Kind): string[];
|
|
7
|
+
/** Every declared user must exist in the org and be active. */
|
|
8
|
+
export declare function evaluateUsers(declared: string[], found: OrgUser[]): Finding[];
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* Every declared target of one kind must resolve to exactly one record in the
|
|
11
|
+
* org. `found` is the list of matching identifiers the org returned; matching is
|
|
12
|
+
* case-insensitive, mirroring how the org compares them.
|
|
11
13
|
*/
|
|
12
|
-
export declare function
|
|
14
|
+
export declare function evaluateTargets(kind: Kind, declared: string[], found: string[]): Finding[];
|
package/lib/core/resolve.js
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
/**
|
|
2
|
-
const
|
|
3
|
-
permissionSet:
|
|
4
|
-
permissionSetGroup:
|
|
5
|
-
permissionSetLicense:
|
|
1
|
+
/** Human label per kind, used in findings. Domain wording, not SObject names. */
|
|
2
|
+
const kindLabels = {
|
|
3
|
+
permissionSet: 'permission set',
|
|
4
|
+
permissionSetGroup: 'permission set group',
|
|
5
|
+
permissionSetLicense: 'permission set license',
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
function soqlLiteral(value) {
|
|
9
|
-
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
10
|
-
}
|
|
11
|
-
/** Build a comma-separated, quoted IN list from the values. */
|
|
12
|
-
function inList(values) {
|
|
13
|
-
return values.map((value) => `'${soqlLiteral(value)}'`).join(', ');
|
|
14
|
-
}
|
|
7
|
+
export const kinds = Object.keys(kindLabels);
|
|
15
8
|
function distinct(values) {
|
|
16
9
|
return [...new Set(values)];
|
|
17
10
|
}
|
|
18
|
-
/**
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
for (const row of rows) {
|
|
22
|
-
const value = (row[field] ?? '').toLowerCase();
|
|
23
|
-
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
24
|
-
}
|
|
25
|
-
return counts;
|
|
11
|
+
/** The distinct usernames assigned across all assignments. */
|
|
12
|
+
export function distinctAssignees(assignments) {
|
|
13
|
+
return distinct(assignments.map((assignment) => assignment.assignee));
|
|
26
14
|
}
|
|
27
|
-
/**
|
|
28
|
-
function
|
|
15
|
+
/** The distinct targets of one kind across all assignments. */
|
|
16
|
+
export function distinctTargets(assignments, kind) {
|
|
17
|
+
return distinct(assignments.filter((assignment) => assignment.kind === kind).map((assignment) => assignment.target));
|
|
18
|
+
}
|
|
19
|
+
/** Every declared user must exist in the org and be active. */
|
|
20
|
+
export function evaluateUsers(declared, found) {
|
|
29
21
|
const byName = new Map();
|
|
30
|
-
for (const
|
|
31
|
-
byName.set(
|
|
22
|
+
for (const user of found) {
|
|
23
|
+
byName.set(user.username.toLowerCase(), user);
|
|
32
24
|
}
|
|
33
25
|
const findings = [];
|
|
34
|
-
for (const username of
|
|
35
|
-
const
|
|
36
|
-
if (!
|
|
26
|
+
for (const username of declared) {
|
|
27
|
+
const user = byName.get(username.toLowerCase());
|
|
28
|
+
if (!user) {
|
|
37
29
|
findings.push({ level: 'error', code: 'USER_NOT_FOUND', message: `${username}: user not found in org` });
|
|
38
30
|
}
|
|
39
|
-
else if (!
|
|
31
|
+
else if (!user.isActive) {
|
|
40
32
|
findings.push({ level: 'error', code: 'USER_INACTIVE', message: `${username}: user is inactive` });
|
|
41
33
|
}
|
|
42
34
|
}
|
|
43
35
|
return findings;
|
|
44
36
|
}
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Every declared target of one kind must resolve to exactly one record in the
|
|
39
|
+
* org. `found` is the list of matching identifiers the org returned; matching is
|
|
40
|
+
* case-insensitive, mirroring how the org compares them.
|
|
41
|
+
*/
|
|
42
|
+
export function evaluateTargets(kind, declared, found) {
|
|
43
|
+
const label = kindLabels[kind];
|
|
44
|
+
const counts = new Map();
|
|
45
|
+
for (const name of found) {
|
|
46
|
+
const key = name.toLowerCase();
|
|
47
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
48
|
+
}
|
|
49
49
|
const findings = [];
|
|
50
|
-
for (const target of
|
|
50
|
+
for (const target of declared) {
|
|
51
51
|
const count = counts.get(target.toLowerCase()) ?? 0;
|
|
52
52
|
if (count === 0) {
|
|
53
53
|
findings.push({
|
|
@@ -66,30 +66,4 @@ function evaluateTargets(kind, targets, rows) {
|
|
|
66
66
|
}
|
|
67
67
|
return findings;
|
|
68
68
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Plan the org queries needed to resolve these assignments, each paired with the
|
|
71
|
-
* pure evaluator for its rows. The online half of validate, kept free of any org
|
|
72
|
-
* connection: the service runs the SOQL and feeds the rows back to evaluate.
|
|
73
|
-
*/
|
|
74
|
-
export function planResolution(assignments) {
|
|
75
|
-
const steps = [];
|
|
76
|
-
const usernames = distinct(assignments.map((assignment) => assignment.assignee));
|
|
77
|
-
if (usernames.length > 0) {
|
|
78
|
-
steps.push({
|
|
79
|
-
soql: `SELECT Username, IsActive FROM User WHERE Username IN (${inList(usernames)})`,
|
|
80
|
-
evaluate: (rows) => evaluateUsers(usernames, rows),
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
for (const kind of Object.keys(KIND_RESOLUTION)) {
|
|
84
|
-
const { sobject, field } = KIND_RESOLUTION[kind];
|
|
85
|
-
const targets = distinct(assignments.filter((assignment) => assignment.kind === kind).map((assignment) => assignment.target));
|
|
86
|
-
if (targets.length > 0) {
|
|
87
|
-
steps.push({
|
|
88
|
-
soql: `SELECT ${field} FROM ${sobject} WHERE ${field} IN (${inList(targets)})`,
|
|
89
|
-
evaluate: (rows) => evaluateTargets(kind, targets, rows),
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return steps;
|
|
94
|
-
}
|
|
95
69
|
//# sourceMappingURL=resolve.js.map
|
package/lib/core/resolve.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/core/resolve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/core/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Kind, OrgUser } from '../core/model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Port: the org lookups a service needs, in domain terms. Declared here, by the
|
|
4
|
+
* consumer, so services depend on the abstraction and the adapter implements it.
|
|
5
|
+
*/
|
|
6
|
+
export interface OrgClient {
|
|
7
|
+
/** The users that exist in the org, among the given usernames. */
|
|
8
|
+
findUsers(usernames: string[]): Promise<OrgUser[]>;
|
|
9
|
+
/** The identifiers that exist in the org, among the given targets of one kind. */
|
|
10
|
+
findTargets(kind: Kind, names: string[]): Promise<string[]>;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org-client.js","sourceRoot":"","sources":["../../src/services/org-client.ts"],"names":[],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Connection } from '@salesforce/core';
|
|
2
1
|
import { DesiredAssignment, Finding } from '../core/model.js';
|
|
2
|
+
import { OrgClient } from './org-client.js';
|
|
3
3
|
export type ValidateResult = {
|
|
4
4
|
files: string[];
|
|
5
5
|
assignments: DesiredAssignment[];
|
|
@@ -10,8 +10,10 @@ export type ValidateResult = {
|
|
|
10
10
|
};
|
|
11
11
|
/** Online validate: run the offline load, then resolve every reference against the org. */
|
|
12
12
|
export declare class ValidateService {
|
|
13
|
-
private readonly
|
|
13
|
+
private readonly org;
|
|
14
14
|
private readonly files;
|
|
15
|
-
constructor(
|
|
15
|
+
constructor(org: OrgClient, files: string[]);
|
|
16
16
|
run(): Promise<ValidateResult>;
|
|
17
|
+
/** Look every reference up in the org (in parallel) and evaluate the results. */
|
|
18
|
+
private resolve;
|
|
17
19
|
}
|
package/lib/services/validate.js
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { loadFiles } from '../core/load.js';
|
|
2
2
|
import { countFindings } from '../core/report.js';
|
|
3
|
-
import {
|
|
3
|
+
import { kinds, distinctAssignees, distinctTargets, evaluateUsers, evaluateTargets } from '../core/resolve.js';
|
|
4
4
|
/** Online validate: run the offline load, then resolve every reference against the org. */
|
|
5
5
|
export class ValidateService {
|
|
6
|
-
|
|
6
|
+
org;
|
|
7
7
|
files;
|
|
8
|
-
constructor(
|
|
9
|
-
this.
|
|
8
|
+
constructor(org, files) {
|
|
9
|
+
this.org = org;
|
|
10
10
|
this.files = files;
|
|
11
11
|
}
|
|
12
12
|
async run() {
|
|
13
13
|
const loaded = await loadFiles(this.files);
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const result = await this.connection.autoFetchQuery(step.soql);
|
|
17
|
-
return step.evaluate(result.records);
|
|
18
|
-
}));
|
|
19
|
-
const findings = [...loaded.findings, ...resolved.flat()];
|
|
14
|
+
const online = await this.resolve(loaded.assignments);
|
|
15
|
+
const findings = [...loaded.findings, ...online];
|
|
20
16
|
const { errors, warnings } = countFindings(findings);
|
|
21
17
|
return {
|
|
22
18
|
files: loaded.files,
|
|
@@ -27,5 +23,21 @@ export class ValidateService {
|
|
|
27
23
|
failed: errors > 0,
|
|
28
24
|
};
|
|
29
25
|
}
|
|
26
|
+
/** Look every reference up in the org (in parallel) and evaluate the results. */
|
|
27
|
+
async resolve(assignments) {
|
|
28
|
+
const tasks = [];
|
|
29
|
+
const usernames = distinctAssignees(assignments);
|
|
30
|
+
if (usernames.length > 0) {
|
|
31
|
+
tasks.push(this.org.findUsers(usernames).then((found) => evaluateUsers(usernames, found)));
|
|
32
|
+
}
|
|
33
|
+
for (const kind of kinds) {
|
|
34
|
+
const targets = distinctTargets(assignments, kind);
|
|
35
|
+
if (targets.length > 0) {
|
|
36
|
+
tasks.push(this.org.findTargets(kind, targets).then((found) => evaluateTargets(kind, targets, found)));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const results = await Promise.all(tasks);
|
|
40
|
+
return results.flat();
|
|
41
|
+
}
|
|
30
42
|
}
|
|
31
43
|
//# sourceMappingURL=validate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/services/validate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/services/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAa/G,2FAA2F;AAC3F,MAAM,OAAO,eAAe;IACY;IAAiC;IAArE,YAAoC,GAAc,EAAmB,KAAe;QAAhD,QAAG,GAAH,GAAG,CAAW;QAAmB,UAAK,GAAL,KAAK,CAAU;IAAG,CAAC;IAEjF,KAAK,CAAC,GAAG;QACZ,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAEtD,MAAM,QAAQ,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,CAAC;QACjD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QAErD,OAAO;YACH,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ;YACR,MAAM;YACN,QAAQ;YACR,MAAM,EAAE,MAAM,GAAG,CAAC;SACrB,CAAC;IACN,CAAC;IAED,iFAAiF;IACzE,KAAK,CAAC,OAAO,CAAC,WAAgC;QAClD,MAAM,KAAK,GAA8B,EAAE,CAAC;QAE5C,MAAM,SAAS,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;QACjD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/F,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,eAAe,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACnD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3G,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;CACJ"}
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED