Skip to content

Commit 6703d6a

Browse files
Migrates 'entra administrative unit get' to Zod
1 parent 2cb88d5 commit 6703d6a

File tree

2 files changed

+52
-76
lines changed

2 files changed

+52
-76
lines changed

src/m365/entra/commands/administrativeunit/administrativeunit-get.spec.ts

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from 'assert';
22
import sinon from "sinon";
3+
import { z } from 'zod';
34
import auth from '../../../../Auth.js';
45
import { CommandInfo } from "../../../../cli/CommandInfo.js";
56
import { Logger } from "../../../../cli/Logger.js";
@@ -19,6 +20,7 @@ describe(commands.ADMINISTRATIVEUNIT_GET, () => {
1920
let logger: Logger;
2021
let loggerLogSpy: sinon.SinonSpy;
2122
let commandInfo: CommandInfo;
23+
let commandOptionsSchema: z.ZodTypeAny;
2224
const administrativeUnitsReponse = {
2325
value: [
2426
{
@@ -43,6 +45,7 @@ describe(commands.ADMINISTRATIVEUNIT_GET, () => {
4345
sinon.stub(session, 'getId').returns('');
4446
auth.connection.active = true;
4547
commandInfo = cli.getCommandInfo(command);
48+
commandOptionsSchema = commandInfo.command.getSchemaToParse()!;
4649
});
4750

4851
beforeEach(() => {
@@ -82,6 +85,32 @@ describe(commands.ADMINISTRATIVEUNIT_GET, () => {
8285
assert.notStrictEqual(command.description, null);
8386
});
8487

88+
it('fails validation when id or displayName are not specified', () => {
89+
const actual = commandOptionsSchema.safeParse({});
90+
assert.strictEqual(actual.success, false);
91+
});
92+
93+
it('passes validation when id is specified', () => {
94+
const actual = commandOptionsSchema.safeParse({
95+
id: validId
96+
});
97+
assert.strictEqual(actual.success, true);
98+
});
99+
100+
it('passes validation when displayName is specified', () => {
101+
const actual = commandOptionsSchema.safeParse({
102+
displayName: validDisplayName
103+
});
104+
assert.strictEqual(actual.success, true);
105+
});
106+
107+
it('fails validation if the id is not a valid GUID', () => {
108+
const actual = commandOptionsSchema.safeParse({
109+
id: '123'
110+
});
111+
assert.strictEqual(actual.success, false);
112+
});
113+
85114
it('retrieves information about the specified administrative unit by id', async () => {
86115
sinon.stub(request, 'get').callsFake(async (opts) => {
87116
if (opts.url === `https://graph.microsoft.com/v1.0/directory/administrativeUnits/${validId}`) {
@@ -91,7 +120,7 @@ describe(commands.ADMINISTRATIVEUNIT_GET, () => {
91120
throw 'Invalid request';
92121
});
93122

94-
await command.action(logger, { options: { id: validId } });
123+
await command.action(logger, { options: commandOptionsSchema.parse({ id: validId }) });
95124
assert(loggerLogSpy.calledOnceWithExactly(administrativeUnitsReponse.value[0]));
96125
});
97126

@@ -104,36 +133,21 @@ describe(commands.ADMINISTRATIVEUNIT_GET, () => {
104133
throw 'Invalid request';
105134
});
106135

107-
await command.action(logger, { options: { id: validId, properties: 'id,displayName,visibility' } });
136+
await command.action(logger, { options: commandOptionsSchema.parse({ id: validId, properties: 'id,displayName,visibility' }) });
108137
assert(loggerLogSpy.calledOnceWithExactly(administrativeUnitsReponse.value[0]));
109138
});
110139

111140
it('retrieves information about the specified administrative unit by displayName', async () => {
112141
sinon.stub(entraAdministrativeUnit, 'getAdministrativeUnitByDisplayName').resolves(administrativeUnitsReponse.value[0]);
113142

114-
await command.action(logger, { options: { displayName: validDisplayName } });
143+
await command.action(logger, { options: commandOptionsSchema.parse({ displayName: validDisplayName }) });
115144
assert(loggerLogSpy.calledOnceWithExactly(administrativeUnitsReponse.value[0]));
116145
});
117146

118147
it('handles random API error', async () => {
119148
const errorMessage = 'Something went wrong';
120149
sinon.stub(request, 'get').rejects(new Error(errorMessage));
121150

122-
await assert.rejects(command.action(logger, { options: { id: validId } }), new CommandError(errorMessage));
123-
});
124-
125-
it('fails validation if the id is not a valid GUID', async () => {
126-
const actual = await command.validate({ options: { id: '123' } }, commandInfo);
127-
assert.notStrictEqual(actual, true);
128-
});
129-
130-
it('passes validation if the id is a valid GUID', async () => {
131-
const actual = await command.validate({ options: { id: validId } }, commandInfo);
132-
assert.strictEqual(actual, true);
133-
});
134-
135-
it('passes validation if required options specified (displayName)', async () => {
136-
const actual = await command.validate({ options: { displayName: validDisplayName } }, commandInfo);
137-
assert.strictEqual(actual, true);
151+
await assert.rejects(command.action(logger, { options: commandOptionsSchema.parse({ id: validId }) }), new CommandError(errorMessage));
138152
});
139153
});

src/m365/entra/commands/administrativeunit/administrativeunit-get.ts

Lines changed: 19 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
import { AdministrativeUnit } from "@microsoft/microsoft-graph-types";
2-
import GlobalOptions from "../../../../GlobalOptions.js";
2+
import { z } from 'zod';
33
import { Logger } from "../../../../cli/Logger.js";
4-
import { validation } from "../../../../utils/validation.js";
54
import request, { CliRequestOptions } from "../../../../request.js";
65
import GraphCommand from "../../../base/GraphCommand.js";
76
import commands from "../../commands.js";
87
import { entraAdministrativeUnit } from "../../../../utils/entraAdministrativeUnit.js";
8+
import { globalOptionsZod } from "../../../../Command.js";
9+
import { zod } from "../../../../utils/zod.js";
10+
11+
const options = globalOptionsZod
12+
.extend({
13+
id: zod.alias('i', z.string().uuid().optional()),
14+
displayName: zod.alias('n', z.string().optional()),
15+
properties: zod.alias('p', z.string().optional())
16+
})
17+
.strict();
18+
19+
declare type Options = z.infer<typeof options>;
920

1021
interface CommandArgs {
1122
options: Options;
1223
}
1324

14-
export interface Options extends GlobalOptions {
15-
id?: string;
16-
displayName?: string;
17-
properties?: string;
18-
}
19-
2025
class EntraAdministrativeUnitGetCommand extends GraphCommand {
2126
public get name(): string {
2227
return commands.ADMINISTRATIVEUNIT_GET;
@@ -26,58 +31,15 @@ class EntraAdministrativeUnitGetCommand extends GraphCommand {
2631
return 'Gets information about a specific administrative unit';
2732
}
2833

29-
constructor() {
30-
super();
31-
32-
this.#initTelemetry();
33-
this.#initOptions();
34-
this.#initValidators();
35-
this.#initOptionSets();
36-
this.#initTypes();
34+
public get schema(): z.ZodTypeAny | undefined {
35+
return options;
3736
}
3837

39-
#initTelemetry(): void {
40-
this.telemetry.push((args: CommandArgs) => {
41-
Object.assign(this.telemetryProperties, {
42-
id: typeof args.options.id !== 'undefined',
43-
displayName: typeof args.options.displayName !== 'undefined',
44-
properties: typeof args.options.properties !== 'undefined'
38+
public getRefinedSchema(schema: typeof options): z.ZodEffects<any> | undefined {
39+
return schema
40+
.refine(options => [options.id, options.displayName].filter(Boolean).length === 1, {
41+
message: 'Specify either id or displayName'
4542
});
46-
});
47-
}
48-
49-
#initOptions(): void {
50-
this.options.unshift(
51-
{
52-
option: '-i, --id [id]'
53-
},
54-
{
55-
option: '-n, --displayName [displayName]'
56-
},
57-
{
58-
option: '-p, --properties [properties]'
59-
}
60-
);
61-
}
62-
63-
#initValidators(): void {
64-
this.validators.push(
65-
async (args: CommandArgs) => {
66-
if (args.options.id && !validation.isValidGuid(args.options.id as string)) {
67-
return `${args.options.id} is not a valid GUID`;
68-
}
69-
70-
return true;
71-
}
72-
);
73-
}
74-
75-
#initOptionSets(): void {
76-
this.optionSets.push({ options: ['id', 'displayName'] });
77-
}
78-
79-
#initTypes(): void {
80-
this.types.string.push('displayName');
8143
}
8244

8345
public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {

0 commit comments

Comments
 (0)