Skip to content
Open
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import GlobalOptions from "../../../../GlobalOptions.js";
import { z } from 'zod';
import { globalOptionsZod } from '../../../../Command.js';
import { Logger } from "../../../../cli/Logger.js";
import { entraAdministrativeUnit } from "../../../../utils/entraAdministrativeUnit.js";
import { entraGroup } from "../../../../utils/entraGroup.js";
import { entraUser } from "../../../../utils/entraUser.js";
import { validation } from "../../../../utils/validation.js";
import GraphCommand from "../../../base/GraphCommand.js";
import commands from "../../commands.js";
import request, { CliRequestOptions } from "../../../../request.js";
import { entraDevice } from "../../../../utils/entraDevice.js";

export const options = z.strictObject({
...globalOptionsZod.shape,
administrativeUnitId: z.uuid().optional().alias('i'),
administrativeUnitName: z.string().optional().alias('n'),
userId: z.uuid().optional(),
userName: z.string().optional(),
groupId: z.uuid().optional(),
groupName: z.string().optional(),
deviceId: z.uuid().optional(),
deviceName: z.string().optional()
});

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

interface CommandArgs {
options: Options;
}

interface Options extends GlobalOptions {
administrativeUnitId?: string;
administrativeUnitName?: string;
userId?: string;
userName?: string;
groupId?: string;
groupName?: string;
deviceId?: string;
deviceName?: string;
}

