@knowttl/mg-axi 0.1.0
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 +1138 -0
- package/dist/api.js +953 -0
- package/dist/app-auth.js +47 -0
- package/dist/auth.js +59 -0
- package/dist/bin/mg-axi.js +7 -0
- package/dist/catalogue.js +2191 -0
- package/dist/cli.js +1314 -0
- package/dist/docs.js +559 -0
- package/dist/doctor.js +75 -0
- package/dist/entra-access-reviews.js +425 -0
- package/dist/entra-apps.js +374 -0
- package/dist/entra-audit-logs.js +381 -0
- package/dist/entra-auth-methods.js +296 -0
- package/dist/entra-ca-policy-update.js +353 -0
- package/dist/entra-certificate-auth.js +384 -0
- package/dist/entra-conditional-access.js +317 -0
- package/dist/entra-contacts.js +827 -0
- package/dist/entra-contracts.js +252 -0
- package/dist/entra-custom-security-attributes.js +464 -0
- package/dist/entra-data-policy-operations.js +274 -0
- package/dist/entra-delegated-admin.js +468 -0
- package/dist/entra-deleted-items.js +461 -0
- package/dist/entra-directory-objects.js +274 -0
- package/dist/entra-directory.js +411 -0
- package/dist/entra-domains.js +399 -0
- package/dist/entra-entitlement-management.js +645 -0
- package/dist/entra-federation-configurations.js +290 -0
- package/dist/entra-grants.js +272 -0
- package/dist/entra-group-lifecycle.js +349 -0
- package/dist/entra-group-member-add.js +278 -0
- package/dist/entra-groups.js +336 -0
- package/dist/entra-identity-providers.js +297 -0
- package/dist/entra-lifecycle-workflows.js +673 -0
- package/dist/entra-multi-tenant-organization.js +309 -0
- package/dist/entra-on-premises-synchronization.js +231 -0
- package/dist/entra-organization.js +398 -0
- package/dist/entra-risk-dismiss.js +191 -0
- package/dist/entra-risk.js +404 -0
- package/dist/entra-roles.js +310 -0
- package/dist/entra-subscriptions.js +289 -0
- package/dist/entra-tenant-information.js +152 -0
- package/dist/entra-terms-of-use.js +380 -0
- package/dist/entra-user-revoke-sessions.js +170 -0
- package/dist/entra-user-update.js +170 -0
- package/dist/entra-users.js +211 -0
- package/dist/graph-session.js +1034 -0
- package/dist/msal-app-provider.js +42 -0
- package/dist/msal-provider.js +82 -0
- package/dist/mutations.js +529 -0
- package/dist/profiles.js +139 -0
- package/dist/setup.js +116 -0
- package/dist/version.js +2 -0
- package/docs/coverage.md +1028 -0
- package/inventory/operations.json +4 -0
- package/inventory/schema.json +71 -0
- package/package.json +32 -0
- package/skills/mg-axi/SKILL.md +293 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
// EXT-02 lifecycle-workflows reads: the read-only workflow, workflow
|
|
3
|
+
// template, task definition and tenant-settings first part, the
|
|
4
|
+
// second-part top-level run and user/subject processing-result reads and
|
|
5
|
+
// the third-part task-report reads. See
|
|
6
|
+
// README.md for the supported commands and usage. Operation construction
|
|
7
|
+
// stays beside its command; the shared session owns URLs, credentials,
|
|
8
|
+
// paging, retries and error translation, and the SDK owns TOON rendering.
|
|
9
|
+
// This module only maps flags to session calls and projects rows for
|
|
10
|
+
// compact output.
|
|
11
|
+
//
|
|
12
|
+
// Reviewed against the v1.0 lifecycleworkflowscontainer-list-workflows,
|
|
13
|
+
// workflow-get, lifecycleworkflowscontainer-list-workflowtemplates,
|
|
14
|
+
// workflowtemplate-get, lifecycleworkflowscontainer-list-taskdefinitions,
|
|
15
|
+
// taskdefinition-get, lifecyclemanagementsettings-get, workflow-list-runs,
|
|
16
|
+
// run-get, workflow-list-userprocessingresults, userprocessingresult-get,
|
|
17
|
+
// run-list-subjectprocessingresults, subjectprocessingresult-get,
|
|
18
|
+
// workflow-list-taskreports and task-get operation documentation and the
|
|
19
|
+
// reviewed resource property sets for workflow, workflowTemplate,
|
|
20
|
+
// taskDefinition, lifecycleManagementSettings, run, userProcessingResult,
|
|
21
|
+
// subjectProcessingResult, taskReport and task, on 2026-10-06.
|
|
22
|
+
// Workflow list/show take D/A
|
|
23
|
+
// LifecycleWorkflows-Workflow.ReadBasic.All as the least privileged choice
|
|
24
|
+
// (LifecycleWorkflows-Workflow.Read.All or LifecycleWorkflows.Read.All for
|
|
25
|
+
// richer detail); template and top-level task-definition reads take D/A
|
|
26
|
+
// LifecycleWorkflows.Read.All; run, processing-result and task-report reads
|
|
27
|
+
// take D/A LifecycleWorkflows-Reports.Read.All as the least privileged
|
|
28
|
+
// choice (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All
|
|
29
|
+
// for richer detail). The taskReport-nested task and taskDefinition singles
|
|
30
|
+
// have no operation-level documentation page, so they share the reporting
|
|
31
|
+
// family's Reports.Read.All default; the workflow-tasks task-get page
|
|
32
|
+
// documents LifecycleWorkflows-Workflow.Read.All for its own route and the
|
|
33
|
+
// top-level taskdefinition-get page documents LifecycleWorkflows.Read.All
|
|
34
|
+
// for its own route, and both stay named as richer alternatives on denial.
|
|
35
|
+
// Delegated personal Microsoft accounts are not supported.
|
|
36
|
+
// Delegated callers additionally need Global Reader or Lifecycle Workflows
|
|
37
|
+
// Administrator. The run and user-processing lists document $filter (and
|
|
38
|
+
// $select), so --filter passes through as plain $filter with no $count or
|
|
39
|
+
// ConsistencyLevel contract ($search/$orderby/$expand stay unreviewed and
|
|
40
|
+
// $top/$skip stay server-side paging concerns); run and user-processing
|
|
41
|
+
// singles document $select only. The subject list documents $filter but not
|
|
42
|
+
// $select and the subject get documents $expand only, so subject rows
|
|
43
|
+
// arrive whole and are projected locally with --fields (the template-show
|
|
44
|
+
// pattern); the subject navigation stays out everywhere. Task reports
|
|
45
|
+
// aggregate one task's results within one workflow; the task and
|
|
46
|
+
// taskDefinition navigations return the related objects whole (task
|
|
47
|
+
// arguments and definition parameters ride along as reviewed fields) while
|
|
48
|
+
// taskProcessingResults, the subject link and every $expand stay out.
|
|
49
|
+
// The taskReports list documents $select/$filter (with $orderby/$expand
|
|
50
|
+
// unreviewed); the taskReport get and the taskReport-nested task and
|
|
51
|
+
// taskDefinition singles have no operation-level page (the task-get page
|
|
52
|
+
// covers the workflow-tasks route only and documents no query parameters
|
|
53
|
+
// there), so those three singles arrive whole and are projected locally
|
|
54
|
+
// with --fields (--select is refused before credentials). Processing results
|
|
55
|
+
// are personal data: user and subject defaults carry status and counts
|
|
56
|
+
// only, never the subject link. Deeper execution detail (run-nested and
|
|
57
|
+
// third-level processing results, reprocessed runs, subjects, workflow and
|
|
58
|
+
// template tasks, createdBy, lastModifiedBy, previewScope, executionScope,
|
|
59
|
+
// versions, insights, deleted items, custom task extensions) needs its own
|
|
60
|
+
// sub-read and stays out, as do the summary functions (bracketed start/end
|
|
61
|
+
// arguments outside the session-guard function-binding allowlist).
|
|
62
|
+
// Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra
|
|
63
|
+
// Suite (every governed user, not only administrators). No mutation (no
|
|
64
|
+
// workflow create/update/delete/activate/run, no settings update) and no
|
|
65
|
+
// beta.
|
|
66
|
+
// Workflows expose the configured joiner/mover/leaver automations. Only
|
|
67
|
+
// scalar properties carry a reviewed $select contract; tasks, runs,
|
|
68
|
+
// processing results, versions, scopes and createdBy/lastModifiedBy links
|
|
69
|
+
// are navigation properties and stay out (tasks ride expanded by default on
|
|
70
|
+
// the get and are dropped in local projection).
|
|
71
|
+
export const KNOWN_WORKFLOW_FIELDS = [
|
|
72
|
+
"id",
|
|
73
|
+
"category",
|
|
74
|
+
"createdDateTime",
|
|
75
|
+
"deletedDateTime",
|
|
76
|
+
"description",
|
|
77
|
+
"displayName",
|
|
78
|
+
"executionConditions",
|
|
79
|
+
"isEnabled",
|
|
80
|
+
"isSchedulingEnabled",
|
|
81
|
+
"lastModifiedDateTime",
|
|
82
|
+
"nextScheduleRunDateTime",
|
|
83
|
+
"version",
|
|
84
|
+
"quarantineDetails",
|
|
85
|
+
"settings",
|
|
86
|
+
];
|
|
87
|
+
// Workflow templates expose the pre-configured Entra scenarios new
|
|
88
|
+
// workflows are built from. The tasks relationship is expanded by default
|
|
89
|
+
// on the wire and is dropped here: template tasks belong to a later slice.
|
|
90
|
+
export const KNOWN_WORKFLOW_TEMPLATE_FIELDS = [
|
|
91
|
+
"id",
|
|
92
|
+
"category",
|
|
93
|
+
"description",
|
|
94
|
+
"displayName",
|
|
95
|
+
"executionConditions",
|
|
96
|
+
];
|
|
97
|
+
// Task definitions expose the built-in tasks workflows are constructed
|
|
98
|
+
// from. They carry no relationships.
|
|
99
|
+
export const KNOWN_TASK_DEFINITION_FIELDS = [
|
|
100
|
+
"id",
|
|
101
|
+
"category",
|
|
102
|
+
"continueOnError",
|
|
103
|
+
"description",
|
|
104
|
+
"displayName",
|
|
105
|
+
"parameters",
|
|
106
|
+
"version",
|
|
107
|
+
];
|
|
108
|
+
// Tenant settings expose the workflow schedule interval and the email and
|
|
109
|
+
// quarantine configuration. They carry no relationships; the update action
|
|
110
|
+
// belongs to no read slice.
|
|
111
|
+
export const KNOWN_LIFECYCLE_SETTINGS_FIELDS = [
|
|
112
|
+
"workflowScheduleIntervalInHours",
|
|
113
|
+
"emailSettings",
|
|
114
|
+
"quarantineConfiguration",
|
|
115
|
+
];
|
|
116
|
+
// Runs expose the execution instances of one workflow. activatedOnScope is
|
|
117
|
+
// a complex scope object (not a reviewed scalar) and the processing-result
|
|
118
|
+
// relationships need their own sub-reads, so neither is projected here.
|
|
119
|
+
// totalTasksCounts keeps the resource documentation's spelling.
|
|
120
|
+
export const KNOWN_RUN_FIELDS = [
|
|
121
|
+
"id",
|
|
122
|
+
"completedDateTime",
|
|
123
|
+
"failedTasksCount",
|
|
124
|
+
"failedUsersCount",
|
|
125
|
+
"lastUpdatedDateTime",
|
|
126
|
+
"processingStatus",
|
|
127
|
+
"scheduledDateTime",
|
|
128
|
+
"startedDateTime",
|
|
129
|
+
"successfulUsersCount",
|
|
130
|
+
"totalTasksCounts",
|
|
131
|
+
"totalUsersCount",
|
|
132
|
+
"totalUnprocessedTasksCount",
|
|
133
|
+
"workflowExecutionType",
|
|
134
|
+
];
|
|
135
|
+
// User processing results aggregate one user's workflow execution. The
|
|
136
|
+
// subject relationship carries the targeted user identity: it needs
|
|
137
|
+
// $expand, stays off the reviewed set, and is never requested.
|
|
138
|
+
export const KNOWN_USER_PROCESSING_FIELDS = [
|
|
139
|
+
"id",
|
|
140
|
+
"completedDateTime",
|
|
141
|
+
"failedTasksCount",
|
|
142
|
+
"processingStatus",
|
|
143
|
+
"scheduledDateTime",
|
|
144
|
+
"startedDateTime",
|
|
145
|
+
"totalTasksCount",
|
|
146
|
+
"totalUnprocessedTasksCount",
|
|
147
|
+
"workflowExecutionType",
|
|
148
|
+
"workflowVersion",
|
|
149
|
+
];
|
|
150
|
+
// Subject processing results mirror user results for non-user subjects.
|
|
151
|
+
// subjectType stays (it names the subject kind, not the subject); the
|
|
152
|
+
// workflowSubject-typed subject navigation stays out with $expand.
|
|
153
|
+
export const KNOWN_SUBJECT_PROCESSING_FIELDS = [
|
|
154
|
+
"id",
|
|
155
|
+
"completedDateTime",
|
|
156
|
+
"failedTasksCount",
|
|
157
|
+
"processingStatus",
|
|
158
|
+
"scheduledDateTime",
|
|
159
|
+
"startedDateTime",
|
|
160
|
+
"subjectType",
|
|
161
|
+
"totalTasksCount",
|
|
162
|
+
"totalUnprocessedTasksCount",
|
|
163
|
+
"workflowExecutionType",
|
|
164
|
+
"workflowVersion",
|
|
165
|
+
];
|
|
166
|
+
// Task reports aggregate one workflow task's processing results within one
|
|
167
|
+
// run. runId names the owning run; the task, taskDefinition and
|
|
168
|
+
// taskProcessingResults relationships need their own sub-reads ($expand
|
|
169
|
+
// stays out), so only scalar report properties are reviewed here.
|
|
170
|
+
export const KNOWN_TASK_REPORT_FIELDS = [
|
|
171
|
+
"id",
|
|
172
|
+
"runId",
|
|
173
|
+
"processingStatus",
|
|
174
|
+
"successfulUsersCount",
|
|
175
|
+
"failedUsersCount",
|
|
176
|
+
"unprocessedUsersCount",
|
|
177
|
+
"totalUsersCount",
|
|
178
|
+
"startedDateTime",
|
|
179
|
+
"completedDateTime",
|
|
180
|
+
"lastUpdatedDateTime",
|
|
181
|
+
];
|
|
182
|
+
// Tasks are the actions a workflow executes when triggered. arguments
|
|
183
|
+
// carries the configured key/value pairs (for example group IDs); the
|
|
184
|
+
// taskProcessingResults relationship needs its own sub-read, so it stays
|
|
185
|
+
// out with $expand.
|
|
186
|
+
export const KNOWN_TASK_FIELDS = [
|
|
187
|
+
"id",
|
|
188
|
+
"arguments",
|
|
189
|
+
"category",
|
|
190
|
+
"continueOnError",
|
|
191
|
+
"description",
|
|
192
|
+
"displayName",
|
|
193
|
+
"executionSequence",
|
|
194
|
+
"isEnabled",
|
|
195
|
+
"taskDefinitionId",
|
|
196
|
+
];
|
|
197
|
+
const WORKFLOW_KNOWN = new Set(KNOWN_WORKFLOW_FIELDS);
|
|
198
|
+
const WORKFLOW_TEMPLATE_KNOWN = new Set(KNOWN_WORKFLOW_TEMPLATE_FIELDS);
|
|
199
|
+
const TASK_DEFINITION_KNOWN = new Set(KNOWN_TASK_DEFINITION_FIELDS);
|
|
200
|
+
const LIFECYCLE_SETTINGS_KNOWN = new Set(KNOWN_LIFECYCLE_SETTINGS_FIELDS);
|
|
201
|
+
const RUN_KNOWN = new Set(KNOWN_RUN_FIELDS);
|
|
202
|
+
const USER_PROCESSING_KNOWN = new Set(KNOWN_USER_PROCESSING_FIELDS);
|
|
203
|
+
const SUBJECT_PROCESSING_KNOWN = new Set(KNOWN_SUBJECT_PROCESSING_FIELDS);
|
|
204
|
+
const TASK_REPORT_KNOWN = new Set(KNOWN_TASK_REPORT_FIELDS);
|
|
205
|
+
const TASK_KNOWN = new Set(KNOWN_TASK_FIELDS);
|
|
206
|
+
// Compact rows: identifiers plus category and enablement state.
|
|
207
|
+
const DEFAULT_WORKFLOW_LIST_SELECT = ["id", "displayName", "category", "isEnabled", "isSchedulingEnabled"];
|
|
208
|
+
const DEFAULT_WORKFLOW_SHOW_SELECT = [...KNOWN_WORKFLOW_FIELDS];
|
|
209
|
+
const DEFAULT_WORKFLOW_TEMPLATE_LIST_SELECT = ["id", "displayName", "category"];
|
|
210
|
+
const DEFAULT_WORKFLOW_TEMPLATE_SHOW_SELECT = [...KNOWN_WORKFLOW_TEMPLATE_FIELDS];
|
|
211
|
+
const DEFAULT_TASK_DEFINITION_LIST_SELECT = ["id", "displayName", "category", "version"];
|
|
212
|
+
const DEFAULT_TASK_DEFINITION_SHOW_SELECT = [...KNOWN_TASK_DEFINITION_FIELDS];
|
|
213
|
+
const DEFAULT_LIFECYCLE_SETTINGS_SELECT = [...KNOWN_LIFECYCLE_SETTINGS_FIELDS];
|
|
214
|
+
// Compact run rows: identifiers plus execution status and user counts.
|
|
215
|
+
const DEFAULT_RUN_LIST_SELECT = ["id", "processingStatus", "totalUsersCount", "failedUsersCount", "successfulUsersCount"];
|
|
216
|
+
const DEFAULT_RUN_SHOW_SELECT = [...KNOWN_RUN_FIELDS];
|
|
217
|
+
// Processing results are personal data: minimal default fields (status and
|
|
218
|
+
// counts only) on both lists and singles; datetimes and versions need an
|
|
219
|
+
// explicit --select (user results) or --fields (subject results).
|
|
220
|
+
const DEFAULT_USER_PROCESSING_LIST_SELECT = ["id", "processingStatus", "failedTasksCount", "totalTasksCount", "totalUnprocessedTasksCount"];
|
|
221
|
+
const DEFAULT_USER_PROCESSING_SHOW_SELECT = [...DEFAULT_USER_PROCESSING_LIST_SELECT];
|
|
222
|
+
const DEFAULT_SUBJECT_PROCESSING_FIELDS = ["id", "subjectType", "processingStatus", "failedTasksCount", "totalTasksCount", "totalUnprocessedTasksCount"];
|
|
223
|
+
// Compact task-report rows: identifiers plus the owning run and user counts.
|
|
224
|
+
const DEFAULT_TASK_REPORT_LIST_SELECT = ["id", "runId", "processingStatus", "totalUsersCount", "failedUsersCount", "successfulUsersCount"];
|
|
225
|
+
const DEFAULT_TASK_REPORT_SHOW_SELECT = [...KNOWN_TASK_REPORT_FIELDS];
|
|
226
|
+
const DEFAULT_TASK_SHOW_SELECT = [...KNOWN_TASK_FIELDS];
|
|
227
|
+
export const DEFAULT_WORKFLOW_SCOPES = ["https://graph.microsoft.com/LifecycleWorkflows-Workflow.ReadBasic.All"];
|
|
228
|
+
export const DEFAULT_LIFECYCLE_SCOPES = ["https://graph.microsoft.com/LifecycleWorkflows.Read.All"];
|
|
229
|
+
export const DEFAULT_REPORTS_SCOPES = ["https://graph.microsoft.com/LifecycleWorkflows-Reports.Read.All"];
|
|
230
|
+
const TRUNCATE_AT = 500;
|
|
231
|
+
function fieldList(raw, flag, known, knownList, help) {
|
|
232
|
+
const parts = String(raw)
|
|
233
|
+
.split(",")
|
|
234
|
+
.map(part => part.trim())
|
|
235
|
+
.filter(part => part.length > 0);
|
|
236
|
+
if (!parts.length)
|
|
237
|
+
throw new AxiError(`--${flag} needs at least one property`, "VALIDATION_ERROR", [help]);
|
|
238
|
+
const fields = [];
|
|
239
|
+
for (const part of parts) {
|
|
240
|
+
if (!known.has(part)) {
|
|
241
|
+
throw new AxiError(`Unknown property ${part} in --${flag}`, "VALIDATION_ERROR", [
|
|
242
|
+
help,
|
|
243
|
+
`Known properties: ${knownList.join(", ")}`,
|
|
244
|
+
]);
|
|
245
|
+
}
|
|
246
|
+
if (!fields.includes(part))
|
|
247
|
+
fields.push(part);
|
|
248
|
+
}
|
|
249
|
+
return fields;
|
|
250
|
+
}
|
|
251
|
+
function scopesFor(flags, profile, defaults, help) {
|
|
252
|
+
if (profile.mode === "application") {
|
|
253
|
+
if (flags.scopes !== undefined) {
|
|
254
|
+
throw new AxiError("Application profiles use the configured Graph .default audience; delegated scopes are unavailable", "VALIDATION_ERROR", [help, "mg-axi profile show --profile <name>"]);
|
|
255
|
+
}
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
if (flags.scopes === undefined)
|
|
259
|
+
return [...defaults];
|
|
260
|
+
const scopes = String(flags.scopes)
|
|
261
|
+
.split(",")
|
|
262
|
+
.map(scope => scope.trim())
|
|
263
|
+
.filter(scope => scope.length > 0);
|
|
264
|
+
return scopes;
|
|
265
|
+
}
|
|
266
|
+
function truncateValue(value, full) {
|
|
267
|
+
if (full)
|
|
268
|
+
return { value, truncated: false };
|
|
269
|
+
if (typeof value === "string" && value.length > TRUNCATE_AT) {
|
|
270
|
+
return { value: `${value.slice(0, TRUNCATE_AT)}... (truncated, ${value.length} chars total)`, truncated: true };
|
|
271
|
+
}
|
|
272
|
+
if (Array.isArray(value)) {
|
|
273
|
+
const results = value.map(item => truncateValue(item, full));
|
|
274
|
+
return { value: results.map(result => result.value), truncated: results.some(result => result.truncated) };
|
|
275
|
+
}
|
|
276
|
+
if (value !== null && typeof value === "object") {
|
|
277
|
+
let truncated = false;
|
|
278
|
+
const entries = Object.entries(value).map(([key, item]) => {
|
|
279
|
+
const result = truncateValue(item, full);
|
|
280
|
+
truncated = truncated || result.truncated;
|
|
281
|
+
return [key, result.value];
|
|
282
|
+
});
|
|
283
|
+
return { value: Object.fromEntries(entries), truncated };
|
|
284
|
+
}
|
|
285
|
+
return { value, truncated: false };
|
|
286
|
+
}
|
|
287
|
+
// Local projection preserves Graph's null/missing distinction: an explicit
|
|
288
|
+
// null stays null, an absent property stays absent and is never synthesized.
|
|
289
|
+
function project(row, fields, full) {
|
|
290
|
+
const source = row !== null && typeof row === "object" && !Array.isArray(row) ? row : {};
|
|
291
|
+
const projected = {};
|
|
292
|
+
let truncated = false;
|
|
293
|
+
for (const field of fields) {
|
|
294
|
+
if (!Object.hasOwn(source, field))
|
|
295
|
+
continue;
|
|
296
|
+
const result = truncateValue(source[field], full);
|
|
297
|
+
projected[field] = result.value;
|
|
298
|
+
truncated = truncated || result.truncated;
|
|
299
|
+
}
|
|
300
|
+
return { row: projected, truncated };
|
|
301
|
+
}
|
|
302
|
+
function selectedFields(flags, defaults, known, knownList, help) {
|
|
303
|
+
const select = flags.select === undefined ? [...defaults] : fieldList(flags.select, "select", known, knownList, help);
|
|
304
|
+
const fields = flags.fields === undefined ? [...select] : fieldList(flags.fields, "fields", known, knownList, help);
|
|
305
|
+
const missing = fields.find(field => !select.includes(field));
|
|
306
|
+
if (missing) {
|
|
307
|
+
throw new AxiError(`--fields ${missing} was not fetched; request it with --select`, "VALIDATION_ERROR", [help]);
|
|
308
|
+
}
|
|
309
|
+
return { select, fields };
|
|
310
|
+
}
|
|
311
|
+
function profileHint(profileName) {
|
|
312
|
+
return `--profile ${shellValue(profileName)}`;
|
|
313
|
+
}
|
|
314
|
+
function shellValue(value) {
|
|
315
|
+
return /^[A-Za-z0-9_.,:/@=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|
|
316
|
+
}
|
|
317
|
+
function fullHint(command, flags, profileName) {
|
|
318
|
+
const args = Object.entries({ ...flags, ...(flags.cursor === undefined ? {} : { cursor: "-" }), profile: profileName, full: true })
|
|
319
|
+
.map(([name, value]) => value === true ? `--${name}` : `--${name} ${shellValue(String(value))}`);
|
|
320
|
+
return `mg-axi ${command} ${args.join(" ")}`;
|
|
321
|
+
}
|
|
322
|
+
// Denials carry the session's generic grant/role/licence cause;
|
|
323
|
+
// lifecycle workflows adds the scope, roles and licensing that actually
|
|
324
|
+
// unlock these reads, because a 403 alone never says which prerequisite is
|
|
325
|
+
// missing.
|
|
326
|
+
function withGuidance(run, hints = LIFECYCLE_DENIAL_HINTS) {
|
|
327
|
+
return run().catch(error => {
|
|
328
|
+
if (error instanceof AxiError && error.code === "GRAPH_ERROR" && /\(403\)/.test(error.message)) {
|
|
329
|
+
throw new AxiError(error.message, "GRAPH_ERROR", [...hints, ...error.suggestions]);
|
|
330
|
+
}
|
|
331
|
+
throw error;
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
const LIFECYCLE_DENIAL_HINTS = [
|
|
335
|
+
"Lifecycle-workflow reads need LifecycleWorkflows.Read.All plus Global Reader or Lifecycle Workflows Administrator for delegated access, or admin-consented LifecycleWorkflows.Read.All for application access; delegated personal Microsoft accounts are not supported",
|
|
336
|
+
"Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite (every governed user, not only administrators)",
|
|
337
|
+
];
|
|
338
|
+
// Workflow reads document their own least-privileged scope, so denials name
|
|
339
|
+
// ReadBasic first with the richer read alternatives beside it.
|
|
340
|
+
const WORKFLOW_DENIAL_HINTS = [
|
|
341
|
+
"Workflow reads need LifecycleWorkflows-Workflow.ReadBasic.All (least privileged; LifecycleWorkflows-Workflow.Read.All or LifecycleWorkflows.Read.All for richer detail) plus Global Reader or Lifecycle Workflows Administrator for delegated access, or admin-consented LifecycleWorkflows-Workflow.ReadBasic.All for application access; delegated personal Microsoft accounts are not supported",
|
|
342
|
+
"Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite (every governed user, not only administrators)",
|
|
343
|
+
];
|
|
344
|
+
// Run and processing-result reads document the reports scope, so denials
|
|
345
|
+
// name Reports.Read.All first with the richer read alternatives beside it.
|
|
346
|
+
const REPORTS_DENIAL_HINTS = [
|
|
347
|
+
"Lifecycle-workflow reporting reads need LifecycleWorkflows-Reports.Read.All (least privileged; LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail) plus Global Reader or Lifecycle Workflows Administrator for delegated access, or admin-consented LifecycleWorkflows-Reports.Read.All for application access; delegated personal Microsoft accounts are not supported",
|
|
348
|
+
"Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite (every governed user, not only administrators)",
|
|
349
|
+
];
|
|
350
|
+
function workflowId(flags, help) {
|
|
351
|
+
const workflow = flags.workflow === undefined ? "" : String(flags.workflow);
|
|
352
|
+
if (!workflow.trim()) {
|
|
353
|
+
throw new AxiError("--workflow needs the parent lifecycle workflow ID", "VALIDATION_ERROR", [help]);
|
|
354
|
+
}
|
|
355
|
+
return workflow;
|
|
356
|
+
}
|
|
357
|
+
function reportId(flags, help) {
|
|
358
|
+
const report = flags.report === undefined ? "" : String(flags.report);
|
|
359
|
+
if (!report.trim()) {
|
|
360
|
+
throw new AxiError("--report needs the parent task report ID", "VALIDATION_ERROR", [help]);
|
|
361
|
+
}
|
|
362
|
+
return report;
|
|
363
|
+
}
|
|
364
|
+
const WORKFLOW_LIST = {
|
|
365
|
+
command: "entra lifecycle workflow list",
|
|
366
|
+
key: "workflows",
|
|
367
|
+
known: WORKFLOW_KNOWN,
|
|
368
|
+
knownList: KNOWN_WORKFLOW_FIELDS,
|
|
369
|
+
defaultSelect: DEFAULT_WORKFLOW_LIST_SELECT,
|
|
370
|
+
defaultScopes: DEFAULT_WORKFLOW_SCOPES,
|
|
371
|
+
hints: WORKFLOW_DENIAL_HINTS,
|
|
372
|
+
emptyNote: "0 workflows matched; the absence of results is the answer, not an error",
|
|
373
|
+
standing: profileName => [
|
|
374
|
+
"Workflows are the configured joiner/mover/leaver automations: a workflow row never carries its tasks, runs or processing results; those belong to later slices",
|
|
375
|
+
`Show one workflow: mg-axi entra lifecycle workflow show --id <workflow-id> ${profileHint(profileName)}`,
|
|
376
|
+
],
|
|
377
|
+
};
|
|
378
|
+
const WORKFLOW_TEMPLATE_LIST = {
|
|
379
|
+
command: "entra lifecycle workflow-template list",
|
|
380
|
+
key: "workflowTemplates",
|
|
381
|
+
known: WORKFLOW_TEMPLATE_KNOWN,
|
|
382
|
+
knownList: KNOWN_WORKFLOW_TEMPLATE_FIELDS,
|
|
383
|
+
defaultSelect: DEFAULT_WORKFLOW_TEMPLATE_LIST_SELECT,
|
|
384
|
+
defaultScopes: DEFAULT_LIFECYCLE_SCOPES,
|
|
385
|
+
hints: LIFECYCLE_DENIAL_HINTS,
|
|
386
|
+
emptyNote: "0 workflow templates matched; the absence of results is the answer, not an error",
|
|
387
|
+
standing: profileName => [
|
|
388
|
+
"Workflow templates are the pre-configured Entra scenarios new workflows are built from; template tasks belong to a later slice",
|
|
389
|
+
`Show one workflow template: mg-axi entra lifecycle workflow-template show --id <template-id> ${profileHint(profileName)}`,
|
|
390
|
+
],
|
|
391
|
+
};
|
|
392
|
+
const TASK_DEFINITION_LIST = {
|
|
393
|
+
command: "entra lifecycle task-definition list",
|
|
394
|
+
key: "taskDefinitions",
|
|
395
|
+
known: TASK_DEFINITION_KNOWN,
|
|
396
|
+
knownList: KNOWN_TASK_DEFINITION_FIELDS,
|
|
397
|
+
defaultSelect: DEFAULT_TASK_DEFINITION_LIST_SELECT,
|
|
398
|
+
defaultScopes: DEFAULT_LIFECYCLE_SCOPES,
|
|
399
|
+
hints: LIFECYCLE_DENIAL_HINTS,
|
|
400
|
+
emptyNote: "0 task definitions matched; the absence of results is the answer, not an error",
|
|
401
|
+
standing: profileName => [
|
|
402
|
+
"Task definitions are the built-in tasks workflows are constructed from; they carry no relationships",
|
|
403
|
+
`Show one task definition: mg-axi entra lifecycle task-definition show --id <task-definition-id> ${profileHint(profileName)}`,
|
|
404
|
+
],
|
|
405
|
+
};
|
|
406
|
+
const RUN_LIST = {
|
|
407
|
+
command: "entra lifecycle run list",
|
|
408
|
+
key: "runs",
|
|
409
|
+
known: RUN_KNOWN,
|
|
410
|
+
knownList: KNOWN_RUN_FIELDS,
|
|
411
|
+
defaultSelect: DEFAULT_RUN_LIST_SELECT,
|
|
412
|
+
defaultScopes: DEFAULT_REPORTS_SCOPES,
|
|
413
|
+
hints: REPORTS_DENIAL_HINTS,
|
|
414
|
+
emptyNote: "0 runs matched; the absence of results is the answer, not an error",
|
|
415
|
+
standing: profileName => [
|
|
416
|
+
"Runs are the execution instances of one workflow: a run row never carries its user, subject or task processing results; those belong to later slices",
|
|
417
|
+
`Show one run: mg-axi entra lifecycle run show --workflow <workflow-id> --id <run-id> ${profileHint(profileName)}`,
|
|
418
|
+
],
|
|
419
|
+
};
|
|
420
|
+
const USER_PROCESSING_LIST = {
|
|
421
|
+
command: "entra lifecycle user-processing-result list",
|
|
422
|
+
key: "userProcessingResults",
|
|
423
|
+
known: USER_PROCESSING_KNOWN,
|
|
424
|
+
knownList: KNOWN_USER_PROCESSING_FIELDS,
|
|
425
|
+
defaultSelect: DEFAULT_USER_PROCESSING_LIST_SELECT,
|
|
426
|
+
defaultScopes: DEFAULT_REPORTS_SCOPES,
|
|
427
|
+
hints: REPORTS_DENIAL_HINTS,
|
|
428
|
+
emptyNote: "0 user processing results matched; the absence of results is the answer, not an error",
|
|
429
|
+
standing: profileName => [
|
|
430
|
+
"User processing results aggregate one user's workflow execution and carry personal data: defaults stay minimal and the subject link is never requested",
|
|
431
|
+
`Show one user processing result: mg-axi entra lifecycle user-processing-result show --workflow <workflow-id> --id <result-id> ${profileHint(profileName)}`,
|
|
432
|
+
],
|
|
433
|
+
};
|
|
434
|
+
const SUBJECT_PROCESSING_LIST = {
|
|
435
|
+
command: "entra lifecycle subject-processing-result list",
|
|
436
|
+
key: "subjectProcessingResults",
|
|
437
|
+
known: SUBJECT_PROCESSING_KNOWN,
|
|
438
|
+
knownList: KNOWN_SUBJECT_PROCESSING_FIELDS,
|
|
439
|
+
defaultSelect: DEFAULT_SUBJECT_PROCESSING_FIELDS,
|
|
440
|
+
defaultScopes: DEFAULT_REPORTS_SCOPES,
|
|
441
|
+
hints: REPORTS_DENIAL_HINTS,
|
|
442
|
+
emptyNote: "0 subject processing results matched; the absence of results is the answer, not an error",
|
|
443
|
+
queryless: true,
|
|
444
|
+
standing: profileName => [
|
|
445
|
+
"Subject processing results mirror user results for non-user subjects and carry personal data: rows arrive whole (Graph documents no $select here) and defaults stay minimal",
|
|
446
|
+
`Show one subject processing result: mg-axi entra lifecycle subject-processing-result show --workflow <workflow-id> --id <result-id> ${profileHint(profileName)}`,
|
|
447
|
+
],
|
|
448
|
+
};
|
|
449
|
+
const TASK_REPORT_LIST = {
|
|
450
|
+
command: "entra lifecycle task-report list",
|
|
451
|
+
key: "taskReports",
|
|
452
|
+
known: TASK_REPORT_KNOWN,
|
|
453
|
+
knownList: KNOWN_TASK_REPORT_FIELDS,
|
|
454
|
+
defaultSelect: DEFAULT_TASK_REPORT_LIST_SELECT,
|
|
455
|
+
defaultScopes: DEFAULT_REPORTS_SCOPES,
|
|
456
|
+
hints: REPORTS_DENIAL_HINTS,
|
|
457
|
+
emptyNote: "0 task reports matched; the absence of results is the answer, not an error",
|
|
458
|
+
standing: profileName => [
|
|
459
|
+
"Task reports aggregate one workflow task's results within one run: a report row never carries its task, task definition or task processing results; those belong to later slices",
|
|
460
|
+
`Show one task report: mg-axi entra lifecycle task-report show --workflow <workflow-id> --id <report-id> ${profileHint(profileName)}`,
|
|
461
|
+
],
|
|
462
|
+
};
|
|
463
|
+
async function listCollection(shape, session, flags, profile, operation, help, profileName, params = {}, resumeHint) {
|
|
464
|
+
const cursor = flags.cursor === undefined ? undefined : String(flags.cursor);
|
|
465
|
+
if (cursor !== undefined && !cursor.trim())
|
|
466
|
+
throw new AxiError("--cursor needs the opaque cursor from a partial result", "VALIDATION_ERROR", [help]);
|
|
467
|
+
if (shape.queryless === true && flags.select !== undefined) {
|
|
468
|
+
throw new AxiError("Graph documents no $select on this read; whole rows are returned and projected locally with --fields", "VALIDATION_ERROR", [help]);
|
|
469
|
+
}
|
|
470
|
+
const saved = cursor === undefined ? undefined : session.cursorQuery(operation, cursor);
|
|
471
|
+
const savedSelect = saved?.$select;
|
|
472
|
+
const { select, fields } = shape.queryless === true
|
|
473
|
+
? { select: [], fields: flags.fields === undefined ? [...shape.defaultSelect] : fieldList(flags.fields, "fields", shape.known, shape.knownList, help) }
|
|
474
|
+
: selectedFields(flags, savedSelect === undefined ? shape.defaultSelect : fieldList(savedSelect, "select", shape.known, shape.knownList, help), shape.known, shape.knownList, help);
|
|
475
|
+
const savedFilter = saved?.$filter;
|
|
476
|
+
const filter = flags.filter === undefined ? savedFilter : String(flags.filter);
|
|
477
|
+
const scopes = scopesFor(flags, profile, shape.defaultScopes, help);
|
|
478
|
+
const full = flags.full === true;
|
|
479
|
+
// These lists document $filter (and $select, except the queryless
|
|
480
|
+
// subject list), so --filter passes through as plain $filter with no
|
|
481
|
+
// $count or ConsistencyLevel contract; $search/$orderby/$expand stay
|
|
482
|
+
// unreviewed and $top/$skip stay server-side paging concerns. Resumed
|
|
483
|
+
// queries replay the stored $select verbatim so resume never conflicts.
|
|
484
|
+
// Queryless lists never send $select: rows arrive whole and --fields
|
|
485
|
+
// projects locally.
|
|
486
|
+
let sendSelect = [];
|
|
487
|
+
if (shape.queryless !== true) {
|
|
488
|
+
sendSelect = cursor === undefined
|
|
489
|
+
? select
|
|
490
|
+
: saved?.$select === undefined ? [] : fieldList(saved.$select, "select", shape.known, shape.knownList, help);
|
|
491
|
+
}
|
|
492
|
+
const query = {};
|
|
493
|
+
if (sendSelect.length)
|
|
494
|
+
query.$select = sendSelect.join(",");
|
|
495
|
+
if (filter !== undefined)
|
|
496
|
+
query.$filter = filter;
|
|
497
|
+
const args = { profile, operation, params, query, scopes };
|
|
498
|
+
if (cursor !== undefined)
|
|
499
|
+
args.cursor = cursor;
|
|
500
|
+
if (flags.all === true) {
|
|
501
|
+
if (flags.limit !== undefined)
|
|
502
|
+
throw new AxiError("--limit and --all cannot be combined", "VALIDATION_ERROR", [help]);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
args.limit = flags.limit === undefined ? 100 : Number(flags.limit);
|
|
506
|
+
}
|
|
507
|
+
const result = await withGuidance(() => session.collect(args), shape.hints);
|
|
508
|
+
const effectiveFlags = { ...flags, ...(result.query.$select === undefined ? {} : { select: select.join(",") }) };
|
|
509
|
+
if (result.query.$filter !== undefined)
|
|
510
|
+
effectiveFlags.filter = result.query.$filter;
|
|
511
|
+
const rows = [];
|
|
512
|
+
let truncated = false;
|
|
513
|
+
for (const row of result.value) {
|
|
514
|
+
const projected = project(row, fields, full);
|
|
515
|
+
rows.push(projected.row);
|
|
516
|
+
truncated = truncated || projected.truncated;
|
|
517
|
+
}
|
|
518
|
+
const truncationHints = truncated ? [fullHint(shape.command, effectiveFlags, profileName)] : [];
|
|
519
|
+
if (truncated && cursor !== undefined)
|
|
520
|
+
truncationHints.push("Supply the original input cursor on stdin to replay this result with --full");
|
|
521
|
+
const resume = resumeHint ?? `Resume losslessly with the same flags plus --cursor - ${profileHint(profileName)} and supply the returned cursor on stdin`;
|
|
522
|
+
const standing = [...shape.standing(profileName)];
|
|
523
|
+
if (!result.complete) {
|
|
524
|
+
return {
|
|
525
|
+
[shape.key]: rows,
|
|
526
|
+
count: { returned: rows.length, complete: false, reason: result.reason },
|
|
527
|
+
cursor: result.cursor,
|
|
528
|
+
help: [...truncationHints, resume, ...standing],
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
const count = { returned: rows.length, complete: true };
|
|
532
|
+
if (!rows.length) {
|
|
533
|
+
return { [shape.key]: rows, count, help: [shape.emptyNote, ...standing] };
|
|
534
|
+
}
|
|
535
|
+
return { [shape.key]: rows, count, help: [...truncationHints, ...standing] };
|
|
536
|
+
}
|
|
537
|
+
async function showOne(key, command, known, knownList, defaultSelect, defaultScopes, hints, session, flags, profile, operation, help, profileName, params, malformed, queryless = false) {
|
|
538
|
+
if (queryless && flags.select !== undefined) {
|
|
539
|
+
throw new AxiError("Graph documents no query parameters on this read; whole rows are always returned", "VALIDATION_ERROR", [help]);
|
|
540
|
+
}
|
|
541
|
+
const { select, fields } = queryless
|
|
542
|
+
? { select: [...defaultSelect], fields: flags.fields === undefined ? [...defaultSelect] : fieldList(flags.fields, "fields", known, knownList, help) }
|
|
543
|
+
: selectedFields(flags, defaultSelect, known, knownList, help);
|
|
544
|
+
const scopes = scopesFor(flags, profile, defaultScopes, help);
|
|
545
|
+
const full = flags.full === true;
|
|
546
|
+
// Singles document $select only (the template get documents no query
|
|
547
|
+
// parameters at all and arrives whole); $expand stays out everywhere:
|
|
548
|
+
// tasks, createdBy, lastModifiedBy and previewScope belong to later
|
|
549
|
+
// slices, never to these reads.
|
|
550
|
+
const query = {};
|
|
551
|
+
if (!queryless && select.length)
|
|
552
|
+
query.$select = select.join(",");
|
|
553
|
+
const raw = await withGuidance(() => session.execute({
|
|
554
|
+
profile,
|
|
555
|
+
operation,
|
|
556
|
+
params,
|
|
557
|
+
query,
|
|
558
|
+
scopes,
|
|
559
|
+
}), hints);
|
|
560
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
561
|
+
throw new AxiError(malformed, "GRAPH_ERROR", [
|
|
562
|
+
"Single-object reads carry one object; treat anything else as unknown, not empty",
|
|
563
|
+
]);
|
|
564
|
+
}
|
|
565
|
+
const { row, truncated } = project(raw, fields, full);
|
|
566
|
+
if (truncated)
|
|
567
|
+
return { [key]: row, help: [fullHint(command, flags, profileName)] };
|
|
568
|
+
return { [key]: row };
|
|
569
|
+
}
|
|
570
|
+
async function countCollection(key, emptyNote, defaultScopes, hints, session, flags, profile, operation, help, _profileName, params = {}) {
|
|
571
|
+
const scopes = scopesFor(flags, profile, defaultScopes, help);
|
|
572
|
+
// The $count route returns a text/plain integer scalar rather than a
|
|
573
|
+
// JSON collection, so the leaf reads it through session.execute with
|
|
574
|
+
// scalar mode and accepts only a non-negative integer. The $count route
|
|
575
|
+
// carries no operation-level documentation page and takes no
|
|
576
|
+
// --filter/--select/--limit/--cursor.
|
|
577
|
+
const raw = await withGuidance(() => session.execute({ profile, operation, params, scopes, scalar: true }), hints);
|
|
578
|
+
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 0) {
|
|
579
|
+
throw new AxiError(`Graph returned a malformed ${key} count body`, "GRAPH_ERROR", [
|
|
580
|
+
"Counts carry one non-negative integer scalar; treat anything else as unknown, not empty",
|
|
581
|
+
]);
|
|
582
|
+
}
|
|
583
|
+
const count = { returned: raw, complete: true };
|
|
584
|
+
return raw === 0 ? { count, help: [emptyNote] } : { count };
|
|
585
|
+
}
|
|
586
|
+
export async function listWorkflows(session, flags, profile, operation, help, profileName) {
|
|
587
|
+
return listCollection(WORKFLOW_LIST, session, flags, profile, operation, help, profileName);
|
|
588
|
+
}
|
|
589
|
+
export async function showWorkflow(session, flags, profile, operation, help, profileName) {
|
|
590
|
+
return showOne("workflow", "entra lifecycle workflow show", WORKFLOW_KNOWN, KNOWN_WORKFLOW_FIELDS, DEFAULT_WORKFLOW_SHOW_SELECT, DEFAULT_WORKFLOW_SCOPES, WORKFLOW_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": String(flags.id) }, "Graph returned a malformed lifecycle workflow body");
|
|
591
|
+
}
|
|
592
|
+
export async function countWorkflows(session, flags, profile, operation, help, profileName) {
|
|
593
|
+
return countCollection("workflow", WORKFLOW_LIST.emptyNote, DEFAULT_WORKFLOW_SCOPES, WORKFLOW_DENIAL_HINTS, session, flags, profile, operation, help, profileName);
|
|
594
|
+
}
|
|
595
|
+
export async function listWorkflowTemplates(session, flags, profile, operation, help, profileName) {
|
|
596
|
+
return listCollection(WORKFLOW_TEMPLATE_LIST, session, flags, profile, operation, help, profileName);
|
|
597
|
+
}
|
|
598
|
+
export async function showWorkflowTemplate(session, flags, profile, operation, help, profileName) {
|
|
599
|
+
return showOne("workflowTemplate", "entra lifecycle workflow-template show", WORKFLOW_TEMPLATE_KNOWN, KNOWN_WORKFLOW_TEMPLATE_FIELDS, DEFAULT_WORKFLOW_TEMPLATE_SHOW_SELECT, DEFAULT_LIFECYCLE_SCOPES, LIFECYCLE_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflowTemplate-id": String(flags.id) }, "Graph returned a malformed workflow template body", true);
|
|
600
|
+
}
|
|
601
|
+
export async function countWorkflowTemplates(session, flags, profile, operation, help, profileName) {
|
|
602
|
+
return countCollection("workflow template", WORKFLOW_TEMPLATE_LIST.emptyNote, DEFAULT_LIFECYCLE_SCOPES, LIFECYCLE_DENIAL_HINTS, session, flags, profile, operation, help, profileName);
|
|
603
|
+
}
|
|
604
|
+
export async function listTaskDefinitions(session, flags, profile, operation, help, profileName) {
|
|
605
|
+
return listCollection(TASK_DEFINITION_LIST, session, flags, profile, operation, help, profileName);
|
|
606
|
+
}
|
|
607
|
+
export async function showTaskDefinition(session, flags, profile, operation, help, profileName) {
|
|
608
|
+
return showOne("taskDefinition", "entra lifecycle task-definition show", TASK_DEFINITION_KNOWN, KNOWN_TASK_DEFINITION_FIELDS, DEFAULT_TASK_DEFINITION_SHOW_SELECT, DEFAULT_LIFECYCLE_SCOPES, LIFECYCLE_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "taskDefinition-id": String(flags.id) }, "Graph returned a malformed task definition body");
|
|
609
|
+
}
|
|
610
|
+
export async function countTaskDefinitions(session, flags, profile, operation, help, profileName) {
|
|
611
|
+
return countCollection("task definition", TASK_DEFINITION_LIST.emptyNote, DEFAULT_LIFECYCLE_SCOPES, LIFECYCLE_DENIAL_HINTS, session, flags, profile, operation, help, profileName);
|
|
612
|
+
}
|
|
613
|
+
export async function showLifecycleSettings(session, flags, profile, operation, help, profileName) {
|
|
614
|
+
return showOne("settings", "entra lifecycle settings show", LIFECYCLE_SETTINGS_KNOWN, KNOWN_LIFECYCLE_SETTINGS_FIELDS, DEFAULT_LIFECYCLE_SETTINGS_SELECT, DEFAULT_LIFECYCLE_SCOPES, LIFECYCLE_DENIAL_HINTS, session, flags, profile, operation, help, profileName, {}, "Graph returned a malformed lifecycle settings body");
|
|
615
|
+
}
|
|
616
|
+
export async function listRuns(session, flags, profile, operation, help, profileName) {
|
|
617
|
+
const workflow = workflowId(flags, help);
|
|
618
|
+
return listCollection(RUN_LIST, session, flags, profile, operation, help, profileName, { "workflow-id": workflow }, `Resume losslessly with the same --workflow and flags plus --cursor - ${profileHint(profileName)} and supply the returned cursor on stdin`);
|
|
619
|
+
}
|
|
620
|
+
export async function showRun(session, flags, profile, operation, help, profileName) {
|
|
621
|
+
const workflow = workflowId(flags, help);
|
|
622
|
+
return showOne("run", "entra lifecycle run show", RUN_KNOWN, KNOWN_RUN_FIELDS, DEFAULT_RUN_SHOW_SELECT, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow, "run-id": String(flags.id) }, "Graph returned a malformed lifecycle run body");
|
|
623
|
+
}
|
|
624
|
+
export async function countRuns(session, flags, profile, operation, help, profileName) {
|
|
625
|
+
const workflow = workflowId(flags, help);
|
|
626
|
+
return countCollection("run", RUN_LIST.emptyNote, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow });
|
|
627
|
+
}
|
|
628
|
+
export async function listUserProcessingResults(session, flags, profile, operation, help, profileName) {
|
|
629
|
+
const workflow = workflowId(flags, help);
|
|
630
|
+
return listCollection(USER_PROCESSING_LIST, session, flags, profile, operation, help, profileName, { "workflow-id": workflow }, `Resume losslessly with the same --workflow and flags plus --cursor - ${profileHint(profileName)} and supply the returned cursor on stdin`);
|
|
631
|
+
}
|
|
632
|
+
export async function showUserProcessingResult(session, flags, profile, operation, help, profileName) {
|
|
633
|
+
const workflow = workflowId(flags, help);
|
|
634
|
+
return showOne("userProcessingResult", "entra lifecycle user-processing-result show", USER_PROCESSING_KNOWN, KNOWN_USER_PROCESSING_FIELDS, DEFAULT_USER_PROCESSING_SHOW_SELECT, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow, "userProcessingResult-id": String(flags.id) }, "Graph returned a malformed user processing result body");
|
|
635
|
+
}
|
|
636
|
+
export async function countUserProcessingResults(session, flags, profile, operation, help, profileName) {
|
|
637
|
+
const workflow = workflowId(flags, help);
|
|
638
|
+
return countCollection("user processing result", USER_PROCESSING_LIST.emptyNote, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow });
|
|
639
|
+
}
|
|
640
|
+
export async function listSubjectProcessingResults(session, flags, profile, operation, help, profileName) {
|
|
641
|
+
const workflow = workflowId(flags, help);
|
|
642
|
+
return listCollection(SUBJECT_PROCESSING_LIST, session, flags, profile, operation, help, profileName, { "workflow-id": workflow }, `Resume losslessly with the same --workflow and flags plus --cursor - ${profileHint(profileName)} and supply the returned cursor on stdin`);
|
|
643
|
+
}
|
|
644
|
+
export async function showSubjectProcessingResult(session, flags, profile, operation, help, profileName) {
|
|
645
|
+
const workflow = workflowId(flags, help);
|
|
646
|
+
return showOne("subjectProcessingResult", "entra lifecycle subject-processing-result show", SUBJECT_PROCESSING_KNOWN, KNOWN_SUBJECT_PROCESSING_FIELDS, DEFAULT_SUBJECT_PROCESSING_FIELDS, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow, "subjectProcessingResult-id": String(flags.id) }, "Graph returned a malformed subject processing result body", true);
|
|
647
|
+
}
|
|
648
|
+
export async function countSubjectProcessingResults(session, flags, profile, operation, help, profileName) {
|
|
649
|
+
const workflow = workflowId(flags, help);
|
|
650
|
+
return countCollection("subject processing result", SUBJECT_PROCESSING_LIST.emptyNote, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow });
|
|
651
|
+
}
|
|
652
|
+
export async function listTaskReports(session, flags, profile, operation, help, profileName) {
|
|
653
|
+
const workflow = workflowId(flags, help);
|
|
654
|
+
return listCollection(TASK_REPORT_LIST, session, flags, profile, operation, help, profileName, { "workflow-id": workflow }, `Resume losslessly with the same --workflow and flags plus --cursor - ${profileHint(profileName)} and supply the returned cursor on stdin`);
|
|
655
|
+
}
|
|
656
|
+
export async function showTaskReport(session, flags, profile, operation, help, profileName) {
|
|
657
|
+
const workflow = workflowId(flags, help);
|
|
658
|
+
return showOne("taskReport", "entra lifecycle task-report show", TASK_REPORT_KNOWN, KNOWN_TASK_REPORT_FIELDS, DEFAULT_TASK_REPORT_SHOW_SELECT, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow, "taskReport-id": String(flags.id) }, "Graph returned a malformed task report body", true);
|
|
659
|
+
}
|
|
660
|
+
export async function countTaskReports(session, flags, profile, operation, help, profileName) {
|
|
661
|
+
const workflow = workflowId(flags, help);
|
|
662
|
+
return countCollection("task report", TASK_REPORT_LIST.emptyNote, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow });
|
|
663
|
+
}
|
|
664
|
+
export async function showTaskReportTask(session, flags, profile, operation, help, profileName) {
|
|
665
|
+
const workflow = workflowId(flags, help);
|
|
666
|
+
const report = reportId(flags, help);
|
|
667
|
+
return showOne("task", "entra lifecycle task-report task show", TASK_KNOWN, KNOWN_TASK_FIELDS, DEFAULT_TASK_SHOW_SELECT, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow, "taskReport-id": report }, "Graph returned a malformed lifecycle task body", true);
|
|
668
|
+
}
|
|
669
|
+
export async function showTaskReportTaskDefinition(session, flags, profile, operation, help, profileName) {
|
|
670
|
+
const workflow = workflowId(flags, help);
|
|
671
|
+
const report = reportId(flags, help);
|
|
672
|
+
return showOne("taskDefinition", "entra lifecycle task-report task-definition show", TASK_DEFINITION_KNOWN, KNOWN_TASK_DEFINITION_FIELDS, DEFAULT_TASK_DEFINITION_SHOW_SELECT, DEFAULT_REPORTS_SCOPES, REPORTS_DENIAL_HINTS, session, flags, profile, operation, help, profileName, { "workflow-id": workflow, "taskReport-id": report }, "Graph returned a malformed task definition body", true);
|
|
673
|
+
}
|