Skip to content
Merged
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
19 changes: 11 additions & 8 deletions src/m365/entra/commands/multitenant/multitenant-add.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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';
Expand Down Expand Up @@ -33,6 +34,7 @@ describe(commands.MULTITENANT_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.MULTITENANT_ADD, () => {
sinon.stub(session, 'getId').returns('');
auth.connection.active = true;
commandInfo = cli.getCommandInfo(command);
commandOptionsSchema = commandInfo.command.getSchemaToParse()!;
});

beforeEach(() => {
Expand Down Expand Up @@ -80,13 +83,13 @@ describe(commands.MULTITENANT_ADD, () => {
});

it('passes validation when only displayName is specified', async () => {
const actual = await command.validate({ options: { displayName: 'Contoso organization' } }, commandInfo);
assert.strictEqual(actual, true);
const parseResult = commandOptionsSchema.safeParse({ displayName: 'Contoso organization' });
assert.strictEqual(parseResult.success, true);
});

it('passes validation when the displayName and description are specified', async () => {
const actual = await command.validate({ options: { displayName: 'Contoso organization', description: 'Contoso and partners' } }, commandInfo);
assert.strictEqual(actual, true);
const parseResult = commandOptionsSchema.safeParse({ displayName: 'Contoso organization', description: 'Contoso and partners' });
assert.strictEqual(parseResult.success, true);
});

it('creates a multitenant organization with a displayName only', async () => {
Expand All @@ -98,7 +101,7 @@ describe(commands.MULTITENANT_ADD, () => {
throw 'Invalid request';
});

await command.action(logger, { options: { displayName: 'Contoso organization', verbose: true } });
await command.action(logger, { options: commandOptionsSchema.parse({ displayName: 'Contoso organization', verbose: true }) });
assert(loggerLogSpy.calledOnceWithExactly(multitenantOrganizationShortReponse));
});

Expand All @@ -111,7 +114,7 @@ describe(commands.MULTITENANT_ADD, () => {
throw 'Invalid request';
});

await command.action(logger, { options: { displayName: 'Contoso organization', description: 'Contoso and partners' } });
await command.action(logger, { options: commandOptionsSchema.parse({ displayName: 'Contoso organization', description: 'Contoso and partners' }) });
assert(loggerLogSpy.calledOnceWithExactly(multitenantOrganizationReponse));
});

Expand All @@ -127,7 +130,7 @@ describe(commands.MULTITENANT_ADD, () => {
}
});

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

it('correctly handles API OData error when the multitenant organization already exist', async () => {
Expand All @@ -143,6 +146,6 @@ describe(commands.MULTITENANT_ADD, () => {
}
});

await assert.rejects(command.action(logger, { options: {} } as any), new CommandError('Method not supported for update operation.'));
await assert.rejects(command.action(logger, { options: commandOptionsSchema.parse({ displayName: 'Contoso organization' }) }), new CommandError('Method not supported for update operation.'));
});
});
48 changes: 12 additions & 36 deletions src/m365/entra/commands/multitenant/multitenant-add.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import GlobalOptions from '../../../../GlobalOptions.js';
import { z } from 'zod';
import { globalOptionsZod } from '../../../../Command.js';
import { Logger } from '../../../../cli/Logger.js';
import request, { CliRequestOptions } from '../../../../request.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';
import { MultitenantOrganization } from './MultitenantOrganization.js';
import { zod } from '../../../../utils/zod.js';

const options = globalOptionsZod
.extend({
displayName: zod.alias('n', z.string()),
description: zod.alias('d', z.string().optional())
}).strict();
declare type Options = z.infer<typeof options>;

interface CommandArgs {
options: Options;
}

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

class EntraMultitenantAddCommand extends GraphCommand {
public get name(): string {
return commands.MULTITENANT_ADD;
Expand All @@ -23,36 +27,8 @@ class EntraMultitenantAddCommand extends GraphCommand {
return 'Creates a new multitenant organization';
}

constructor() {
super();

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

#initTelemetry(): void {
this.telemetry.push((args: CommandArgs) => {
Object.assign(this.telemetryProperties, {
displayName: typeof args.options.displayName !== 'undefined',
description: typeof args.options.description !== 'undefined'
});
});
}

#initOptions(): void {
this.options.unshift(
{
option: '-n, --displayName <displayName>'
},
{
option: '-d, --description [description]'
}
);
}

#initTypes(): void {
this.types.string.push('displayName', 'description');
public get schema(): z.ZodTypeAny | undefined {
return options;
}

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