class EntraAdministrativeUnitMemberAddCommand extends GraphCommand {
public get name(): string {
return commands.ADMINISTRATIVEUNIT_MEMBER_ADD;
Expand All @@ -33,84 +36,26 @@ class EntraAdministrativeUnitMemberAddCommand extends GraphCommand {
return 'Adds a member (user, group, device) to an administrative unit';
}

constructor() {
super();

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

#initTelemetry(): void {
this.telemetry.push((args: CommandArgs) => {
Object.assign(this.telemetryProperties, {
userId: typeof args.options.userId !== 'undefined',
userName: typeof args.options.userName !== 'undefined',
groupId: typeof args.options.groupId !== 'undefined',
groupName: typeof args.options.groupName !== 'undefined',
deviceId: typeof args.options.deviceId !== 'undefined',
deviceName: typeof args.options.deviceName !== 'undefined'
});
});
}

#initOptions(): void {
this.options.unshift(
{
option: '-i, --administrativeUnitId [administrativeUnitId]'
},
{
option: '-n, --administrativeUnitName [administrativeUnitName]'
},
{
option: "--userId [userId]"
},
{
option: "--userName [userName]"
},
{
option: "--groupId [groupId]"
},
{
option: "--groupName [groupName]"
},
{
option: "--deviceId [deviceId]"
},
{
option: "--deviceName [deviceName]"
}
);
public get schema(): z.ZodType | undefined {
return options;
}

#initValidators(): void {
this.validators.push(
async (args: CommandArgs) => {
if (args.options.administrativeUnitId && !validation.isValidGuid(args.options.administrativeUnitId as string)) {
return `${args.options.administrativeUnitId} is not a valid GUID`;
public getRefinedSchema(schema: typeof options): z.ZodObject<any> | undefined {
return schema
.refine(options => [options.administrativeUnitId, options.administrativeUnitName].filter(Boolean).length === 1, {
error: 'Specify either administrativeUnitId or administrativeUnitName',
params: {
customCode: 'optionSet',
options: ['administrativeUnitId', 'administrativeUnitName']
}

if (args.options.userId && !validation.isValidGuid(args.options.userId as string)) {
return `${args.options.userId} is not a valid GUID`;
}

if (args.options.groupId && !validation.isValidGuid(args.options.groupId as string)) {
return `${args.options.groupId} is not a valid GUID`;
}

if (args.options.deviceId && !validation.isValidGuid(args.options.deviceId as string)) {
return `${args.options.deviceId} is not a valid GUID`;
})
.refine(options => [options.userId, options.userName, options.groupId, options.groupName, options.deviceId, options.deviceName].filter(Boolean).length === 1, {
error: 'Specify either userId, userName, groupId, groupName, deviceId, or deviceName',
params: {
customCode: 'optionSet',
options: ['userId', 'userName', 'groupId', 'groupName', 'deviceId', 'deviceName']
}

return true;
}
);
}

#initOptionSets(): void {
this.optionSets.push({ options: ['administrativeUnitId', 'administrativeUnitName'] });
this.optionSets.push({ options: ['userId', 'userName', 'groupId', 'groupName', 'deviceId', 'deviceName'] });
});
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import { telemetry } from '../../../../telemetry.js';
import { pid } from '../../../../utils/pid.js';
import { session } from '../../../../utils/session.js';
import { cli } from '../../../../cli/cli.js';
import command from './administrativeunit-member-get.js';
import command, { options } from './administrativeunit-member-get.js';
import request from '../../../../request.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import { CommandError } from '../../../../Command.js';
import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js';
import { settingsNames } from '../../../../settingsNames.js';

describe(commands.ADMINISTRATIVEUNIT_MEMBER_GET, () => {
const administrativeUnitId = 'fc33aa61-cf0e-46b6-9506-f633347202ab';
Expand Down Expand Up @@ -41,6 +40,7 @@ describe(commands.ADMINISTRATIVEUNIT_MEMBER_GET, () => {
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;
let commandOptionsSchema: typeof options;

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

beforeEach(() => {
Expand All @@ -70,8 +71,7 @@ describe(commands.ADMINISTRATIVEUNIT_MEMBER_GET, () => {
afterEach(() => {
sinonUtil.restore([
request.get,
entraAdministrativeUnit.getAdministrativeUnitByDisplayName,
cli.getSettingWithDefaultValue
entraAdministrativeUnit.getAdministrativeUnitByDisplayName
]);
});

Expand All @@ -88,45 +88,29 @@ describe(commands.ADMINISTRATIVEUNIT_MEMBER_GET, () => {
assert.notStrictEqual(command.description, null);
});

it('passes validation when member id and administrativeUnitId are GUIDs', async () => {
const actual = await command.validate({ options: { id: userId, administrativeUnitId: administrativeUnitId } }, commandInfo);
assert.strictEqual(actual, true);
it('passes validation when member id and administrativeUnitId are GUIDs', () => {
const actual = commandOptionsSchema.safeParse({ id: userId, administrativeUnitId: administrativeUnitId });
assert.strictEqual(actual.success, true);
});

it('fails validation if member id is not a valid GUID', async () => {
const actual = await command.validate({ options: { id: 'invalid', administrativeUnitId: administrativeUnitId } }, commandInfo);
assert.notStrictEqual(actual, true);
it('fails validation if member id is not a valid GUID', () => {
const actual = commandOptionsSchema.safeParse({ id: 'invalid', administrativeUnitId: administrativeUnitId });
assert.strictEqual(actual.success, false);
});

it('fails validation if administrativeUnitId is not a valid GUID', async () => {
const actual = await command.validate({ options: { id: userId, administrativeUnitId: 'invalid' } }, commandInfo);
assert.notStrictEqual(actual, true);
it('fails validation if administrativeUnitId is not a valid GUID', () => {
const actual = commandOptionsSchema.safeParse({ id: userId, administrativeUnitId: 'invalid' });
assert.strictEqual(actual.success, false);
});

it('fails validation when both administrativeUnitId and administrativeUnitName options are passed', async () => {
sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => {
if (settingName === settingsNames.prompt) {
return false;
}

return defaultValue;
});

const actual = await command.validate({ options: { id: userId, administrativeUnitId: administrativeUnitId, administrativeUnitName: administrativeUnitName } }, commandInfo);
assert.notStrictEqual(actual, true);
it('fails validation when both administrativeUnitId and administrativeUnitName options are passed', () => {
const actual = commandOptionsSchema.safeParse({ id: userId, administrativeUnitId: administrativeUnitId, administrativeUnitName: administrativeUnitName });
assert.strictEqual(actual.success, false);
});

it('fails validation if both administrativeUnitId and administrativeUnitName options are not passed', async () => {
sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => {
if (settingName === settingsNames.prompt) {
return false;
}

return defaultValue;
});

const actual = await command.validate({ options: { id: userId } }, commandInfo);
assert.notStrictEqual(actual, true);
it('fails validation if both administrativeUnitId and administrativeUnitName options are not passed', () => {
const actual = commandOptionsSchema.safeParse({ id: userId });
assert.strictEqual(actual.success, false);
});

it('get member info for an administrative unit specified by id and member specified by id', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import { DirectoryObject } from '@microsoft/microsoft-graph-types';
import GlobalOptions from '../../../../GlobalOptions.js';
import { z } from 'zod';
import { globalOptionsZod } from '../../../../Command.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';
import { Logger } from '../../../../cli/Logger.js';
import { validation } from '../../../../utils/validation.js';
import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js';
import request, { CliRequestOptions } from '../../../../request.js';

export const options = z.strictObject({
...globalOptionsZod.shape,
id: z.uuid().alias('i'),
administrativeUnitId: z.uuid().optional().alias('u'),
administrativeUnitName: z.string().optional().alias('n'),
properties: z.string().optional().alias('p')
});

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

interface CommandArgs {
options: Options;
}

export interface Options extends GlobalOptions {
id: string;
administrativeUnitId?: string;
administrativeUnitName?: string;
properties?: string;
}

interface DirectoryObjectEx extends DirectoryObject {
'@odata.context'?: string;
'@odata.type'?: string;
Expand All @@ -33,60 +36,19 @@ class EntraAdministrativeUnitMemberGetCommand extends GraphCommand {
return 'Retrieve a specific member (user, group, or device) of an administrative unit';
}

constructor() {
super();

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

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

#initOptions(): void {
this.options.unshift(
{
option: '-i, --id <id>'
},
{
option: '-u, --administrativeUnitId [administrativeUnitId]'
},
{
option: '-n, --administrativeUnitName [administrativeUnitName]'
},
{
option: '-p, --properties [properties]'
}
);
public get schema(): z.ZodType | undefined {
return options;
}

#initValidators(): void {
this.validators.push(
async (args: CommandArgs) => {
if (args.options.id && !validation.isValidGuid(args.options.id)) {
return `${args.options.id} is not a valid GUID`;
public getRefinedSchema(schema: typeof options): z.ZodObject<any> | undefined {
return schema
.refine(options => [options.administrativeUnitId, options.administrativeUnitName].filter(Boolean).length === 1, {
error: 'Specify either administrativeUnitId or administrativeUnitName',
params: {
customCode: 'optionSet',
options: ['administrativeUnitId', 'administrativeUnitName']
}

if (args.options.administrativeUnitId && !validation.isValidGuid(args.options.administrativeUnitId)) {
return `${args.options.administrativeUnitId} is not a valid GUID`;
}

return true;
}
);
}

#initOptionSets(): void {
this.optionSets.push({ options: ['administrativeUnitId', 'administrativeUnitName'] });
});
}

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