Skip to content

Migrate 'entra administrativeunit add' to Zod #6848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import assert from 'assert';
import sinon from 'sinon';
import { z } from 'zod';
import auth from '../../../../Auth.js';
import { cli } from '../../../../cli/cli.js';
import { CommandInfo } from '../../../../cli/CommandInfo.js';
import commands from '../../commands.js';
import command from './administrativeunit-add.js';
import { Logger } from '../../../../cli/Logger.js';
import { CommandError } from '../../../../Command.js';
import request from '../../../../request.js';
import { telemetry } from '../../../../telemetry.js';
import { pid } from '../../../../utils/pid.js';
import { session } from '../../../../utils/session.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import request from '../../../../request.js';
import { Logger } from '../../../../cli/Logger.js';
import { CommandError } from '../../../../Command.js';
import commands from '../../commands.js';
import command from './administrativeunit-add.js';

describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
const administrativeUnitReponse: any = {
Expand All @@ -33,6 +34,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;
let commandOptionsSchema: z.ZodTypeAny;

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
Expand All @@ -41,6 +43,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
sinon.stub(session, 'getId').returns('');
auth.connection.active = true;
commandInfo = cli.getCommandInfo(command);
commandOptionsSchema = commandInfo.command.getSchemaToParse()!;
});

beforeEach(() => {
Expand Down Expand Up @@ -83,6 +86,27 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
assert.strictEqual(command.allowUnknownOptions(), true);
});

it('fails validation when displayName is not specified', () => {
const actual = commandOptionsSchema.safeParse({});
assert.strictEqual(actual.success, false);
});

it('passes validation when displayName is specified', () => {
const actual = commandOptionsSchema.safeParse({
displayName: 'European Division'
});
assert.strictEqual(actual.success, true);
});

it('passes validation when displayName, description and hiddenMembership are specified', () => {
const actual = commandOptionsSchema.safeParse({
displayName: 'European Division',
description: 'European Division Administration',
hiddenMembership: true
});
assert.strictEqual(actual.success, true);
});

it('creates an administrative unit with a specific display name', async () => {
const postStub = sinon.stub(request, 'post').callsFake(async (opts) => {
if (opts.url === 'https://graph.microsoft.com/v1.0/directory/administrativeUnits') {
Expand All @@ -92,7 +116,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
throw 'Invalid request';
});

await command.action(logger, { options: { displayName: 'European Division' } });
await command.action(logger, { options: commandOptionsSchema.parse({ displayName: 'European Division' }) });
assert.deepStrictEqual(postStub.lastCall.args[0].data, {
displayName: 'European Division',
description: undefined,
Expand All @@ -113,7 +137,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
throw 'Invalid request';
});

await command.action(logger, { options: { displayName: 'European Division', description: 'European Division Administration' } });
await command.action(logger, { options: commandOptionsSchema.parse({ displayName: 'European Division', description: 'European Division Administration' }) });
assert.deepStrictEqual(postStub.lastCall.args[0].data, {
displayName: 'European Division',
description: 'European Division Administration',
Expand All @@ -132,12 +156,11 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
});

await command.action(logger, {
options:
{
options: commandOptionsSchema.parse({
displayName: 'European Division',
description: 'European Division Administration',
extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker: 'JobGroupN'
}
})
});
assert.deepStrictEqual(postStub.lastCall.args[0].data, {
displayName: 'European Division',
Expand All @@ -161,7 +184,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
throw 'Invalid request';
});

await command.action(logger, { options: { displayName: 'European Division', description: 'European Division Administration', hiddenMembership: true } });
await command.action(logger, { options: commandOptionsSchema.parse({ displayName: 'European Division', description: 'European Division Administration', hiddenMembership: true }) });
assert.deepStrictEqual(postStub.lastCall.args[0].data, {
displayName: 'European Division',
description: 'European Division Administration',
Expand All @@ -182,17 +205,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => {
}
});

await assert.rejects(command.action(logger, { options: {} } as any), new CommandError('Invalid request'));
});

it('passes validation when only displayName is specified', async () => {
const actual = await command.validate({ options: { displayName: 'European Division' } }, commandInfo);
assert.strictEqual(actual, true);
});

it('passes validation when the displayName, description and hiddenMembership are specified', async () => {
const actual = await command.validate({ options: { displayName: 'European Division', description: 'European Division Administration', hiddenMembership: true } }, commandInfo);
assert.strictEqual(actual, true);
await assert.rejects(command.action(logger, { options: commandOptionsSchema.parse({ displayName: 'European Division' }) } as any), new CommandError('Invalid request'));
});

it('supports specifying displayName', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { AdministrativeUnit } from "@microsoft/microsoft-graph-types";
import GlobalOptions from "../../../../GlobalOptions.js";
import { z } from 'zod';
import { Logger } from "../../../../cli/Logger.js";
import { globalOptionsZod } from '../../../../Command.js';
import request, { CliRequestOptions } from "../../../../request.js";
import { zod } from '../../../../utils/zod.js';
import GraphCommand from "../../../base/GraphCommand.js";
import commands from "../../commands.js";

const options = globalOptionsZod
.extend({
displayName: zod.alias('n', z.string()),
description: zod.alias('d', z.string().optional()),
hiddenMembership: z.boolean().optional()
})
.passthrough();

declare type Options = z.infer<typeof options>;

interface CommandArgs {
options: Options;
}

interface Options extends GlobalOptions {
displayName: string;
description?: string;
hiddenMembership?: boolean;
}

class EntraAdministrativeUnitAddCommand extends GraphCommand {
public get name(): string {
return commands.ADMINISTRATIVEUNIT_ADD;
Expand All @@ -28,33 +34,8 @@ class EntraAdministrativeUnitAddCommand extends GraphCommand {
return true;
}

constructor() {
super();

this.#initTelemetry();
this.#initOptions();
}

#initTelemetry(): void {
this.telemetry.push((args: CommandArgs) => {
Object.assign(this.telemetryProperties, {
hiddenMembership: !!args.options.hiddenMembership
});
});
}

#initOptions(): void {
this.options.unshift(
{
option: '-n, --displayName <displayName>'
},
{
option: '-d, --description [description]'
},
{
option: '--hiddenMembership'
}
);
public get schema(): z.ZodTypeAny | undefined {
return options;
}

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