@knowttl/mg-axi 0.1.1 → 0.1.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 +251 -77
- package/dist/api.js +253 -8
- package/dist/bin/mg-axi-hook.js +31 -0
- package/dist/catalogue.js +693 -2
- package/dist/cli.js +155 -17
- package/dist/docs.js +175 -46
- package/dist/doctor.js +2 -2
- package/dist/entra-access-reviews.js +11 -4
- package/dist/entra-apps.js +171 -14
- package/dist/entra-audit-logs.js +101 -16
- package/dist/entra-auth-methods.js +8 -4
- package/dist/entra-certificate-auth.js +12 -8
- package/dist/entra-conditional-access.js +425 -15
- package/dist/entra-contacts.js +19 -12
- package/dist/entra-contracts.js +7 -4
- package/dist/entra-custom-security-attributes.js +19 -12
- package/dist/entra-data-policy-operations.js +7 -4
- package/dist/entra-delegated-admin.js +55 -6
- package/dist/entra-deleted-items.js +6 -4
- package/dist/entra-directory-objects.js +6 -4
- package/dist/entra-directory.js +19 -12
- package/dist/entra-domains.js +13 -8
- package/dist/entra-entitlement-management.js +12 -4
- package/dist/entra-federation-configurations.js +7 -4
- package/dist/entra-grants.js +8 -4
- package/dist/entra-group-lifecycle.js +13 -8
- package/dist/entra-group-pim.js +431 -0
- package/dist/entra-groups.js +13 -8
- package/dist/entra-identity-providers.js +7 -4
- package/dist/entra-lifecycle-workflows.js +152 -13
- package/dist/entra-multi-tenant-organization.js +7 -4
- package/dist/entra-on-premises-synchronization.js +6 -4
- package/dist/entra-organization.js +13 -8
- package/dist/entra-risk-prevention.js +442 -0
- package/dist/entra-risk.js +224 -9
- package/dist/entra-roles.js +204 -12
- package/dist/entra-subscriptions.js +6 -4
- package/dist/entra-terms-of-use.js +13 -8
- package/dist/entra-users.js +7 -4
- package/dist/graph-session.js +30 -5
- package/dist/hook.js +84 -0
- package/dist/list-totals.js +52 -0
- package/dist/setup.js +9 -1
- package/docs/coverage.md +743 -610
- package/inventory/operations.json +2 -2
- package/inventory/schema.json +1 -1
- package/package.json +4 -3
- package/skills/mg-axi/SKILL.md +86 -1
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
import { listTotals } from "./list-totals.js";
|
|
2
3
|
// READ-03: the Conditional Access read mapping behind
|
|
3
4
|
// `mg-axi entra conditional-access policy list/show` and
|
|
4
5
|
// `mg-axi entra conditional-access named-location list/show` as separate
|
|
5
|
-
// az-style grammar
|
|
6
|
+
// az-style grammar, plus the remaining v1.0 named collections under
|
|
7
|
+
// `identity/conditionalAccess`: authentication-strength policies, each
|
|
8
|
+
// policy's combination configurations, authentication method modes,
|
|
9
|
+
// templates, authentication context class references and the deleted
|
|
10
|
+
// policy and named-location collections. Operation construction stays beside its command; the
|
|
6
11
|
// shared session owns URLs, credentials, paging, retries and error
|
|
7
12
|
// translation, and the SDK owns TOON rendering. This module only maps flags
|
|
8
13
|
// to session calls and projects rows for compact output. No policy mutation
|
|
@@ -10,13 +15,45 @@ import { AxiError } from "axi-sdk-js";
|
|
|
10
15
|
//
|
|
11
16
|
// Reviewed against the v1.0 conditionalaccessroot-list-policies,
|
|
12
17
|
// conditionalaccesspolicy-get, conditionalaccessroot-list-namedlocations and
|
|
13
|
-
// countrynamedlocation-get operation documentation on 2026-10-04
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
18
|
+
// countrynamedlocation-get operation documentation on 2026-10-04, and the
|
|
19
|
+
// authenticationstrengthroot-list-policies,
|
|
20
|
+
// authenticationstrengthpolicy-get,
|
|
21
|
+
// authenticationstrengthpolicy-list-combinationconfigurations,
|
|
22
|
+
// authenticationcombinationconfiguration-get,
|
|
23
|
+
// authenticationstrengthroot-list-authenticationmethodmodes,
|
|
24
|
+
// authenticationmethodmodedetail-get, conditionalaccessroot-list-templates,
|
|
25
|
+
// conditionalaccesstemplate-get, conditionalaccessroot-list-
|
|
26
|
+
// authenticationcontextclassreferences and authenticationcontextclassreference-
|
|
27
|
+
// get operation documentation on 2026-10-06. The deleted policy and
|
|
28
|
+
// named-location collections carry no v1.0 operation-level documentation
|
|
29
|
+
// page; their review follows the beta policyDeletableItem list/get contract
|
|
30
|
+
// for conditional-access objects with the conditionalAccessPolicy,
|
|
31
|
+
// namedLocation and policyDeletableItem resource references.
|
|
32
|
+
// Policy and named-location reads need D/A Policy.Read.All; the
|
|
33
|
+
// authentication-strength family (policies, combination configurations and
|
|
34
|
+
// authentication method modes) reads D/A Policy.Read.AuthenticationMethod
|
|
35
|
+
// instead, and template reads D/A Policy.Read.All. Delegated callers
|
|
36
|
+
// additionally need a supported directory role (Conditional Access
|
|
37
|
+
// Administrator, Security Administrator or Security Reader for the
|
|
38
|
+
// strength family; those plus Global Reader and Global Secure Access
|
|
39
|
+
// Administrator for policies, named locations and templates). The
|
|
40
|
+
// authentication context class references read D/A
|
|
41
|
+
// AuthenticationContext.Read.All with the same broad delegated role set.
|
|
42
|
+
// Deleted policies and named locations read D/A Policy.Read.All with the
|
|
43
|
+
// same broad delegated role set; restore is a beta-only mutation and stays
|
|
44
|
+
// out of scope.
|
|
17
45
|
// Conditional Access needs P1; risk-based Conditional Access needs P2. The
|
|
18
46
|
// reviewed raw surface in src/api.ts carries exactly these routes, fields
|
|
19
47
|
// and access choices; the named commands below reuse that contract.
|
|
48
|
+
//
|
|
49
|
+
// Deferred with reason: the parameterless
|
|
50
|
+
// `authenticationStrength/policies/{id}/usage()` binds no placeholder, so
|
|
51
|
+
// it never passes through the session's validated function-argument
|
|
52
|
+
// binding (FUNCTION_ARGUMENT_BINDINGS covers only named-parameter
|
|
53
|
+
// segments); widening the request-path guard for one function is out of
|
|
54
|
+
// scope. Authentication context class references land here: the former
|
|
55
|
+
// needs an AuthenticationContext.Read.All allowlist addition, now carried
|
|
56
|
+
// in the session's READ_SCOPES set.
|
|
20
57
|
// Every policy property this slice may request or display, matching the
|
|
21
58
|
// reviewed raw surface. Anything else fails before credentials.
|
|
22
59
|
export const KNOWN_POLICY_FIELDS = [
|
|
@@ -43,6 +80,61 @@ export const KNOWN_LOCATION_FIELDS = [
|
|
|
43
80
|
];
|
|
44
81
|
const POLICY_KNOWN = new Set(KNOWN_POLICY_FIELDS);
|
|
45
82
|
const LOCATION_KNOWN = new Set(KNOWN_LOCATION_FIELDS);
|
|
83
|
+
// Every authentication-strength policy property this slice may request or
|
|
84
|
+
// display, matching the reviewed raw surface.
|
|
85
|
+
export const KNOWN_STRENGTH_FIELDS = [
|
|
86
|
+
"id",
|
|
87
|
+
"displayName",
|
|
88
|
+
"description",
|
|
89
|
+
"policyType",
|
|
90
|
+
"requirementsSatisfied",
|
|
91
|
+
"allowedCombinations",
|
|
92
|
+
"createdDateTime",
|
|
93
|
+
"modifiedDateTime",
|
|
94
|
+
];
|
|
95
|
+
// Every combination-configuration property this slice may request or
|
|
96
|
+
// display: the base type carries only an identifier and the combinations
|
|
97
|
+
// it applies to; subtype detail rides on @odata.type instead.
|
|
98
|
+
export const KNOWN_COMBO_FIELDS = [
|
|
99
|
+
"id",
|
|
100
|
+
"appliesToCombinations",
|
|
101
|
+
];
|
|
102
|
+
// Every authentication method mode property this slice may request or
|
|
103
|
+
// display, matching the reviewed raw surface.
|
|
104
|
+
export const KNOWN_MODE_FIELDS = [
|
|
105
|
+
"id",
|
|
106
|
+
"displayName",
|
|
107
|
+
"authenticationMethod",
|
|
108
|
+
];
|
|
109
|
+
// Every Conditional Access template property this slice may request or
|
|
110
|
+
// display, matching the reviewed raw surface.
|
|
111
|
+
export const KNOWN_TEMPLATE_FIELDS = [
|
|
112
|
+
"id",
|
|
113
|
+
"name",
|
|
114
|
+
"description",
|
|
115
|
+
"scenarios",
|
|
116
|
+
"details",
|
|
117
|
+
];
|
|
118
|
+
// Every authentication context class reference property this slice may
|
|
119
|
+
// request or display, matching the reviewed v1.0 resource shape.
|
|
120
|
+
export const KNOWN_AUTH_CONTEXT_FIELDS = [
|
|
121
|
+
"id",
|
|
122
|
+
"displayName",
|
|
123
|
+
"description",
|
|
124
|
+
"isAvailable",
|
|
125
|
+
];
|
|
126
|
+
// Deleted policies and named locations return the underlying policy and
|
|
127
|
+
// location objects with the soft-delete timestamp the policyDeletableItem
|
|
128
|
+
// base type adds, so the reviewed sets extend the live ones.
|
|
129
|
+
export const KNOWN_DELETED_POLICY_FIELDS = [...KNOWN_POLICY_FIELDS, "deletedDateTime"];
|
|
130
|
+
export const KNOWN_DELETED_LOCATION_FIELDS = [...KNOWN_LOCATION_FIELDS, "deletedDateTime"];
|
|
131
|
+
const STRENGTH_KNOWN = new Set(KNOWN_STRENGTH_FIELDS);
|
|
132
|
+
const COMBO_KNOWN = new Set(KNOWN_COMBO_FIELDS);
|
|
133
|
+
const MODE_KNOWN = new Set(KNOWN_MODE_FIELDS);
|
|
134
|
+
const TEMPLATE_KNOWN = new Set(KNOWN_TEMPLATE_FIELDS);
|
|
135
|
+
const AUTH_CONTEXT_KNOWN = new Set(KNOWN_AUTH_CONTEXT_FIELDS);
|
|
136
|
+
const DELETED_POLICY_KNOWN = new Set(KNOWN_DELETED_POLICY_FIELDS);
|
|
137
|
+
const DELETED_LOCATION_KNOWN = new Set(KNOWN_DELETED_LOCATION_FIELDS);
|
|
46
138
|
// @odata.type is preserved on named-location rows without being selectable:
|
|
47
139
|
// it names the location kind (ipNamedLocation versus countryNamedLocation).
|
|
48
140
|
const LOCATION_TYPE_PROPERTY = "@odata.type";
|
|
@@ -57,8 +149,44 @@ const DEFAULT_POLICY_SHOW_SELECT = [...KNOWN_POLICY_FIELDS];
|
|
|
57
149
|
const DEFAULT_LOCATION_LIST_SELECT = ["id", "displayName"];
|
|
58
150
|
// Show rows: the full reviewed location set.
|
|
59
151
|
const DEFAULT_LOCATION_SHOW_SELECT = [...KNOWN_LOCATION_FIELDS];
|
|
60
|
-
//
|
|
152
|
+
// Compact strength rows: identifier, name and the built-in/custom kind.
|
|
153
|
+
const DEFAULT_STRENGTH_LIST_SELECT = ["id", "displayName", "policyType"];
|
|
154
|
+
// Fetch the full reviewed strength set for inspection.
|
|
155
|
+
const DEFAULT_STRENGTH_SHOW_SELECT = [...KNOWN_STRENGTH_FIELDS];
|
|
156
|
+
// Compact combination rows: the identifier; the kind rides as @odata.type.
|
|
157
|
+
const DEFAULT_COMBO_LIST_SELECT = ["id"];
|
|
158
|
+
// Show rows: the full reviewed base set.
|
|
159
|
+
const DEFAULT_COMBO_SHOW_SELECT = [...KNOWN_COMBO_FIELDS];
|
|
160
|
+
// Compact mode rows: identifier and name.
|
|
161
|
+
const DEFAULT_MODE_LIST_SELECT = ["id", "displayName"];
|
|
162
|
+
// Show rows: the full reviewed mode set.
|
|
163
|
+
const DEFAULT_MODE_SHOW_SELECT = [...KNOWN_MODE_FIELDS];
|
|
164
|
+
// Compact template rows: identifier and name.
|
|
165
|
+
const DEFAULT_TEMPLATE_LIST_SELECT = ["id", "name"];
|
|
166
|
+
// Show rows: the full reviewed template set.
|
|
167
|
+
const DEFAULT_TEMPLATE_SHOW_SELECT = [...KNOWN_TEMPLATE_FIELDS];
|
|
168
|
+
// Compact authentication-context rows: identifier and name.
|
|
169
|
+
const DEFAULT_AUTH_CONTEXT_LIST_SELECT = ["id", "displayName"];
|
|
170
|
+
// Show rows: the full reviewed authentication-context set.
|
|
171
|
+
const DEFAULT_AUTH_CONTEXT_SHOW_SELECT = [...KNOWN_AUTH_CONTEXT_FIELDS];
|
|
172
|
+
// Compact deleted-policy rows: identifier, name, enforcement state and the
|
|
173
|
+
// soft-delete timestamp, which is the point of the deleted collection.
|
|
174
|
+
const DEFAULT_DELETED_POLICY_LIST_SELECT = ["id", "displayName", "state", "deletedDateTime"];
|
|
175
|
+
// Show rows: the full reviewed deleted-policy set.
|
|
176
|
+
const DEFAULT_DELETED_POLICY_SHOW_SELECT = [...KNOWN_DELETED_POLICY_FIELDS];
|
|
177
|
+
// Compact deleted-location rows: identifier, name and deletion timestamp;
|
|
178
|
+
// the kind rides as @odata.type.
|
|
179
|
+
const DEFAULT_DELETED_LOCATION_LIST_SELECT = ["id", "displayName", "deletedDateTime"];
|
|
180
|
+
// Show rows: the full reviewed deleted-location set.
|
|
181
|
+
const DEFAULT_DELETED_LOCATION_SHOW_SELECT = [...KNOWN_DELETED_LOCATION_FIELDS];
|
|
182
|
+
// Policy.Read.All covers policies, named locations and templates in both modes.
|
|
61
183
|
export const DEFAULT_DELEGATED_SCOPES = ["https://graph.microsoft.com/Policy.Read.All"];
|
|
184
|
+
// Policy.Read.AuthenticationMethod is the least-privileged read for the
|
|
185
|
+
// authentication-strength family in both modes.
|
|
186
|
+
export const DEFAULT_STRENGTH_SCOPES = ["https://graph.microsoft.com/Policy.Read.AuthenticationMethod"];
|
|
187
|
+
// AuthenticationContext.Read.All is the least-privileged read for the
|
|
188
|
+
// authentication context class references in both modes.
|
|
189
|
+
export const DEFAULT_AUTH_CONTEXT_SCOPES = ["https://graph.microsoft.com/AuthenticationContext.Read.All"];
|
|
62
190
|
const TRUNCATE_AT = 500;
|
|
63
191
|
function fieldList(raw, flag, known, knownList, noun, help) {
|
|
64
192
|
const parts = String(raw)
|
|
@@ -80,7 +208,7 @@ function fieldList(raw, flag, known, knownList, noun, help) {
|
|
|
80
208
|
}
|
|
81
209
|
return fields;
|
|
82
210
|
}
|
|
83
|
-
function scopesFor(flags, profile, help) {
|
|
211
|
+
function scopesFor(flags, profile, help, defaults = DEFAULT_DELEGATED_SCOPES) {
|
|
84
212
|
if (profile.mode === "application") {
|
|
85
213
|
if (flags.scopes !== undefined) {
|
|
86
214
|
throw new AxiError("Application profiles use the configured Graph .default audience; delegated scopes are unavailable", "VALIDATION_ERROR", [help, "mg-axi profile show --profile <name>"]);
|
|
@@ -88,7 +216,7 @@ function scopesFor(flags, profile, help) {
|
|
|
88
216
|
return undefined;
|
|
89
217
|
}
|
|
90
218
|
if (flags.scopes === undefined)
|
|
91
|
-
return [...
|
|
219
|
+
return [...defaults];
|
|
92
220
|
const scopes = String(flags.scopes)
|
|
93
221
|
.split(",")
|
|
94
222
|
.map(scope => scope.trim())
|
|
@@ -179,13 +307,48 @@ const LOCATION_DENIAL_HINTS = [
|
|
|
179
307
|
"Named-location reads need Policy.Read.All plus a supported directory role: Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader for delegated access, or admin-consented Policy.Read.All for application access",
|
|
180
308
|
"Conditional Access and named locations need P1; risk-based Conditional Access needs P2",
|
|
181
309
|
];
|
|
310
|
+
// The authentication-strength family (strength policies, combination
|
|
311
|
+
// configurations, authentication method modes) reads the least-privileged
|
|
312
|
+
// Policy.Read.AuthenticationMethod instead of Policy.Read.All, with the
|
|
313
|
+
// narrower Conditional Access Administrator / Security Administrator /
|
|
314
|
+
// Security Reader role set.
|
|
315
|
+
const STRENGTH_DENIAL_HINTS = [
|
|
316
|
+
"Authentication-strength reads need Policy.Read.AuthenticationMethod plus a supported directory role: Conditional Access Administrator, Security Administrator or Security Reader for delegated access, or admin-consented Policy.Read.AuthenticationMethod for application access",
|
|
317
|
+
"Conditional Access needs P1",
|
|
318
|
+
];
|
|
319
|
+
const TEMPLATE_DENIAL_HINTS = [
|
|
320
|
+
"Conditional Access template reads need Policy.Read.All plus a supported directory role: Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader for delegated access, or admin-consented Policy.Read.All for application access",
|
|
321
|
+
"Conditional Access needs P1",
|
|
322
|
+
];
|
|
323
|
+
// Authentication-context reads carry the least-privileged
|
|
324
|
+
// AuthenticationContext.Read.All with the broad Conditional Access role
|
|
325
|
+
// set shared with policies, named locations and templates.
|
|
326
|
+
const AUTH_CONTEXT_DENIAL_HINTS = [
|
|
327
|
+
"Authentication-context reads need AuthenticationContext.Read.All plus a supported directory role: Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader for delegated access, or admin-consented AuthenticationContext.Read.All for application access",
|
|
328
|
+
"Conditional Access needs P1",
|
|
329
|
+
];
|
|
330
|
+
// Deleted policy and named-location reads keep Policy.Read.All with the
|
|
331
|
+
// broad Conditional Access role set; restore is a beta-only mutation, so
|
|
332
|
+
// denial guidance never offers it as a next step.
|
|
333
|
+
const DELETED_POLICY_DENIAL_HINTS = [
|
|
334
|
+
"Deleted-policy reads need Policy.Read.All plus a supported directory role: Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader for delegated access, or admin-consented Policy.Read.All for application access",
|
|
335
|
+
"Deleted policies restore within 30 days; restore is a beta-only mutation outside mg-axi scope",
|
|
336
|
+
"Conditional Access needs P1",
|
|
337
|
+
];
|
|
338
|
+
const DELETED_LOCATION_DENIAL_HINTS = [
|
|
339
|
+
"Deleted named-location reads need Policy.Read.All plus a supported directory role: Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader for delegated access, or admin-consented Policy.Read.All for application access",
|
|
340
|
+
"Deleted named locations restore within 30 days; restore is a beta-only mutation outside mg-axi scope",
|
|
341
|
+
"Conditional Access and named locations need P1",
|
|
342
|
+
];
|
|
182
343
|
const POLICY_LIST = {
|
|
183
344
|
command: "entra conditional-access policy list",
|
|
184
345
|
key: "policies",
|
|
185
346
|
noun: "policy",
|
|
347
|
+
plural: "policies",
|
|
186
348
|
known: POLICY_KNOWN,
|
|
187
349
|
knownList: KNOWN_POLICY_FIELDS,
|
|
188
350
|
defaultSelect: DEFAULT_POLICY_LIST_SELECT,
|
|
351
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
189
352
|
denialHints: POLICY_DENIAL_HINTS,
|
|
190
353
|
showHint: "mg-axi entra conditional-access policy show --id <policy-id>",
|
|
191
354
|
emptyHint: "0 conditional-access policies matched; the absence of results is the answer, not an error",
|
|
@@ -195,9 +358,11 @@ const LOCATION_LIST = {
|
|
|
195
358
|
command: "entra conditional-access named-location list",
|
|
196
359
|
key: "namedLocations",
|
|
197
360
|
noun: "named-location",
|
|
361
|
+
plural: "named locations",
|
|
198
362
|
known: LOCATION_KNOWN,
|
|
199
363
|
knownList: KNOWN_LOCATION_FIELDS,
|
|
200
364
|
defaultSelect: DEFAULT_LOCATION_LIST_SELECT,
|
|
365
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
201
366
|
denialHints: LOCATION_DENIAL_HINTS,
|
|
202
367
|
showHint: "mg-axi entra conditional-access named-location show --id <named-location-id>",
|
|
203
368
|
emptyHint: "0 named locations matched; the absence of results is the answer, not an error",
|
|
@@ -209,12 +374,19 @@ async function listCollection(shape, session, flags, profile, operation, help, p
|
|
|
209
374
|
throw new AxiError("--cursor needs the opaque cursor from a partial result", "VALIDATION_ERROR", [help]);
|
|
210
375
|
const savedSelect = cursor === undefined ? undefined : session.cursorQuery(operation, cursor).$select;
|
|
211
376
|
const { select, fields } = selectedFields(flags, savedSelect === undefined ? shape.defaultSelect : fieldList(savedSelect, "select", shape.known, shape.knownList, shape.noun, help), shape.known, shape.knownList, shape.noun, help);
|
|
212
|
-
const scopes = scopesFor(flags, profile, help);
|
|
377
|
+
const scopes = scopesFor(flags, profile, help, shape.defaultScopes);
|
|
213
378
|
const full = flags.full === true;
|
|
214
379
|
const query = { $select: select.join(",") };
|
|
215
380
|
if (flags.filter !== undefined)
|
|
216
381
|
query.$filter = String(flags.filter);
|
|
217
382
|
const args = { profile, operation, query, scopes };
|
|
383
|
+
if (shape.parent !== undefined) {
|
|
384
|
+
const raw = flags[shape.parent.flag];
|
|
385
|
+
if (raw === undefined || !String(raw).trim()) {
|
|
386
|
+
throw new AxiError(`--${shape.parent.flag} needs the ${shape.parent.label}`, "VALIDATION_ERROR", [help]);
|
|
387
|
+
}
|
|
388
|
+
args.params = { [shape.parent.param]: String(raw) };
|
|
389
|
+
}
|
|
218
390
|
if (cursor !== undefined)
|
|
219
391
|
args.cursor = cursor;
|
|
220
392
|
if (flags.all === true) {
|
|
@@ -242,23 +414,25 @@ async function listCollection(shape, session, flags, profile, operation, help, p
|
|
|
242
414
|
if (!result.complete) {
|
|
243
415
|
return {
|
|
244
416
|
[shape.key]: rows,
|
|
245
|
-
|
|
417
|
+
...listTotals(rows.length, result.total, shape.plural, false),
|
|
418
|
+
complete: false,
|
|
419
|
+
reason: result.reason,
|
|
246
420
|
cursor: result.cursor,
|
|
247
421
|
help: [...truncationHints, `Resume losslessly with the same flags plus --cursor - ${profileHint(profileName)} and supply the returned cursor on stdin`, showHint],
|
|
248
422
|
};
|
|
249
423
|
}
|
|
250
|
-
const count = { returned: rows.length, complete: true };
|
|
251
424
|
if (!rows.length) {
|
|
252
425
|
return {
|
|
253
426
|
[shape.key]: rows,
|
|
254
|
-
|
|
427
|
+
...listTotals(rows.length, result.total, shape.plural, true),
|
|
428
|
+
complete: true,
|
|
255
429
|
help: [
|
|
256
430
|
`mg-axi ${shape.command} --filter <odata-filter> ${profileHint(profileName)}`,
|
|
257
431
|
shape.emptyHint,
|
|
258
432
|
],
|
|
259
433
|
};
|
|
260
434
|
}
|
|
261
|
-
return { [shape.key]: rows,
|
|
435
|
+
return { [shape.key]: rows, ...listTotals(rows.length, result.total, shape.plural, true), complete: true, help: [...truncationHints, showHint] };
|
|
262
436
|
}
|
|
263
437
|
const POLICY_SHOW = {
|
|
264
438
|
command: "entra conditional-access policy show",
|
|
@@ -268,6 +442,7 @@ const POLICY_SHOW = {
|
|
|
268
442
|
known: POLICY_KNOWN,
|
|
269
443
|
knownList: KNOWN_POLICY_FIELDS,
|
|
270
444
|
defaultSelect: DEFAULT_POLICY_SHOW_SELECT,
|
|
445
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
271
446
|
denialHints: POLICY_DENIAL_HINTS,
|
|
272
447
|
preserveType: false,
|
|
273
448
|
};
|
|
@@ -279,17 +454,26 @@ const LOCATION_SHOW = {
|
|
|
279
454
|
known: LOCATION_KNOWN,
|
|
280
455
|
knownList: KNOWN_LOCATION_FIELDS,
|
|
281
456
|
defaultSelect: DEFAULT_LOCATION_SHOW_SELECT,
|
|
457
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
282
458
|
denialHints: LOCATION_DENIAL_HINTS,
|
|
283
459
|
preserveType: true,
|
|
284
460
|
};
|
|
285
461
|
async function showSingle(shape, session, flags, profile, operation, help, profileName) {
|
|
286
462
|
const { select, fields } = selectedFields(flags, shape.defaultSelect, shape.known, shape.knownList, shape.noun, help);
|
|
287
|
-
const scopes = scopesFor(flags, profile, help);
|
|
463
|
+
const scopes = scopesFor(flags, profile, help, shape.defaultScopes);
|
|
288
464
|
const full = flags.full === true;
|
|
465
|
+
const params = { [shape.param]: String(flags.id) };
|
|
466
|
+
if (shape.parent !== undefined) {
|
|
467
|
+
const raw = flags[shape.parent.flag];
|
|
468
|
+
if (raw === undefined || !String(raw).trim()) {
|
|
469
|
+
throw new AxiError(`--${shape.parent.flag} needs the ${shape.parent.label}`, "VALIDATION_ERROR", [help]);
|
|
470
|
+
}
|
|
471
|
+
params[shape.parent.param] = String(raw);
|
|
472
|
+
}
|
|
289
473
|
const raw = await withGuidance(shape.denialHints, () => session.execute({
|
|
290
474
|
profile,
|
|
291
475
|
operation,
|
|
292
|
-
params
|
|
476
|
+
params,
|
|
293
477
|
query: { $select: select.join(",") },
|
|
294
478
|
scopes,
|
|
295
479
|
}));
|
|
@@ -315,3 +499,229 @@ export async function listNamedLocations(session, flags, profile, operation, hel
|
|
|
315
499
|
export async function showNamedLocation(session, flags, profile, operation, help, profileName) {
|
|
316
500
|
return showSingle(LOCATION_SHOW, session, flags, profile, operation, help, profileName);
|
|
317
501
|
}
|
|
502
|
+
const STRENGTH_LIST = {
|
|
503
|
+
command: "entra conditional-access auth-strength-policy list",
|
|
504
|
+
key: "authStrengthPolicies",
|
|
505
|
+
noun: "auth-strength-policy",
|
|
506
|
+
known: STRENGTH_KNOWN,
|
|
507
|
+
knownList: KNOWN_STRENGTH_FIELDS,
|
|
508
|
+
defaultSelect: DEFAULT_STRENGTH_LIST_SELECT,
|
|
509
|
+
defaultScopes: DEFAULT_STRENGTH_SCOPES,
|
|
510
|
+
denialHints: STRENGTH_DENIAL_HINTS,
|
|
511
|
+
showHint: "mg-axi entra conditional-access auth-strength-policy show --id <auth-strength-policy-id>",
|
|
512
|
+
emptyHint: "0 authentication-strength policies matched; the absence of results is the answer, not an error",
|
|
513
|
+
preserveType: false,
|
|
514
|
+
plural: "auth-strength policies",
|
|
515
|
+
};
|
|
516
|
+
const COMBO_LIST = {
|
|
517
|
+
command: "entra conditional-access combination-configuration list",
|
|
518
|
+
key: "combinationConfigurations",
|
|
519
|
+
noun: "combination-configuration",
|
|
520
|
+
known: COMBO_KNOWN,
|
|
521
|
+
knownList: KNOWN_COMBO_FIELDS,
|
|
522
|
+
defaultSelect: DEFAULT_COMBO_LIST_SELECT,
|
|
523
|
+
defaultScopes: DEFAULT_STRENGTH_SCOPES,
|
|
524
|
+
denialHints: STRENGTH_DENIAL_HINTS,
|
|
525
|
+
showHint: "mg-axi entra conditional-access combination-configuration show --policy <auth-strength-policy-id> --id <combination-configuration-id>",
|
|
526
|
+
emptyHint: "0 combination configurations matched; the absence of results is the answer, not an error",
|
|
527
|
+
preserveType: true,
|
|
528
|
+
parent: { param: "authenticationStrengthPolicy-id", flag: "policy", label: "authentication-strength policy ID owning the combination configurations" },
|
|
529
|
+
plural: "combination configurations",
|
|
530
|
+
};
|
|
531
|
+
const MODE_LIST = {
|
|
532
|
+
command: "entra conditional-access auth-method-mode list",
|
|
533
|
+
key: "authMethodModes",
|
|
534
|
+
noun: "auth-method-mode",
|
|
535
|
+
known: MODE_KNOWN,
|
|
536
|
+
knownList: KNOWN_MODE_FIELDS,
|
|
537
|
+
defaultSelect: DEFAULT_MODE_LIST_SELECT,
|
|
538
|
+
defaultScopes: DEFAULT_STRENGTH_SCOPES,
|
|
539
|
+
denialHints: STRENGTH_DENIAL_HINTS,
|
|
540
|
+
showHint: "mg-axi entra conditional-access auth-method-mode show --id <auth-method-mode-id>",
|
|
541
|
+
emptyHint: "0 authentication method modes matched; the absence of results is the answer, not an error",
|
|
542
|
+
preserveType: false,
|
|
543
|
+
plural: "auth-method modes",
|
|
544
|
+
};
|
|
545
|
+
const TEMPLATE_LIST = {
|
|
546
|
+
command: "entra conditional-access template list",
|
|
547
|
+
key: "templates",
|
|
548
|
+
noun: "template",
|
|
549
|
+
known: TEMPLATE_KNOWN,
|
|
550
|
+
knownList: KNOWN_TEMPLATE_FIELDS,
|
|
551
|
+
defaultSelect: DEFAULT_TEMPLATE_LIST_SELECT,
|
|
552
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
553
|
+
denialHints: TEMPLATE_DENIAL_HINTS,
|
|
554
|
+
showHint: "mg-axi entra conditional-access template show --id <template-id>",
|
|
555
|
+
emptyHint: "0 Conditional Access templates matched; the absence of results is the answer, not an error",
|
|
556
|
+
preserveType: false,
|
|
557
|
+
plural: "templates",
|
|
558
|
+
};
|
|
559
|
+
const STRENGTH_SHOW = {
|
|
560
|
+
command: "entra conditional-access auth-strength-policy show",
|
|
561
|
+
key: "authStrengthPolicy",
|
|
562
|
+
param: "authenticationStrengthPolicy-id",
|
|
563
|
+
noun: "auth-strength-policy",
|
|
564
|
+
known: STRENGTH_KNOWN,
|
|
565
|
+
knownList: KNOWN_STRENGTH_FIELDS,
|
|
566
|
+
defaultSelect: DEFAULT_STRENGTH_SHOW_SELECT,
|
|
567
|
+
defaultScopes: DEFAULT_STRENGTH_SCOPES,
|
|
568
|
+
denialHints: STRENGTH_DENIAL_HINTS,
|
|
569
|
+
preserveType: false,
|
|
570
|
+
};
|
|
571
|
+
const COMBO_SHOW = {
|
|
572
|
+
command: "entra conditional-access combination-configuration show",
|
|
573
|
+
key: "combinationConfiguration",
|
|
574
|
+
param: "authenticationCombinationConfiguration-id",
|
|
575
|
+
noun: "combination-configuration",
|
|
576
|
+
known: COMBO_KNOWN,
|
|
577
|
+
knownList: KNOWN_COMBO_FIELDS,
|
|
578
|
+
defaultSelect: DEFAULT_COMBO_SHOW_SELECT,
|
|
579
|
+
defaultScopes: DEFAULT_STRENGTH_SCOPES,
|
|
580
|
+
denialHints: STRENGTH_DENIAL_HINTS,
|
|
581
|
+
preserveType: true,
|
|
582
|
+
parent: { param: "authenticationStrengthPolicy-id", flag: "policy", label: "authentication-strength policy ID owning the combination configuration" },
|
|
583
|
+
};
|
|
584
|
+
const MODE_SHOW = {
|
|
585
|
+
command: "entra conditional-access auth-method-mode show",
|
|
586
|
+
key: "authMethodMode",
|
|
587
|
+
param: "authenticationMethodModeDetail-id",
|
|
588
|
+
noun: "auth-method-mode",
|
|
589
|
+
known: MODE_KNOWN,
|
|
590
|
+
knownList: KNOWN_MODE_FIELDS,
|
|
591
|
+
defaultSelect: DEFAULT_MODE_SHOW_SELECT,
|
|
592
|
+
defaultScopes: DEFAULT_STRENGTH_SCOPES,
|
|
593
|
+
denialHints: STRENGTH_DENIAL_HINTS,
|
|
594
|
+
preserveType: false,
|
|
595
|
+
};
|
|
596
|
+
const TEMPLATE_SHOW = {
|
|
597
|
+
command: "entra conditional-access template show",
|
|
598
|
+
key: "template",
|
|
599
|
+
param: "conditionalAccessTemplate-id",
|
|
600
|
+
noun: "template",
|
|
601
|
+
known: TEMPLATE_KNOWN,
|
|
602
|
+
knownList: KNOWN_TEMPLATE_FIELDS,
|
|
603
|
+
defaultSelect: DEFAULT_TEMPLATE_SHOW_SELECT,
|
|
604
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
605
|
+
denialHints: TEMPLATE_DENIAL_HINTS,
|
|
606
|
+
preserveType: false,
|
|
607
|
+
};
|
|
608
|
+
export async function listAuthStrengthPolicies(session, flags, profile, operation, help, profileName) {
|
|
609
|
+
return listCollection(STRENGTH_LIST, session, flags, profile, operation, help, profileName);
|
|
610
|
+
}
|
|
611
|
+
export async function showAuthStrengthPolicy(session, flags, profile, operation, help, profileName) {
|
|
612
|
+
return showSingle(STRENGTH_SHOW, session, flags, profile, operation, help, profileName);
|
|
613
|
+
}
|
|
614
|
+
export async function listCombinationConfigurations(session, flags, profile, operation, help, profileName) {
|
|
615
|
+
return listCollection(COMBO_LIST, session, flags, profile, operation, help, profileName);
|
|
616
|
+
}
|
|
617
|
+
export async function showCombinationConfiguration(session, flags, profile, operation, help, profileName) {
|
|
618
|
+
return showSingle(COMBO_SHOW, session, flags, profile, operation, help, profileName);
|
|
619
|
+
}
|
|
620
|
+
export async function listAuthMethodModes(session, flags, profile, operation, help, profileName) {
|
|
621
|
+
return listCollection(MODE_LIST, session, flags, profile, operation, help, profileName);
|
|
622
|
+
}
|
|
623
|
+
export async function showAuthMethodMode(session, flags, profile, operation, help, profileName) {
|
|
624
|
+
return showSingle(MODE_SHOW, session, flags, profile, operation, help, profileName);
|
|
625
|
+
}
|
|
626
|
+
export async function listTemplates(session, flags, profile, operation, help, profileName) {
|
|
627
|
+
return listCollection(TEMPLATE_LIST, session, flags, profile, operation, help, profileName);
|
|
628
|
+
}
|
|
629
|
+
export async function showTemplate(session, flags, profile, operation, help, profileName) {
|
|
630
|
+
return showSingle(TEMPLATE_SHOW, session, flags, profile, operation, help, profileName);
|
|
631
|
+
}
|
|
632
|
+
const AUTH_CONTEXT_LIST = {
|
|
633
|
+
command: "entra conditional-access auth-context list",
|
|
634
|
+
key: "authContexts",
|
|
635
|
+
noun: "auth-context",
|
|
636
|
+
plural: "auth contexts",
|
|
637
|
+
known: AUTH_CONTEXT_KNOWN,
|
|
638
|
+
knownList: KNOWN_AUTH_CONTEXT_FIELDS,
|
|
639
|
+
defaultSelect: DEFAULT_AUTH_CONTEXT_LIST_SELECT,
|
|
640
|
+
defaultScopes: DEFAULT_AUTH_CONTEXT_SCOPES,
|
|
641
|
+
denialHints: AUTH_CONTEXT_DENIAL_HINTS,
|
|
642
|
+
showHint: "mg-axi entra conditional-access auth-context show --id <auth-context-id>",
|
|
643
|
+
emptyHint: "0 authentication context class references matched; the absence of results is the answer, not an error",
|
|
644
|
+
preserveType: false,
|
|
645
|
+
};
|
|
646
|
+
const DELETED_POLICY_LIST = {
|
|
647
|
+
command: "entra conditional-access deleted-policy list",
|
|
648
|
+
key: "deletedPolicies",
|
|
649
|
+
noun: "deleted-policy",
|
|
650
|
+
plural: "deleted policies",
|
|
651
|
+
known: DELETED_POLICY_KNOWN,
|
|
652
|
+
knownList: KNOWN_DELETED_POLICY_FIELDS,
|
|
653
|
+
defaultSelect: DEFAULT_DELETED_POLICY_LIST_SELECT,
|
|
654
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
655
|
+
denialHints: DELETED_POLICY_DENIAL_HINTS,
|
|
656
|
+
showHint: "mg-axi entra conditional-access deleted-policy show --id <deleted-policy-id>",
|
|
657
|
+
emptyHint: "0 deleted policies matched; the absence of results is the answer, not an error",
|
|
658
|
+
preserveType: false,
|
|
659
|
+
};
|
|
660
|
+
const DELETED_LOCATION_LIST = {
|
|
661
|
+
command: "entra conditional-access deleted-named-location list",
|
|
662
|
+
key: "deletedNamedLocations",
|
|
663
|
+
noun: "deleted-named-location",
|
|
664
|
+
plural: "deleted named locations",
|
|
665
|
+
known: DELETED_LOCATION_KNOWN,
|
|
666
|
+
knownList: KNOWN_DELETED_LOCATION_FIELDS,
|
|
667
|
+
defaultSelect: DEFAULT_DELETED_LOCATION_LIST_SELECT,
|
|
668
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
669
|
+
denialHints: DELETED_LOCATION_DENIAL_HINTS,
|
|
670
|
+
showHint: "mg-axi entra conditional-access deleted-named-location show --id <deleted-named-location-id>",
|
|
671
|
+
emptyHint: "0 deleted named locations matched; the absence of results is the answer, not an error",
|
|
672
|
+
preserveType: true,
|
|
673
|
+
};
|
|
674
|
+
const AUTH_CONTEXT_SHOW = {
|
|
675
|
+
command: "entra conditional-access auth-context show",
|
|
676
|
+
key: "authContext",
|
|
677
|
+
param: "authenticationContextClassReference-id",
|
|
678
|
+
noun: "auth-context",
|
|
679
|
+
known: AUTH_CONTEXT_KNOWN,
|
|
680
|
+
knownList: KNOWN_AUTH_CONTEXT_FIELDS,
|
|
681
|
+
defaultSelect: DEFAULT_AUTH_CONTEXT_SHOW_SELECT,
|
|
682
|
+
defaultScopes: DEFAULT_AUTH_CONTEXT_SCOPES,
|
|
683
|
+
denialHints: AUTH_CONTEXT_DENIAL_HINTS,
|
|
684
|
+
preserveType: false,
|
|
685
|
+
};
|
|
686
|
+
const DELETED_POLICY_SHOW = {
|
|
687
|
+
command: "entra conditional-access deleted-policy show",
|
|
688
|
+
key: "deletedPolicy",
|
|
689
|
+
param: "conditionalAccessPolicy-id",
|
|
690
|
+
noun: "deleted-policy",
|
|
691
|
+
known: DELETED_POLICY_KNOWN,
|
|
692
|
+
knownList: KNOWN_DELETED_POLICY_FIELDS,
|
|
693
|
+
defaultSelect: DEFAULT_DELETED_POLICY_SHOW_SELECT,
|
|
694
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
695
|
+
denialHints: DELETED_POLICY_DENIAL_HINTS,
|
|
696
|
+
preserveType: false,
|
|
697
|
+
};
|
|
698
|
+
const DELETED_LOCATION_SHOW = {
|
|
699
|
+
command: "entra conditional-access deleted-named-location show",
|
|
700
|
+
key: "deletedNamedLocation",
|
|
701
|
+
param: "namedLocation-id",
|
|
702
|
+
noun: "deleted-named-location",
|
|
703
|
+
known: DELETED_LOCATION_KNOWN,
|
|
704
|
+
knownList: KNOWN_DELETED_LOCATION_FIELDS,
|
|
705
|
+
defaultSelect: DEFAULT_DELETED_LOCATION_SHOW_SELECT,
|
|
706
|
+
defaultScopes: DEFAULT_DELEGATED_SCOPES,
|
|
707
|
+
denialHints: DELETED_LOCATION_DENIAL_HINTS,
|
|
708
|
+
preserveType: true,
|
|
709
|
+
};
|
|
710
|
+
export async function listAuthContexts(session, flags, profile, operation, help, profileName) {
|
|
711
|
+
return listCollection(AUTH_CONTEXT_LIST, session, flags, profile, operation, help, profileName);
|
|
712
|
+
}
|
|
713
|
+
export async function showAuthContext(session, flags, profile, operation, help, profileName) {
|
|
714
|
+
return showSingle(AUTH_CONTEXT_SHOW, session, flags, profile, operation, help, profileName);
|
|
715
|
+
}
|
|
716
|
+
export async function listDeletedPolicies(session, flags, profile, operation, help, profileName) {
|
|
717
|
+
return listCollection(DELETED_POLICY_LIST, session, flags, profile, operation, help, profileName);
|
|
718
|
+
}
|
|
719
|
+
export async function showDeletedPolicy(session, flags, profile, operation, help, profileName) {
|
|
720
|
+
return showSingle(DELETED_POLICY_SHOW, session, flags, profile, operation, help, profileName);
|
|
721
|
+
}
|
|
722
|
+
export async function listDeletedNamedLocations(session, flags, profile, operation, help, profileName) {
|
|
723
|
+
return listCollection(DELETED_LOCATION_LIST, session, flags, profile, operation, help, profileName);
|
|
724
|
+
}
|
|
725
|
+
export async function showDeletedNamedLocation(session, flags, profile, operation, help, profileName) {
|
|
726
|
+
return showSingle(DELETED_LOCATION_SHOW, session, flags, profile, operation, help, profileName);
|
|
727
|
+
}
|
package/dist/entra-contacts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
import { listTotals } from "./list-totals.js";
|
|
2
3
|
// EXT-01 contacts subfamily plus EXT-01m/EXT-01n navigation: the read mapping
|
|
3
4
|
// behind `mg-axi entra contact list/show/count`, the manager/directReports
|
|
4
5
|
// navigation reads (`show-manager`, `list/show/count-direct-reports`) and the
|
|
@@ -290,23 +291,25 @@ export async function listContacts(session, flags, profile, operation, help, pro
|
|
|
290
291
|
if (!result.complete) {
|
|
291
292
|
return {
|
|
292
293
|
contacts,
|
|
293
|
-
|
|
294
|
+
...listTotals(contacts.length, result.total, "contacts", false),
|
|
295
|
+
complete: false,
|
|
296
|
+
reason: result.reason,
|
|
294
297
|
cursor: result.cursor,
|
|
295
298
|
help: [...truncationHints, resumeHint(profileName), showHint],
|
|
296
299
|
};
|
|
297
300
|
}
|
|
298
|
-
const count = { returned: contacts.length, complete: true };
|
|
299
301
|
if (!contacts.length) {
|
|
300
302
|
return {
|
|
301
303
|
contacts,
|
|
302
|
-
|
|
304
|
+
...listTotals(contacts.length, result.total, "contacts", true),
|
|
305
|
+
complete: true,
|
|
303
306
|
help: [
|
|
304
307
|
`mg-axi entra contact list --filter <odata-filter> ${profileHint(profileName)}`,
|
|
305
308
|
"0 contacts matched; the absence of results is the answer, not an error",
|
|
306
309
|
],
|
|
307
310
|
};
|
|
308
311
|
}
|
|
309
|
-
return { contacts,
|
|
312
|
+
return { contacts, ...listTotals(contacts.length, result.total, "contacts", true), complete: true, help: [...truncationHints, showHint] };
|
|
310
313
|
}
|
|
311
314
|
export async function showContact(session, flags, profile, operation, help, profileName) {
|
|
312
315
|
checkReadOperation(operation, help);
|
|
@@ -512,20 +515,22 @@ export async function listContactDirectReports(session, flags, profile, operatio
|
|
|
512
515
|
if (!result.complete) {
|
|
513
516
|
return {
|
|
514
517
|
directReports,
|
|
515
|
-
|
|
518
|
+
...listTotals(directReports.length, result.total, "direct reports", false),
|
|
519
|
+
complete: false,
|
|
520
|
+
reason: result.reason,
|
|
516
521
|
cursor: result.cursor,
|
|
517
522
|
help: [...truncationHints, ...limitedHints, resumeHint(profileName), showHint],
|
|
518
523
|
};
|
|
519
524
|
}
|
|
520
|
-
const count = { returned: directReports.length, complete: true };
|
|
521
525
|
if (!directReports.length) {
|
|
522
526
|
return {
|
|
523
527
|
directReports,
|
|
524
|
-
|
|
528
|
+
...listTotals(directReports.length, result.total, "direct reports", true),
|
|
529
|
+
complete: true,
|
|
525
530
|
help: [...limitedHints, "0 direct reports matched; the absence of results is the answer, not an error", showHint],
|
|
526
531
|
};
|
|
527
532
|
}
|
|
528
|
-
return { directReports,
|
|
533
|
+
return { directReports, ...listTotals(directReports.length, result.total, "direct reports", true), complete: true, help: [...truncationHints, ...limitedHints, showHint] };
|
|
529
534
|
}
|
|
530
535
|
export async function showContactDirectReport(session, flags, profile, operation, help, profileName) {
|
|
531
536
|
checkReadOperation(operation, help);
|
|
@@ -767,20 +772,22 @@ export async function listContactMemberOf(session, flags, profile, operation, he
|
|
|
767
772
|
if (!result.complete) {
|
|
768
773
|
return {
|
|
769
774
|
memberOf,
|
|
770
|
-
|
|
775
|
+
...listTotals(memberOf.length, result.total, "memberships", false),
|
|
776
|
+
complete: false,
|
|
777
|
+
reason: result.reason,
|
|
771
778
|
cursor: result.cursor,
|
|
772
779
|
help: [...truncationHints, ...limitedHints, resumeHint(profileName), scopeHint, showHint],
|
|
773
780
|
};
|
|
774
781
|
}
|
|
775
|
-
const count = { returned: memberOf.length, complete: true };
|
|
776
782
|
if (!memberOf.length) {
|
|
777
783
|
return {
|
|
778
784
|
memberOf,
|
|
779
|
-
|
|
785
|
+
...listTotals(memberOf.length, result.total, "memberships", true),
|
|
786
|
+
complete: true,
|
|
780
787
|
help: [...limitedHints, `0 ${noun} matched; the absence of results is the answer, not an error`, scopeHint, showHint],
|
|
781
788
|
};
|
|
782
789
|
}
|
|
783
|
-
return { memberOf,
|
|
790
|
+
return { memberOf, ...listTotals(memberOf.length, result.total, "memberships", true), complete: true, help: [...truncationHints, ...limitedHints, scopeHint, showHint] };
|
|
784
791
|
}
|
|
785
792
|
export async function showContactMemberOf(session, flags, profile, operation, help, profileName) {
|
|
786
793
|
checkReadOperation(operation, help);
|