diff --git a/.vscode/launch.json b/.vscode/launch.json index c8ec8010070..d807f346f47 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,7 @@ // 'm365 spo site get --url /', you'd use: // "args": ["spo", "site", "get", "--url", "/"] // after debugging, revert changes so that they won't end up in your PR - "args": [], + "args": ["login"], "console": "integratedTerminal", "env": { "NODE_OPTIONS": "--enable-source-maps" diff --git a/docs/docs/contribute/new-command/build-command-logic.mdx b/docs/docs/contribute/new-command/build-command-logic.mdx index 2e1e8006e1a..4eb69978d4d 100644 --- a/docs/docs/contribute/new-command/build-command-logic.mdx +++ b/docs/docs/contribute/new-command/build-command-logic.mdx @@ -71,16 +71,16 @@ Some commands require options. For example, when you want to get a group, you ne Global CLI options, such as `query`, `output`, `debug` or `verbose` are defined in the `globalOptionsZod` property. To define options specific to your command, extend the global schema with your command-specific options. ```ts title="src/m365/spo/commands/group/group-get.ts" +import { z } from 'zod'; import { globalOptionsZod } from '../../Command.js'; -export const options = globalOptionsZod - .extend({ - webUrl: z.string(), - id: z.number().optional(), - name: z.string().optional(), - associatedGroup: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod, + webUrl: z.string(), + id: z.number().optional(), + name: z.string().optional(), + associatedGroup: z.string().optional() +}); ``` In this example, we're adding 4 command options: `webUrl`, `id`, `name`, and `associatedGroup`. The `webUrl` option is required and must be a string. The `id`, `name`, and `associatedGroup` options are optional. Since our command doesn't support unknown options, we set the schema to strict. @@ -88,16 +88,16 @@ In this example, we're adding 4 command options: `webUrl`, `id`, `name`, and `as Next, we define a TypeScript type for options and command args, which allows us to benefit from type-safety when working with options in the command logic. ```ts title="src/m365/spo/commands/group/group-get.ts" +import { z } from 'zod'; import { globalOptionsZod } from '../../Command.js'; -export const options = globalOptionsZod - .extend({ - webUrl: z.string(), - id: z.number().optional(), - name: z.string().optional(), - associatedGroup: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod, + webUrl: z.string(), + id: z.number().optional(), + name: z.string().optional(), + associatedGroup: z.string().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -108,16 +108,16 @@ interface CommandArgs { Finally, we expose the options schema in the command class. ```ts title="src/m365/spo/commands/group/group-get.ts" +import { z } from 'zod'; import { globalOptionsZod } from '../../Command.js'; -export const options = globalOptionsZod - .extend({ - webUrl: z.string(), - id: z.number().optional(), - name: z.string().optional(), - associatedGroup: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod, + webUrl: z.string(), + id: z.number().optional(), + name: z.string().optional(), + associatedGroup: z.string().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -127,7 +127,7 @@ interface CommandArgs { class SpoGroupGetCommand extends SpoCommand { // ... - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } @@ -146,17 +146,16 @@ class SpoGroupGetCommand extends SpoCommand { To simplify using the CLI, we often use aliases for options. For example, the `--webUrl` option can be shortened to `-u`. To define an alias for an option, we wrap the property in the `alias` function in the schema. ```ts title="src/m365/spo/commands/group/group-get.ts" +import { z } from 'zod'; import { globalOptionsZod } from '../../Command.js'; -import { zod } from '../../utils/zod.js'; -export const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string()), - id: zod.alias('i', z.number().optional()), - name: z.string().optional(), - associatedGroup: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod, + webUrl: z.string().alias('u'), + id: z.number().optional().alias('i'), + name: z.string().optional(), + associatedGroup: z.string().optional() +}); ``` ## Defining option autocomplete @@ -164,6 +163,7 @@ export const options = globalOptionsZod Some options require predefined values. For example, the `associatedGroup` option can only have one of the following values: `Owner`, `Member`, or `Visitor`. To define the allowed values for an option, we use enums with the `coercedEnum` helper function. This function allows users to specify the values in a case-insensitive way. In the code, the value of the `associatedGroup` option will be expressed as an enum which allows us to benefit from type-safety and support refactoring. ```ts title="src/m365/spo/commands/group/group-get.ts" +import { z } from 'zod'; import { globalOptionsZod } from '../../Command.js'; import { zod } from '../../utils/zod.js'; @@ -173,14 +173,13 @@ enum AssociatedGroup { Visitor = 'visitor' }; -export const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string()), - id: zod.alias('i', z.number().optional()), - name: z.string().optional(), - associatedGroup: zod.coercedEnum(AssociatedGroup).optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod, + webUrl: z.string().alias('u'), + id: z.number().optional().alias('i'), + name: z.string().optional(), + associatedGroup: zod.coercedEnum(AssociatedGroup).optional() +}); ``` ## Option validation @@ -190,6 +189,7 @@ The options that users specify won't always be correct. So instead of passing fa Zod automatically validates primitive values, which means that you only need to add 'business' validation logic. Validation is implemented using Zod refinements on the specific property. For example, to validate that the specified URL is a SharePoint URL, use: ```ts title="src/m365/spo/commands/group/group-get.ts" +import { z } from 'zod'; import { globalOptionsZod } from '../../Command.js'; import { validation } from '../../utils/validation.js'; import { zod } from '../../utils/zod.js'; @@ -200,16 +200,15 @@ enum AssociatedGroup { Visitor = 'visitor' }; -export const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string().refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `Specified URL ${url} is not a valid SharePoint URL`, - }))), - id: zod.alias('i', z.number().optional()), - name: z.string().optional(), - associatedGroup: zod.coercedEnum(AssociatedGroup).optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod, + webUrl: z.string().refine(url => validation.isValidSharePointUrl(url) === true, { + error: 'Invalid SharePoint URL', + }).alias('u'), + id: z.number().optional().alias('i'), + name: z.string().optional(), + associatedGroup: zod.coercedEnum(AssociatedGroup).optional() +}); ``` A refinement takes two arguments: a predicate function and an error object. The predicate function is used to validate the input, and the error object is used to define the error message that will be displayed when the input is invalid. @@ -219,6 +218,7 @@ A refinement takes two arguments: a predicate function and an error object. The Option sets are used to ensure that only one option has a value from a set of options. When no option is used, the command will return an error, and the same goes when multiple of these options are used. To use option sets, add a Zod refinement on the whole schema, which gives you access to all schema properties. ```ts title="src/m365/spo/commands/group/group-get.ts" +import { z } from 'zod'; import { globalOptionsZod } from '../../Command.js'; import { validation } from '../../utils/validation.js'; import { zod } from '../../utils/zod.js'; @@ -229,16 +229,15 @@ enum AssociatedGroup { Visitor = 'visitor' }; -export const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string().refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `Specified URL ${url} is not a valid SharePoint URL`, - }))), - id: zod.alias('i', z.number().optional()), - name: z.string().optional(), - associatedGroup: zod.coercedEnum(AssociatedGroup).optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod, + webUrl: z.string().refine(url => validation.isValidSharePointUrl(url) === true, { + error: 'Invalid SharePoint URL', + }).alias('u'), + id: z.number().optional().alias('i'), + name: z.string().optional(), + associatedGroup: zod.coercedEnum(AssociatedGroup).optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -248,14 +247,14 @@ interface CommandArgs { class SpoGroupGetCommand extends SpoCommand { // ... - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => options.id !== undefined || options.name !== undefined && !(options.id !== undefined && options.name !== undefined), { - message: `Either id or name is required, but not both.` + error: `Either id or name is required, but not both.` }); } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index d2e0ea432c8..e4c380998fb 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -39,7 +39,7 @@ "uuid": "^13.0.0", "yaml": "^2.8.1", "yargs-parser": "^22.0.0", - "zod": "^3.25.76" + "zod": "^4.1.11" }, "bin": { "m365": "dist/index.js", @@ -1150,6 +1150,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -2902,6 +2903,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3046,6 +3048,7 @@ "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.4", "@typescript-eslint/types": "8.46.4", @@ -3260,6 +3263,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4142,6 +4146,7 @@ "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz", "integrity": "sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw==", "license": "MIT", + "peer": true, "dependencies": { "semver": "^7.5.3" } @@ -4333,6 +4338,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7299,6 +7305,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7913,9 +7920,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz", + "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index fb91f7fcd89..006f4d07933 100644 --- a/package.json +++ b/package.json @@ -292,7 +292,7 @@ "uuid": "^13.0.0", "yaml": "^2.8.1", "yargs-parser": "^22.0.0", - "zod": "^3.25.76" + "zod": "^4.1.11" }, "devDependencies": { "@actions/core": "^1.11.1", diff --git a/src/Command.spec.ts b/src/Command.spec.ts index 0a62b6e1ceb..f99b6852454 100644 --- a/src/Command.spec.ts +++ b/src/Command.spec.ts @@ -155,7 +155,7 @@ class MockCommandWithSchema extends Command { return 'Mock command description'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return globalOptionsZod; } diff --git a/src/Command.ts b/src/Command.ts index 66bc23357c1..4727c17c527 100644 --- a/src/Command.ts +++ b/src/Command.ts @@ -1,5 +1,5 @@ import os from 'os'; -import { ZodTypeAny, z } from 'zod'; +import { ZodType, z } from 'zod'; import auth from './Auth.js'; import GlobalOptions from './GlobalOptions.js'; import { CommandInfo } from './cli/CommandInfo.js'; @@ -12,9 +12,9 @@ import { telemetry } from './telemetry.js'; import { accessToken } from './utils/accessToken.js'; import { md } from './utils/md.js'; import { GraphResponseError } from './utils/odata.js'; +import { optionsUtils } from './utils/optionsUtils.js'; import { prompt } from './utils/prompt.js'; import { zod } from './utils/zod.js'; -import { optionsUtils } from './utils/optionsUtils.js'; export interface CommandOption { option: string; @@ -48,7 +48,7 @@ interface ODataError { export const globalOptionsZod = z.object({ query: z.string().optional(), - output: zod.alias('o', z.enum(['csv', 'json', 'md', 'text', 'none']).optional()), + output: z.enum(['csv', 'json', 'md', 'text', 'none']).optional().alias('o'), debug: z.boolean().default(false), verbose: z.boolean().default(false) }); @@ -85,17 +85,17 @@ export default abstract class Command { public abstract get name(): string; public abstract get description(): string; - public get schema(): ZodTypeAny | undefined { + public get schema(): ZodType | undefined { return undefined; } // eslint-disable-next-line @typescript-eslint/no-unused-vars - public getRefinedSchema(schema: ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: ZodType): z.ZodType | undefined { return undefined; } - public getSchemaToParse(): z.ZodTypeAny | undefined { - return this.getRefinedSchema(this.schema as z.ZodTypeAny) ?? this.schema; + public getSchemaToParse(): z.ZodType | undefined { + return this.getRefinedSchema(this.schema as z.ZodType) ?? this.schema; } // metadata for command's options diff --git a/src/cli/cli.spec.ts b/src/cli/cli.spec.ts index 336983ac8a8..cea76042033 100644 --- a/src/cli/cli.spec.ts +++ b/src/cli/cli.spec.ts @@ -255,8 +255,8 @@ class MockCommandWithSchema extends AnonymousCommand { public get description(): string { return 'Mock command with schema'; } - public get schema(): z.ZodTypeAny { - return globalOptionsZod.strict(); + public get schema(): z.ZodType { + return z.strictObject({ ...globalOptionsZod.shape }); } public async commandAction(): Promise { } @@ -269,12 +269,11 @@ class MockCommandWithSchemaAndRequiredOptions extends AnonymousCommand { public get description(): string { return 'Mock command with schema and required options'; } - public get schema(): z.ZodTypeAny { - return globalOptionsZod - .extend({ - url: z.string() - }) - .strict(); + public get schema(): z.ZodType { + return z.strictObject({ + ...globalOptionsZod.shape, + url: z.string() + }); } public async commandAction(): Promise { } @@ -287,13 +286,12 @@ class MockCommandWithSchemaAndBoolRequiredOption extends AnonymousCommand { public get description(): string { return 'Mock command with schema and required options'; } - public get schema(): z.ZodTypeAny { - return globalOptionsZod - .extend({ - url: z.string(), - bool: z.boolean() - }) - .strict(); + public get schema(): z.ZodType { + return z.strictObject({ + ...globalOptionsZod.shape, + url: z.string(), + bool: z.boolean() + }); } public async commandAction(): Promise { } diff --git a/src/cli/cli.ts b/src/cli/cli.ts index efd4ea60932..7ab0f356a90 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -186,15 +186,15 @@ async function execute(rawArgs: string[]): Promise { break; } else { - const hasNonRequiredErrors = result.error.errors.some(e => e.code !== 'invalid_type' || e.received !== 'undefined'); + const hasNonRequiredErrors = result.error.issues.some(i => i.code !== 'invalid_type'); const shouldPrompt = cli.getSettingWithDefaultValue(settingsNames.prompt, true); if (hasNonRequiredErrors === false && shouldPrompt) { await cli.error('🌶️ Provide values for the following parameters:'); - for (const error of result.error.errors) { - const optionName = error.path.join('.'); + for (const issue of result.error.issues) { + const optionName = issue.path.join('.'); const optionInfo = cli.commandToExecute.options.find(o => o.name === optionName); const answer = await cli.promptForValue(optionInfo!); // coerce the answer to the correct type @@ -208,10 +208,10 @@ async function execute(rawArgs: string[]): Promise { } } else { - result.error.errors.forEach(e => { - if (e.code === 'invalid_type' && - e.received === 'undefined') { - e.message = `Required option not specified`; + result.error.issues.forEach(i => { + if (i.code === 'invalid_type' && + i.input === undefined) { + (i.message as any) = `Required option not specified`; } }); return cli.closeWithError(result.error, cli.optionsFromArgs, true); @@ -940,7 +940,7 @@ async function closeWithError(error: any, args: CommandArgs, showHelpIfEnabled: let errorMessage: string = error instanceof CommandError ? error.message : error; if (error instanceof ZodError) { - errorMessage = error.errors.map(e => (e.path.length > 0 ? `${e.path.join('.')}: ${e.message}` : e.message)).join(os.EOL); + errorMessage = error.issues.map(i => (i.path.length > 0 ? `${i.path.join('.')}: ${i.message}` : i.message)).join(os.EOL); } if ((!args.options.output || args.options.output === 'json') && diff --git a/src/m365/adaptivecard/commands/adaptivecard-send.spec.ts b/src/m365/adaptivecard/commands/adaptivecard-send.spec.ts index 55237c23cd4..0f7a7c03f26 100644 --- a/src/m365/adaptivecard/commands/adaptivecard-send.spec.ts +++ b/src/m365/adaptivecard/commands/adaptivecard-send.spec.ts @@ -11,17 +11,16 @@ import { pid } from '../../../utils/pid.js'; import { session } from '../../../utils/session.js'; import { sinonUtil } from '../../../utils/sinonUtil.js'; import commands from '../commands.js'; -import command from './adaptivecard-send.js'; +import command, { options } from './adaptivecard-send.js'; // required to avoid tests from timing out due to dynamic imports import 'adaptivecards-templating'; -import { z } from 'zod'; import { settingsNames } from '../../../settingsNames.js'; describe(commands.SEND, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -29,7 +28,7 @@ describe(commands.SEND, () => { sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/adaptivecard/commands/adaptivecard-send.ts b/src/m365/adaptivecard/commands/adaptivecard-send.ts index 60e5c191cd6..4de0b587e35 100644 --- a/src/m365/adaptivecard/commands/adaptivecard-send.ts +++ b/src/m365/adaptivecard/commands/adaptivecard-send.ts @@ -8,17 +8,16 @@ import { zod } from '../../../utils/zod.js'; import AnonymousCommand from '../../base/AnonymousCommand.js'; import commands from '../commands.js'; -export const options = globalOptionsZod - .extend({ - url: z.string(), - title: zod.alias('t', z.string().optional()), - description: zod.alias('d', z.string().optional()), - imageUrl: zod.alias('i', z.string().optional()), - actionUrl: zod.alias('a', z.string().optional()), - card: z.string().optional(), - cardData: z.string().optional() - }) - .passthrough(); +export const options = z.looseObject({ + ...globalOptionsZod.shape, + url: z.string(), + title: z.string().optional().alias('t'), + description: z.string().optional().alias('d'), + imageUrl: z.string().optional().alias('i'), + actionUrl: z.string().optional().alias('a'), + card: z.string().optional(), + cardData: z.string().optional() +}); declare type Options = z.infer; @@ -35,14 +34,14 @@ class AdaptiveCardSendCommand extends AnonymousCommand { return 'Sends adaptive card to the specified URL'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.cardData || options.card, { - message: 'When you specify cardData, you must also specify card.', + error: 'When you specify cardData, you must also specify card.', path: ['cardData'] }) .refine(options => { @@ -57,7 +56,7 @@ class AdaptiveCardSendCommand extends AnonymousCommand { } return true; }, { - message: 'Specified card is not a valid JSON string.', + error: 'Specified card is not a valid JSON string.', path: ['card'] }) .refine(options => { @@ -72,7 +71,7 @@ class AdaptiveCardSendCommand extends AnonymousCommand { } return true; }, { - message: 'Specified cardData is not a valid JSON string.', + error: 'Specified cardData is not a valid JSON string.', path: ['cardData'] }); } diff --git a/src/m365/app/commands/app-get.spec.ts b/src/m365/app/commands/app-get.spec.ts index 697a6c3c5be..0d1d0c5d2b8 100644 --- a/src/m365/app/commands/app-get.spec.ts +++ b/src/m365/app/commands/app-get.spec.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import fs from 'fs'; 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'; @@ -13,6 +12,7 @@ import { entraApp } from '../../../utils/entraApp.js'; import { pid } from '../../../utils/pid.js'; import { session } from '../../../utils/session.js'; import { sinonUtil } from '../../../utils/sinonUtil.js'; +import { appCommandOptions } from '../../base/AppCommand.js'; import commands from '../commands.js'; import command from './app-get.js'; @@ -21,7 +21,7 @@ describe(commands.GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof appCommandOptions; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -39,7 +39,7 @@ describe(commands.GET, () => { })); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof appCommandOptions; }); beforeEach(() => { diff --git a/src/m365/app/commands/app-get.ts b/src/m365/app/commands/app-get.ts index fd290a8cb25..2d804348151 100644 --- a/src/m365/app/commands/app-get.ts +++ b/src/m365/app/commands/app-get.ts @@ -13,7 +13,7 @@ class AppGetCommand extends AppCommand { return 'Retrieves information about the current Microsoft Entra app'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return appCommandOptions; } diff --git a/src/m365/app/commands/app-open.spec.ts b/src/m365/app/commands/app-open.spec.ts index 1ca99111a4a..8ee3c51483f 100644 --- a/src/m365/app/commands/app-open.spec.ts +++ b/src/m365/app/commands/app-open.spec.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import fs from 'fs'; 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'; @@ -12,7 +11,7 @@ import { browserUtil } from '../../../utils/browserUtil.js'; import { pid } from '../../../utils/pid.js'; import { session } from '../../../utils/session.js'; import commands from '../commands.js'; -import command from './app-open.js'; +import command, { options } from './app-open.js'; describe(commands.OPEN, () => { let log: string[]; @@ -21,7 +20,7 @@ describe(commands.OPEN, () => { let getSettingWithDefaultValueStub: sinon.SinonStub; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -39,7 +38,7 @@ describe(commands.OPEN, () => { ] })); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/app/commands/app-open.ts b/src/m365/app/commands/app-open.ts index 526f81c83d8..eb267af8d94 100644 --- a/src/m365/app/commands/app-open.ts +++ b/src/m365/app/commands/app-open.ts @@ -6,11 +6,10 @@ import { browserUtil } from '../../../utils/browserUtil.js'; import AppCommand, { appCommandOptions } from '../../base/AppCommand.js'; import commands from '../commands.js'; -const options = appCommandOptions - .extend({ - preview: z.boolean().optional().default(false) - }) - .strict(); +export const options = z.strictObject({ + ...appCommandOptions.shape, + preview: z.boolean().optional().default(false) +}); type Options = z.infer; interface CommandArgs { @@ -26,7 +25,7 @@ class AppOpenCommand extends AppCommand { return 'Opens Microsoft Entra app in the Microsoft Entra ID portal'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/app/commands/permission/permission-add.spec.ts b/src/m365/app/commands/permission/permission-add.spec.ts index 947ccb98dfb..de3515ea7db 100644 --- a/src/m365/app/commands/permission/permission-add.spec.ts +++ b/src/m365/app/commands/permission/permission-add.spec.ts @@ -2,7 +2,6 @@ import { Application, ServicePrincipal } from '@microsoft/microsoft-graph-types' import assert from 'assert'; import fs from 'fs'; 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'; @@ -11,13 +10,13 @@ import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { settingsNames } from '../../../../settingsNames.js'; import { telemetry } from '../../../../telemetry.js'; +import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; import { odata } from '../../../../utils/odata.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './permission-add.js'; -import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; +import command, { options } from './permission-add.js'; describe(commands.PERMISSION_ADD, () => { //#region Mocked responses @@ -32,7 +31,7 @@ describe(commands.PERMISSION_ADD, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -50,7 +49,7 @@ describe(commands.PERMISSION_ADD, () => { })); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName: string, defaultValue: any) => { if (settingName === 'prompt') { return false; diff --git a/src/m365/app/commands/permission/permission-add.ts b/src/m365/app/commands/permission/permission-add.ts index b9b3dfb4770..d233d3eb29b 100644 --- a/src/m365/app/commands/permission/permission-add.ts +++ b/src/m365/app/commands/permission/permission-add.ts @@ -8,13 +8,12 @@ import AppCommand, { appCommandOptions } from '../../../base/AppCommand.js'; import commands from '../../commands.js'; import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; -const options = appCommandOptions - .extend({ - applicationPermissions: z.string().optional(), - delegatedPermissions: z.string().optional(), - grantAdminConsent: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...appCommandOptions.shape, + applicationPermissions: z.string().optional(), + delegatedPermissions: z.string().optional(), + grantAdminConsent: z.boolean().optional() +}); type Options = z.infer; @@ -42,14 +41,14 @@ class AppPermissionAddCommand extends AppCommand { return 'Adds the specified application and/or delegated permissions to the current Microsoft Entra app API permissions'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => options.applicationPermissions || options.delegatedPermissions, { - message: 'Specify at least one of applicationPermissions or delegatedPermissions, or both.', + error: 'Specify at least one of applicationPermissions or delegatedPermissions, or both.', path: ['delegatedPermissions'] }); } diff --git a/src/m365/app/commands/permission/permission-list.spec.ts b/src/m365/app/commands/permission/permission-list.spec.ts index c73d5d48899..1bd7d225743 100644 --- a/src/m365/app/commands/permission/permission-list.spec.ts +++ b/src/m365/app/commands/permission/permission-list.spec.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import fs from 'fs'; 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'; @@ -13,6 +12,7 @@ import { entraApp } from '../../../../utils/entraApp.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import { appCommandOptions } from '../../../base/AppCommand.js'; import commands from '../../commands.js'; import command from './permission-list.js'; import { appRegApplicationPermissions, appRegDelegatedPermissionsMultipleResources, appRegNoApiPermissions, flowServiceOAuth2PermissionScopes, msGraphPrincipalAppRoles, msGraphPrincipalOAuth2PermissionScopes } from './permission-list.mock.js'; @@ -23,7 +23,7 @@ describe(commands.PERMISSION_LIST, () => { let loggerLogSpy: sinon.SinonSpy; let loggerLogToStderrSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof appCommandOptions; //#region Mocked Responses const appId = '9c79078b-815e-4a3e-bb80-2aaf2d9e9b3d'; @@ -46,7 +46,7 @@ describe(commands.PERMISSION_LIST, () => { sinon.stub(fs, 'readFileSync').returns(JSON.stringify(appResponse)); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof appCommandOptions; }); beforeEach(() => { diff --git a/src/m365/app/commands/permission/permission-list.ts b/src/m365/app/commands/permission/permission-list.ts index 8323fa8d052..b2e1de44242 100644 --- a/src/m365/app/commands/permission/permission-list.ts +++ b/src/m365/app/commands/permission/permission-list.ts @@ -31,7 +31,7 @@ class AppPermissionListCommand extends AppCommand { return 'Lists API permissions for the current Microsoft Entra app'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return appCommandOptions; } diff --git a/src/m365/base/AppCommand.spec.ts b/src/m365/base/AppCommand.spec.ts index ab3d0ee5a4c..8c1c0f167b0 100644 --- a/src/m365/base/AppCommand.spec.ts +++ b/src/m365/base/AppCommand.spec.ts @@ -1,14 +1,13 @@ import assert from 'assert'; import fs from 'fs'; import sinon from 'sinon'; -import { z } from 'zod'; import { cli } from '../../cli/cli.js'; import { CommandInfo } from '../../cli/CommandInfo.js'; import { Logger } from '../../cli/Logger.js'; import Command, { CommandError } from '../../Command.js'; import { telemetry } from '../../telemetry.js'; import { sinonUtil } from '../../utils/sinonUtil.js'; -import AppCommand from './AppCommand.js'; +import AppCommand, { appCommandOptions } from './AppCommand.js'; class MockCommand extends AppCommand { public get name(): string { @@ -31,11 +30,11 @@ describe('AppCommand', () => { let logger: Logger; let log: string[]; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof appCommandOptions; before(() => { commandInfo = cli.getCommandInfo(new MockCommand()); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof appCommandOptions; sinon.stub(telemetry, 'trackEvent').resolves(); }); diff --git a/src/m365/base/AppCommand.ts b/src/m365/base/AppCommand.ts index becbd30b44f..63b17c84014 100644 --- a/src/m365/base/AppCommand.ts +++ b/src/m365/base/AppCommand.ts @@ -6,10 +6,10 @@ import Command, { CommandError, globalOptionsZod } from '../../Command.js'; import { formatting } from '../../utils/formatting.js'; import { M365RcJson, M365RcJsonApp } from './M365RcJson.js'; -export const appCommandOptions = globalOptionsZod - .extend({ - appId: z.string().uuid().optional() - }); +export const appCommandOptions = z.object({ + ...globalOptionsZod.shape, + appId: z.uuid().optional() +}); type Options = z.infer; export interface AppCommandArgs { @@ -24,7 +24,7 @@ export default abstract class AppCommand extends Command { return 'https://graph.microsoft.com'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return appCommandOptions; } diff --git a/src/m365/booking/commands/business/business-get.spec.ts b/src/m365/booking/commands/business/business-get.spec.ts index 9c4f21496e6..cc2ee600158 100644 --- a/src/m365/booking/commands/business/business-get.spec.ts +++ b/src/m365/booking/commands/business/business-get.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -14,7 +13,7 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './business-get.js'; +import command, { options } from './business-get.js'; describe(commands.BUSINESS_GET, () => { const validId = 'mail@contoso.onmicrosoft.com'; @@ -34,7 +33,7 @@ describe(commands.BUSINESS_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -44,7 +43,7 @@ describe(commands.BUSINESS_GET, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/booking/commands/business/business-get.ts b/src/m365/booking/commands/business/business-get.ts index bace8b52c6a..7bbeff1fb8e 100644 --- a/src/m365/booking/commands/business/business-get.ts +++ b/src/m365/booking/commands/business/business-get.ts @@ -5,16 +5,14 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().optional()), - name: zod.alias('n', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.string().optional().alias('i'), + name: z.string().optional().alias('n') +}); declare type Options = z.infer; @@ -31,14 +29,14 @@ class BookingBusinessGetCommand extends GraphCommand { return 'Retrieve the specified Microsoft Bookings business.'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => options.id || options.name, { - message: 'Specify either id or name' + error: 'Specify either id or name' }); } diff --git a/src/m365/booking/commands/business/business-list.spec.ts b/src/m365/booking/commands/business/business-list.spec.ts index 7bf4af82888..01693505624 100644 --- a/src/m365/booking/commands/business/business-list.spec.ts +++ b/src/m365/booking/commands/business/business-list.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,14 +11,14 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './business-list.js'; +import command, { options } from './business-list.js'; describe(commands.BUSINESS_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -29,7 +28,7 @@ describe(commands.BUSINESS_LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/booking/commands/business/business-list.ts b/src/m365/booking/commands/business/business-list.ts index e3019f71780..309c871b9fc 100644 --- a/src/m365/booking/commands/business/business-list.ts +++ b/src/m365/booking/commands/business/business-list.ts @@ -6,7 +6,7 @@ import { odata } from '../../../../utils/odata.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = z.strictObject({ ...globalOptionsZod.shape }); class BookingBusinessListCommand extends GraphCommand { public get name(): string { @@ -17,7 +17,7 @@ class BookingBusinessListCommand extends GraphCommand { return 'Lists all Microsoft Bookings businesses that are created for the tenant.'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/cli/commands/app/app-add.spec.ts b/src/m365/cli/commands/app/app-add.spec.ts index 1b3e6865dbd..2f56f5f6583 100644 --- a/src/m365/cli/commands/app/app-add.spec.ts +++ b/src/m365/cli/commands/app/app-add.spec.ts @@ -1,28 +1,27 @@ import assert from 'assert'; import Configstore from 'configstore'; import sinon from 'sinon'; -import { z } from 'zod'; import auth from '../../../../Auth.js'; -import cliConfig from '../../../../config.js'; +import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.js'; +import { CommandError } from '../../../../Command.js'; +import cliConfig from '../../../../config.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; +import { entraApp } from '../../../../utils/entraApp.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import { cli } from '../../../../cli/cli.js'; -import command from './app-add.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { entraApp } from '../../../../utils/entraApp.js'; -import { accessToken } from '../../../../utils/accessToken.js'; -import request from '../../../../request.js'; +import commands from '../../commands.js'; +import command, { options } from './app-add.js'; describe(commands.APP_ADD, () => { let log: any[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let config: Configstore; let configSetSpy: sinon.SinonSpy; @@ -33,7 +32,7 @@ describe(commands.APP_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; config = cli.getConfig(); configSetSpy = sinon.stub(config, 'set').returns(); }); @@ -137,7 +136,7 @@ describe(commands.APP_ADD, () => { scopes: 'all', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert.deepEqual(createAppRegistrationSpy.getCall(0).args[0], { options: { allowPublicClientFlows: true, @@ -181,7 +180,7 @@ describe(commands.APP_ADD, () => { saveToConfig: true, verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); const expected = { clientId: '00000000-0000-0000-0000-000000000001', tenantId: '00000000-0000-0000-0000-000000000003' @@ -237,7 +236,7 @@ describe(commands.APP_ADD, () => { scopes: 'https://graph.microsoft.com/User.Read,https://graph.microsoft.com/Group.Read.All', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert.deepEqual(createAppRegistrationSpy.getCall(0).args[0], { options: { allowPublicClientFlows: true, @@ -284,6 +283,6 @@ describe(commands.APP_ADD, () => { saveToConfig: true, verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('Invalid request')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('Invalid request')); }); }); diff --git a/src/m365/cli/commands/app/app-add.ts b/src/m365/cli/commands/app/app-add.ts index 1c2a3288d85..3564cef0ca0 100644 --- a/src/m365/cli/commands/app/app-add.ts +++ b/src/m365/cli/commands/app/app-add.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import config from '../../../../config.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; @@ -10,13 +9,12 @@ import { AppCreationOptions, AppInfo, entraApp } from '../../../../utils/entraAp import { accessToken } from '../../../../utils/accessToken.js'; import auth from '../../../../Auth.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string().optional().default('CLI for M365')), - scopes: zod.alias('s', z.string().optional().default('minimal')), - saveToConfig: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().optional().default('CLI for M365').alias('n'), + scopes: z.string().optional().default('minimal').alias('s'), + saveToConfig: z.boolean().optional() +}); declare type Options = z.infer; @@ -33,11 +31,11 @@ class CliAppAddCommand extends GraphCommand { return 'Creates a Microsoft Entra application registration for CLI for Microsoft 365'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => { const scopes = options.scopes; diff --git a/src/m365/cli/commands/cli-issue.spec.ts b/src/m365/cli/commands/cli-issue.spec.ts index b431e6c4229..24f35b16f51 100644 --- a/src/m365/cli/commands/cli-issue.spec.ts +++ b/src/m365/cli/commands/cli-issue.spec.ts @@ -7,7 +7,6 @@ import { telemetry } from '../../../telemetry.js'; import { pid } from '../../../utils/pid.js'; import { session } from '../../../utils/session.js'; import commands from '../commands.js'; - import { browserUtil } from '../../../utils/browserUtil.js'; import command from './cli-issue.js'; diff --git a/src/m365/commands/docs.spec.ts b/src/m365/commands/docs.spec.ts index 54952753c46..d53063aa6e6 100644 --- a/src/m365/commands/docs.spec.ts +++ b/src/m365/commands/docs.spec.ts @@ -1,6 +1,5 @@ import assert from 'assert'; import sinon from 'sinon'; -import { z } from 'zod'; import { cli } from '../../cli/cli.js'; import { CommandInfo } from '../../cli/CommandInfo.js'; import { Logger } from '../../cli/Logger.js'; @@ -11,7 +10,7 @@ import { pid } from '../../utils/pid.js'; import { session } from '../../utils/session.js'; import { sinonUtil } from '../../utils/sinonUtil.js'; import commands from './commands.js'; -import command from './docs.js'; +import command, { options } from './docs.js'; describe(commands.DOCS, () => { let log: any[]; @@ -19,14 +18,14 @@ describe(commands.DOCS, () => { let loggerLogSpy: sinon.SinonSpy; let getSettingWithDefaultValueStub: sinon.SinonStub; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(telemetry, 'trackEvent').resolves(); sinon.stub(pid, 'getProcessName').callsFake(() => ''); sinon.stub(session, 'getId').callsFake(() => ''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/commands/docs.ts b/src/m365/commands/docs.ts index 55213e89586..8dfc8170492 100644 --- a/src/m365/commands/docs.ts +++ b/src/m365/commands/docs.ts @@ -8,7 +8,7 @@ import { browserUtil } from '../../utils/browserUtil.js'; import AnonymousCommand from '../base/AnonymousCommand.js'; import commands from './commands.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); class DocsCommand extends AnonymousCommand { public get name(): string { diff --git a/src/m365/commands/login.spec.ts b/src/m365/commands/login.spec.ts index 7283a37ef7d..43b8806081c 100644 --- a/src/m365/commands/login.spec.ts +++ b/src/m365/commands/login.spec.ts @@ -2,7 +2,6 @@ import assert from 'assert'; import Configstore from 'configstore'; import fs from 'fs'; import sinon from 'sinon'; -import { z } from 'zod'; import auth, { AuthType, CloudType } from '../../Auth.js'; import { CommandArgs, CommandError } from '../../Command.js'; import { CommandInfo } from '../../cli/CommandInfo.js'; @@ -14,13 +13,13 @@ import { pid } from '../../utils/pid.js'; import { session } from '../../utils/session.js'; import { sinonUtil } from '../../utils/sinonUtil.js'; import commands from './commands.js'; -import command from './login.js'; +import command, { options } from './login.js'; describe(commands.LOGIN, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let config: Configstore; let deactivateStub: sinon.SinonStub; @@ -32,7 +31,7 @@ describe(commands.LOGIN, () => { sinon.stub(pid, 'getProcessName').callsFake(() => ''); sinon.stub(session, 'getId').callsFake(() => ''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.accessTokens[auth.defaultResource] = { expiresOn: '123', accessToken: 'abc' diff --git a/src/m365/commands/login.ts b/src/m365/commands/login.ts index 60649cce0dc..90a1a6247f0 100644 --- a/src/m365/commands/login.ts +++ b/src/m365/commands/login.ts @@ -10,25 +10,24 @@ import { settingsNames } from '../../settingsNames.js'; import { zod } from '../../utils/zod.js'; import commands from './commands.js'; -const options = globalOptionsZod - .extend({ - authType: zod.alias('t', zod.coercedEnum(AuthType).optional()), - cloud: zod.coercedEnum(CloudType).optional().default(CloudType.Public), - userName: zod.alias('u', z.string().optional()), - password: zod.alias('p', z.string().optional()), - certificateFile: zod.alias('c', z.string().optional() - .refine(filePath => !filePath || fs.existsSync(filePath), filePath => ({ - message: `Certificate file ${filePath} does not exist` - }))), - certificateBase64Encoded: z.string().optional(), - thumbprint: z.string().optional(), - appId: z.string().optional(), - tenant: z.string().optional(), - secret: zod.alias('s', z.string().optional()), - connectionName: z.string().optional(), - ensure: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + authType: zod.coercedEnum(AuthType).optional().alias('t'), + cloud: zod.coercedEnum(CloudType).optional().default(CloudType.Public), + userName: z.string().optional().alias('u'), + password: z.string().optional().alias('p'), + certificateFile: z.string().optional().alias('c') + .refine(filePath => !filePath || fs.existsSync(filePath), { + error: e => `Certificate file ${e.input} does not exist` + }), + certificateBase64Encoded: z.string().optional(), + thumbprint: z.string().optional(), + appId: z.string().optional(), + tenant: z.string().optional(), + secret: z.string().optional().alias('s'), + connectionName: z.string().optional(), + ensure: z.boolean().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -44,26 +43,26 @@ class LoginCommand extends Command { return 'Log in to Microsoft 365'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => typeof options.appId !== 'undefined' || cli.getClientId() || options.authType === 'identity' || options.authType === 'federatedIdentity', { - message: `appId is required. TIP: use the "m365 setup" command to configure the default appId.`, + error: `appId is required. TIP: use the "m365 setup" command to configure the default appId.`, path: ['appId'] }) .refine(options => options.authType !== 'password' || options.userName, { - message: 'Username is required when using password authentication.', + error: 'Username is required when using password authentication.', path: ['userName'] }) .refine(options => options.authType !== 'password' || options.password, { - message: 'Password is required when using password authentication.', + error: 'Password is required when using password authentication.', path: ['password'] }) .refine(options => options.authType !== 'certificate' || !(options.certificateFile && options.certificateBase64Encoded), { - message: 'Specify either certificateFile or certificateBase64Encoded, but not both.', + error: 'Specify either certificateFile or certificateBase64Encoded, but not both.', path: ['certificateBase64Encoded'] }) .refine(options => options.authType !== 'certificate' || @@ -71,13 +70,13 @@ class LoginCommand extends Command { options.certificateBase64Encoded || cli.getConfig().get(settingsNames.clientCertificateFile) || cli.getConfig().get(settingsNames.clientCertificateBase64Encoded), { - message: 'Specify either certificateFile or certificateBase64Encoded.', + error: 'Specify either certificateFile or certificateBase64Encoded.', path: ['certificateFile'] }) .refine(options => options.authType !== 'secret' || options.secret || cli.getConfig().get(settingsNames.clientSecret), { - message: 'Secret is required when using secret authentication.', + error: 'Secret is required when using secret authentication.', path: ['secret'] }); } diff --git a/src/m365/commands/logout.spec.ts b/src/m365/commands/logout.spec.ts index 8a3bb326ae3..69e106369b6 100644 --- a/src/m365/commands/logout.spec.ts +++ b/src/m365/commands/logout.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -11,14 +10,14 @@ import { pid } from '../../utils/pid.js'; import { session } from '../../utils/session.js'; import { sinonUtil } from '../../utils/sinonUtil.js'; import commands from './commands.js'; -import command from './logout.js'; +import command, { options } from './logout.js'; describe(commands.LOGOUT, () => { let log: string[]; let logger: Logger; let authClearConnectionInfoStub: sinon.SinonStub; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').callsFake(() => Promise.resolve()); @@ -28,7 +27,7 @@ describe(commands.LOGOUT, () => { sinon.stub(session, 'getId').callsFake(() => ''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -136,7 +135,7 @@ describe(commands.LOGOUT, () => { sinon.stub(auth, 'restoreAuth').callsFake(() => Promise.reject('An error has occurred')); try { - await assert.rejects(command.action(logger, commandOptionsSchema.parse({})), new CommandError('An error has occurred')); + await assert.rejects(command.action(logger, { options: commandOptionsSchema.parse({}) }), new CommandError('An error has occurred')); } finally { sinonUtil.restore([ diff --git a/src/m365/commands/logout.ts b/src/m365/commands/logout.ts index 3554b18a56c..9bef34483f0 100644 --- a/src/m365/commands/logout.ts +++ b/src/m365/commands/logout.ts @@ -4,7 +4,7 @@ import { Logger } from '../../cli/Logger.js'; import Command, { CommandError, globalOptionsZod } from '../../Command.js'; import commands from './commands.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); declare type Options = z.infer; diff --git a/src/m365/commands/status.spec.ts b/src/m365/commands/status.spec.ts index e2ab6f9d9a3..e9d1e0f3e82 100644 --- a/src/m365/commands/status.spec.ts +++ b/src/m365/commands/status.spec.ts @@ -1,18 +1,17 @@ import assert from 'assert'; import sinon from 'sinon'; -import { z } from 'zod'; import auth, { AuthType, CertificateType, CloudType } from '../../Auth.js'; -import { CommandError } from '../../Command.js'; import { cli } from '../../cli/cli.js'; import { CommandInfo } from '../../cli/CommandInfo.js'; import { Logger } from '../../cli/Logger.js'; +import { CommandError } from '../../Command.js'; import { telemetry } from '../../telemetry.js'; import { accessToken } from '../../utils/accessToken.js'; import { pid } from '../../utils/pid.js'; import { session } from '../../utils/session.js'; import { sinonUtil } from '../../utils/sinonUtil.js'; import commands from './commands.js'; -import command from './status.js'; +import command, { options } from './status.js'; describe(commands.STATUS, () => { let log: any[]; @@ -20,16 +19,16 @@ describe(commands.STATUS, () => { let loggerLogSpy: sinon.SinonSpy; let loggerLogToStderrSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').callsFake(() => Promise.resolve()); sinon.stub(telemetry, 'trackEvent').resolves(); sinon.stub(pid, 'getProcessName').callsFake(() => ''); sinon.stub(session, 'getId').callsFake(() => ''); - + commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/commands/status.ts b/src/m365/commands/status.ts index af8bb2bb001..142316d64ff 100644 --- a/src/m365/commands/status.ts +++ b/src/m365/commands/status.ts @@ -4,8 +4,7 @@ import { Logger } from '../../cli/Logger.js'; import Command, { CommandArgs, CommandError, globalOptionsZod } from '../../Command.js'; import commands from './commands.js'; -const options = globalOptionsZod.strict(); - +export const options = z.strictObject({ ...globalOptionsZod.shape }); class StatusCommand extends Command { public get name(): string { @@ -16,7 +15,7 @@ class StatusCommand extends Command { return 'Shows Microsoft 365 login status'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/connection/commands/connection-list.spec.ts b/src/m365/connection/commands/connection-list.spec.ts index 407e81e53da..91d053cced1 100644 --- a/src/m365/connection/commands/connection-list.spec.ts +++ b/src/m365/connection/commands/connection-list.spec.ts @@ -1,25 +1,24 @@ import assert from 'assert'; import sinon from 'sinon'; -import { z } from 'zod'; import auth, { AuthType, CertificateType, CloudType } from '../../../Auth.js'; import { cli } from '../../../cli/cli.js'; import { CommandInfo } from '../../../cli/CommandInfo.js'; import { Logger } from '../../../cli/Logger.js'; +import { CommandError } from '../../../Command.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 { spo } from '../../../utils/spo.js'; import commands from '../commands.js'; -import command from './connection-list.js'; -import { sinonUtil } from '../../../utils/sinonUtil.js'; -import { CommandError } from '../../../Command.js'; +import command, { options } from './connection-list.js'; describe(commands.LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const mockListResponse = [ { @@ -93,7 +92,7 @@ describe(commands.LIST, () => { ]; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/connection/commands/connection-list.ts b/src/m365/connection/commands/connection-list.ts index 43e2413c38c..ae1a46585b0 100644 --- a/src/m365/connection/commands/connection-list.ts +++ b/src/m365/connection/commands/connection-list.ts @@ -5,7 +5,7 @@ import { Logger } from '../../../cli/Logger.js'; import Command, { CommandArgs, CommandError, globalOptionsZod } from '../../../Command.js'; import commands from '../commands.js'; -const options = globalOptionsZod.strict(); +export const options = z.strictObject({ ...globalOptionsZod.shape }); class ConnectionListCommand extends Command { public get name(): string { @@ -16,7 +16,7 @@ class ConnectionListCommand extends Command { return 'Show the list of available connections'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/context/commands/context-remove.spec.ts b/src/m365/context/commands/context-remove.spec.ts index cd68a1e2943..bd01cc8aaf8 100644 --- a/src/m365/context/commands/context-remove.spec.ts +++ b/src/m365/context/commands/context-remove.spec.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import fs from 'fs'; import sinon from 'sinon'; -import { z } from 'zod'; import { cli } from '../../../cli/cli.js'; import { CommandInfo } from '../../../cli/CommandInfo.js'; import { Logger } from '../../../cli/Logger.js'; @@ -9,19 +8,19 @@ import { CommandError } from '../../../Command.js'; import { telemetry } from '../../../telemetry.js'; import { sinonUtil } from '../../../utils/sinonUtil.js'; import commands from '../commands.js'; -import command from './context-remove.js'; +import command, { options } from './context-remove.js'; describe(commands.REMOVE, () => { let log: any[]; let logger: Logger; let promptIssued: boolean = false; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(telemetry, 'trackEvent').resolves(); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/context/commands/context-remove.ts b/src/m365/context/commands/context-remove.ts index 50c1dd67ec1..1581a56d3e8 100644 --- a/src/m365/context/commands/context-remove.ts +++ b/src/m365/context/commands/context-remove.ts @@ -3,16 +3,14 @@ import { z } from 'zod'; import { cli } from '../../../cli/cli.js'; import { Logger } from '../../../cli/Logger.js'; import { CommandError, globalOptionsZod } from '../../../Command.js'; -import { zod } from '../../../utils/zod.js'; import AnonymousCommand from '../../base/AnonymousCommand.js'; import { M365RcJson } from '../../base/M365RcJson.js'; import commands from '../commands.js'; -const options = globalOptionsZod - .extend({ - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -29,7 +27,7 @@ class ContextRemoveCommand extends AnonymousCommand { return 'Removes the CLI for Microsoft 365 context in the current working folder'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/context/commands/option/option-remove.spec.ts b/src/m365/context/commands/option/option-remove.spec.ts index 5a8980b3bd4..d6c992efef6 100644 --- a/src/m365/context/commands/option/option-remove.spec.ts +++ b/src/m365/context/commands/option/option-remove.spec.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import fs from 'fs'; import sinon from 'sinon'; -import { z } from 'zod'; import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -9,19 +8,19 @@ import { CommandError } from '../../../../Command.js'; import { telemetry } from '../../../../telemetry.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './option-remove.js'; +import command, { options } from './option-remove.js'; describe(commands.OPTION_REMOVE, () => { let log: any[]; let logger: Logger; let promptIssued: boolean = false; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(telemetry, 'trackEvent').resolves(); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/context/commands/option/option-remove.ts b/src/m365/context/commands/option/option-remove.ts index 5386720aa3a..c7847b57b10 100644 --- a/src/m365/context/commands/option/option-remove.ts +++ b/src/m365/context/commands/option/option-remove.ts @@ -3,17 +3,15 @@ import { z } from 'zod'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError, globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import ContextCommand from '../../../base/ContextCommand.js'; import { M365RcJson } from '../../../base/M365RcJson.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string()), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().alias('n'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -30,7 +28,7 @@ class ContextOptionRemoveCommand extends ContextCommand { return 'Removes an already available name from local context file.'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-add.spec.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-add.spec.ts index f3afffabbba..d01f49a6741 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-add.spec.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-add.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -13,6 +12,7 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; import command from './administrativeunit-add.js'; +import { options } from './administrativeunit-get.js'; describe(commands.ADMINISTRATIVEUNIT_ADD, () => { const administrativeUnitReponse: any = { @@ -34,7 +34,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -43,7 +43,7 @@ describe(commands.ADMINISTRATIVEUNIT_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-add.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-add.ts index 5c896d223e3..4cf446969d5 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-add.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-add.ts @@ -3,17 +3,15 @@ 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(); +const options = z.looseObject({ + ...globalOptionsZod.shape, + displayName: z.string().alias('n'), + description: z.string().optional().alias('d'), + hiddenMembership: z.boolean().optional() +}); declare type Options = z.infer; @@ -34,7 +32,7 @@ class EntraAdministrativeUnitAddCommand extends GraphCommand { return true; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-get.spec.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-get.spec.ts index 7e215275776..7de0571c775 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-get.spec.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-get.spec.ts @@ -1,26 +1,25 @@ 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 { Logger } from "../../../../cli/Logger.js"; -import commands from "../../commands.js"; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import { cli } from '../../../../cli/cli.js'; -import command from './administrativeunit-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 commands from "../../commands.js"; +import command, { options } from './administrativeunit-get.js'; describe(commands.ADMINISTRATIVEUNIT_GET, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const administrativeUnitsReponse = { value: [ { @@ -45,7 +44,7 @@ describe(commands.ADMINISTRATIVEUNIT_GET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-get.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-get.ts index 7806b79bbb3..a3b59869bdb 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-get.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-get.ts @@ -6,15 +6,13 @@ import GraphCommand from "../../../base/GraphCommand.js"; import commands from "../../commands.js"; import { entraAdministrativeUnit } from "../../../../utils/entraAdministrativeUnit.js"; import { globalOptionsZod } from "../../../../Command.js"; -import { zod } from "../../../../utils/zod.js"; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().uuid().optional()), - displayName: zod.alias('n', z.string().optional()), - properties: zod.alias('p', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + displayName: z.string().optional().alias('n'), + properties: z.string().optional().alias('p') +}); declare type Options = z.infer; @@ -31,14 +29,14 @@ class EntraAdministrativeUnitGetCommand extends GraphCommand { return 'Gets information about a specific administrative unit'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.id, options.displayName].filter(Boolean).length === 1, { - message: 'Specify either id or displayName' + error: 'Specify either id or displayName' }); } diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-list.spec.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-list.spec.ts index 73a4f930e70..be3bc192fca 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-list.spec.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-list.spec.ts @@ -1,7 +1,7 @@ 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 { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; @@ -10,16 +10,15 @@ import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { cli } from '../../../../cli/cli.js'; import commands from '../../commands.js'; -import command from './administrativeunit-list.js'; +import command, { options } from './administrativeunit-list.js'; describe(commands.ADMINISTRATIVEUNIT_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -28,7 +27,7 @@ describe(commands.ADMINISTRATIVEUNIT_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-list.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-list.ts index 8183115d8a5..3a265a52581 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-list.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-list.ts @@ -3,14 +3,13 @@ import { AdministrativeUnit } from '@microsoft/microsoft-graph-types'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import { odata } from '../../../../utils/odata.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - properties: zod.alias('p', z.string().optional()) - }).strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + properties: z.string().optional().alias('p') +}); declare type Options = z.infer; interface CommandArgs { @@ -26,7 +25,7 @@ class EntraAdministrativeUnitListCommand extends GraphCommand { return 'Retrieves a list of administrative units'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-remove.spec.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-remove.spec.ts index 3f9f4c34f88..8e3f0712d3e 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-remove.spec.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-remove.spec.ts @@ -1,19 +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 { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { telemetry } from '../../../../telemetry.js'; -import request from '../../../../request.js'; -import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; import commands from '../../commands.js'; -import command from './administrativeunit-remove.js'; +import command, { options } from './administrativeunit-remove.js'; describe(commands.ADMINISTRATIVEUNIT_REMOVE, () => { const administrativeUnitId = 'fc33aa61-cf0e-46b6-9506-f633347202ab'; @@ -22,7 +21,7 @@ describe(commands.ADMINISTRATIVEUNIT_REMOVE, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let promptIssued: boolean; before(() => { @@ -32,7 +31,7 @@ describe(commands.ADMINISTRATIVEUNIT_REMOVE, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/administrativeunit/administrativeunit-remove.ts b/src/m365/entra/commands/administrativeunit/administrativeunit-remove.ts index e7c584a16b3..0c5a63c1a60 100644 --- a/src/m365/entra/commands/administrativeunit/administrativeunit-remove.ts +++ b/src/m365/entra/commands/administrativeunit/administrativeunit-remove.ts @@ -4,17 +4,15 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().uuid().optional()), - displayName: zod.alias('n', z.string().optional()), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + displayName: z.string().optional().alias('n'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -30,17 +28,17 @@ class EntraAdministrativeUnitRemoveCommand extends GraphCommand { return 'Removes an administrative unit'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => options.id || options.displayName, { - message: 'Specify either id or displayName' + error: 'Specify either id or displayName' }) .refine(options => !(options.id && options.displayName), { - message: 'Specify either id or displayName but not both' + error: 'Specify either id or displayName but not both' }); } diff --git a/src/m365/entra/commands/license/license-list.spec.ts b/src/m365/entra/commands/license/license-list.spec.ts index bc60a8ce56d..8a0047c2474 100644 --- a/src/m365/entra/commands/license/license-list.spec.ts +++ b/src/m365/entra/commands/license/license-list.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,7 +11,7 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './license-list.js'; +import command, { options } from './license-list.js'; describe(commands.LICENSE_LIST, () => { //#region Mocked Responses @@ -70,7 +69,7 @@ describe(commands.LICENSE_LIST, () => { let loggerLogSpy: sinon.SinonSpy; let loggerStderrSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -79,7 +78,7 @@ describe(commands.LICENSE_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/license/license-list.ts b/src/m365/entra/commands/license/license-list.ts index a76f46c8dc4..37007768a95 100644 --- a/src/m365/entra/commands/license/license-list.ts +++ b/src/m365/entra/commands/license/license-list.ts @@ -5,7 +5,7 @@ import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { globalOptionsZod } from '../../../../Command.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); class EntraLicenseListCommand extends GraphCommand { public get name(): string { diff --git a/src/m365/entra/commands/multitenant/multitenant-add.spec.ts b/src/m365/entra/commands/multitenant/multitenant-add.spec.ts index 336cd714504..02de5be4528 100644 --- a/src/m365/entra/commands/multitenant/multitenant-add.spec.ts +++ b/src/m365/entra/commands/multitenant/multitenant-add.spec.ts @@ -1,18 +1,17 @@ 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 './multitenant-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, { options } from './multitenant-add.js'; describe(commands.MULTITENANT_ADD, () => { const multitenantOrganizationShortReponse = { @@ -34,7 +33,7 @@ describe(commands.MULTITENANT_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -43,7 +42,7 @@ describe(commands.MULTITENANT_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/multitenant/multitenant-add.ts b/src/m365/entra/commands/multitenant/multitenant-add.ts index 1823f88478c..d5c0718676b 100644 --- a/src/m365/entra/commands/multitenant/multitenant-add.ts +++ b/src/m365/entra/commands/multitenant/multitenant-add.ts @@ -5,12 +5,11 @@ 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 +export const options = globalOptionsZod .extend({ - displayName: zod.alias('n', z.string()), - description: zod.alias('d', z.string().optional()) + displayName: z.string().alias('n'), + description: z.string().optional().alias('d') }).strict(); declare type Options = z.infer; diff --git a/src/m365/entra/commands/organization/organization-list.spec.ts b/src/m365/entra/commands/organization/organization-list.spec.ts index b90ffe75808..ea7c8019e20 100644 --- a/src/m365/entra/commands/organization/organization-list.spec.ts +++ b/src/m365/entra/commands/organization/organization-list.spec.ts @@ -1,18 +1,17 @@ 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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.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 { cli } from '../../../../cli/cli.js'; -import command from './organization-list.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command, { options } from './organization-list.js'; describe(commands.ORGANIZATION_LIST, () => { const response = { @@ -75,7 +74,7 @@ describe(commands.ORGANIZATION_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -84,7 +83,7 @@ describe(commands.ORGANIZATION_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -142,7 +141,7 @@ describe(commands.ORGANIZATION_LIST, () => { const parsedSchema = commandOptionsSchema.safeParse({ verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly([response])); }); @@ -163,7 +162,7 @@ describe(commands.ORGANIZATION_LIST, () => { properties: 'id,displayName', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly([response])); }); @@ -185,6 +184,6 @@ describe(commands.ORGANIZATION_LIST, () => { const parsedSchema = commandOptionsSchema.safeParse({ verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('Request Authorization failed')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('Request Authorization failed')); }); }); \ No newline at end of file diff --git a/src/m365/entra/commands/organization/organization-list.ts b/src/m365/entra/commands/organization/organization-list.ts index c8aa3a92494..1819b05c30b 100644 --- a/src/m365/entra/commands/organization/organization-list.ts +++ b/src/m365/entra/commands/organization/organization-list.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; @@ -8,11 +7,10 @@ import { CliRequestOptions } from '../../../../request.js'; import { Organization } from '@microsoft/microsoft-graph-types'; import { odata } from '../../../../utils/odata.js'; -const options = globalOptionsZod - .extend({ - properties: zod.alias('p', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + properties: z.string().optional().alias('p') +}); declare type Options = z.infer; interface CommandArgs { @@ -32,7 +30,7 @@ class EntraOrganizationListCommand extends GraphCommand { return ['id', 'displayName', 'tenantType']; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/entra/commands/organization/organization-set.spec.ts b/src/m365/entra/commands/organization/organization-set.spec.ts index 4f7df1743b2..fd6c1af4bb6 100644 --- a/src/m365/entra/commands/organization/organization-set.spec.ts +++ b/src/m365/entra/commands/organization/organization-set.spec.ts @@ -1,18 +1,17 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './organization-set.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; +import commands from '../../commands.js'; +import command, { options } from './organization-set.js'; describe(commands.ORGANIZATION_SET, () => { const organizationId = '84841066-274d-4ec0-a5c1-276be684bdd3'; @@ -21,7 +20,7 @@ describe(commands.ORGANIZATION_SET, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -30,7 +29,7 @@ describe(commands.ORGANIZATION_SET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -148,7 +147,7 @@ describe(commands.ORGANIZATION_SET, () => { statementUrl: 'https://contoso.com/privacyStatement', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(patchRequestStub.called); }); @@ -183,7 +182,7 @@ describe(commands.ORGANIZATION_SET, () => { contactEmail: 'contact@contoso.com', statementUrl: 'https://contoso.com/privacyStatement' }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(patchRequestStub.called); }); @@ -211,7 +210,7 @@ describe(commands.ORGANIZATION_SET, () => { statementUrl: 'https://contoso.com/privacyStatement' }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError(`The specified organization '${organizationName}' does not exist.`)); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError(`The specified organization '${organizationName}' does not exist.`)); }); it('correctly handles API OData error', async () => { @@ -232,7 +231,7 @@ describe(commands.ORGANIZATION_SET, () => { marketingNotificationEmails: 'marketing@contoso.com' }); await assert.rejects(command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! }), new CommandError('Invalid tenant identifier; it must match that of the requested tenant.')); }); }); \ No newline at end of file diff --git a/src/m365/entra/commands/organization/organization-set.ts b/src/m365/entra/commands/organization/organization-set.ts index da0b769c779..cb7a75617c7 100644 --- a/src/m365/entra/commands/organization/organization-set.ts +++ b/src/m365/entra/commands/organization/organization-set.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; @@ -9,28 +8,25 @@ import request, { CliRequestOptions } from '../../../../request.js'; import { Organization } from '@microsoft/microsoft-graph-types'; import { odata } from '../../../../utils/odata.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional()), - displayName: zod.alias('d', z.string().optional()), - marketingNotificationEmails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, invalidEmails => ({ - message: `The following marketing notification emails are invalid: ${invalidEmails}.` - })).transform((value) => value.split(',')).optional(), - securityComplianceNotificationMails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, invalidEmails => ({ - message: `The following security compliance notification emails are invalid: ${invalidEmails}.` - })).transform((value) => value.split(',')).optional(), - securityComplianceNotificationPhones: z.string().transform((value) => value.split(',')).optional(), - technicalNotificationMails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, invalidEmails => ({ - message: `The following technical notification emails are invalid: ${invalidEmails}.` - })).transform((value) => value.split(',')).optional(), - contactEmail: z.string().refine(id => validation.isValidUserPrincipalName(id), id => ({ - message: `'${id}' is not a valid email.` - })).optional(), - statementUrl: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + displayName: z.string().optional().alias('d'), + marketingNotificationEmails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, { + error: e => `The following marketing notification emails are invalid: ${e.input}.` + }).transform((value) => value.split(',')).optional(), + securityComplianceNotificationMails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, { + error: e => `The following security compliance notification emails are invalid: ${e.input}.` + }).transform((value) => value.split(',')).optional(), + securityComplianceNotificationPhones: z.string().transform((value) => value.split(',')).optional(), + technicalNotificationMails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, { + error: e => `The following technical notification emails are invalid: ${e.input}.` + }).transform((value) => value.split(',')).optional(), + contactEmail: z.string().refine(id => validation.isValidUserPrincipalName(id), { + error: e => `'${e.input}' is not a valid email.` + }).optional(), + statementUrl: z.string().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -46,21 +42,22 @@ class EntraOrganizationSetCommand extends GraphCommand { return 'Updates info about the organization'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !(options.id && options.displayName), { - message: 'Specify either id or displayName, but not both' + error: 'Specify either id or displayName, but not both' }) .refine(options => options.id || options.displayName, { - message: 'Specify either id or displayName' + error: 'Specify either id or displayName' }) - .refine(options => [options.contactEmail, options.marketingNotificationEmails, options.securityComplianceNotificationMails, options.securityComplianceNotificationPhones, + .refine(options => [ + options.contactEmail, options.marketingNotificationEmails, options.securityComplianceNotificationMails, options.securityComplianceNotificationPhones, options.statementUrl, options.technicalNotificationMails].filter(o => o !== undefined).length > 0, { - message: 'Specify at least one of the following options: contactEmail, marketingNotificationEmails, securityComplianceNotificationMails, securityComplianceNotificationPhones, statementUrl, or technicalNotificationMails' + error: 'Specify at least one of the following options: contactEmail, marketingNotificationEmails, securityComplianceNotificationMails, securityComplianceNotificationPhones, statementUrl, or technicalNotificationMails' }); } diff --git a/src/m365/entra/commands/roleassignment/roleassignment-add.spec.ts b/src/m365/entra/commands/roleassignment/roleassignment-add.spec.ts index 5dd91be47b4..3b714174738 100644 --- a/src/m365/entra/commands/roleassignment/roleassignment-add.spec.ts +++ b/src/m365/entra/commands/roleassignment/roleassignment-add.spec.ts @@ -1,25 +1,24 @@ +import { AdministrativeUnit, Application, ServicePrincipal, UnifiedRoleAssignment, UnifiedRoleDefinition } from '@microsoft/microsoft-graph-types'; import assert from 'assert'; import sinon from 'sinon'; -import { z } from 'zod'; import auth from '../../../../Auth.js'; -import { Logger } from '../../../../cli/Logger.js'; -import { telemetry } from '../../../../telemetry.js'; -import { pid } from '../../../../utils/pid.js'; -import { session } from '../../../../utils/session.js'; import { cli } from '../../../../cli/cli.js'; -import { roleDefinition } from '../../../../utils/roleDefinition.js'; -import commands from '../../commands.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; -import { entraUser } from '../../../../utils/entraUser.js'; -import { entraGroup } from '../../../../utils/entraGroup.js'; +import { telemetry } from '../../../../telemetry.js'; import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; -import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; import { entraApp } from '../../../../utils/entraApp.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import command from './roleassignment-add.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; +import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; +import { entraUser } from '../../../../utils/entraUser.js'; +import { pid } from '../../../../utils/pid.js'; +import { roleDefinition } from '../../../../utils/roleDefinition.js'; +import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { AdministrativeUnit, Application, ServicePrincipal, UnifiedRoleAssignment, UnifiedRoleDefinition } from '@microsoft/microsoft-graph-types'; +import commands from '../../commands.js'; +import command, { options } from './roleassignment-add.js'; describe(commands.ROLEASSIGNMENT_ADD, () => { const principalId = 'fc33aa61-cf0e-46b6-9506-f633347202ab'; @@ -98,7 +97,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -107,7 +106,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -824,7 +823,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeTenant)); }); @@ -849,7 +848,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeTenant)); }); @@ -874,7 +873,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeUser)); }); @@ -900,7 +899,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeUser)); }); @@ -925,7 +924,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeAdministrativeUnit)); }); @@ -951,7 +950,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeAdministrativeUnit)); }); @@ -976,7 +975,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeApplication)); }); @@ -1002,7 +1001,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeApplication)); }); @@ -1028,7 +1027,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeApplication)); }); @@ -1053,7 +1052,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeServicePrincipal)); }); @@ -1079,7 +1078,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeServicePrincipal)); }); @@ -1104,7 +1103,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeGroup)); }); @@ -1130,7 +1129,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeGroup)); }); @@ -1155,7 +1154,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeTenant)); }); @@ -1180,7 +1179,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeTenant)); }); @@ -1201,7 +1200,7 @@ describe(commands.ROLEASSIGNMENT_ADD, () => { principal: principalId }); await assert.rejects(command.action(logger, { - options: result.data + options: result.data! }), new CommandError('Invalid request')); }); }); \ No newline at end of file diff --git a/src/m365/entra/commands/roleassignment/roleassignment-add.ts b/src/m365/entra/commands/roleassignment/roleassignment-add.ts index ac22cf849fd..aecfc48d422 100644 --- a/src/m365/entra/commands/roleassignment/roleassignment-add.ts +++ b/src/m365/entra/commands/roleassignment/roleassignment-add.ts @@ -13,42 +13,27 @@ import { entraGroup } from '../../../../utils/entraGroup.js'; import { roleDefinition } from '../../../../utils/roleDefinition.js'; import { entraUser } from '../../../../utils/entraUser.js'; -const options = globalOptionsZod - .extend({ - roleDefinitionId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - roleDefinitionName: z.string().optional(), - principal: z.string().refine(principal => validation.isValidGuid(principal) || validation.isValidUserPrincipalName(principal) || validation.isValidMailNickname(principal), principal => ({ - message: `'${principal}' is not a valid GUID, UPN or group mail nickname.` - })), - userId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - userName: z.string().refine(upn => validation.isValidUserPrincipalName(upn), upn => ({ - message: `'${upn}' is not a valid UPN.` - })).optional(), - administrativeUnitId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - administrativeUnitName: z.string().optional(), - applicationId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - applicationObjectId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - applicationName: z.string().optional(), - servicePrincipalId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - servicePrincipalName: z.string().optional(), - groupId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - groupName: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + roleDefinitionId: z.uuid().optional(), + roleDefinitionName: z.string().optional(), + principal: z.string().refine(principal => validation.isValidGuid(principal) || validation.isValidUserPrincipalName(principal) || validation.isValidMailNickname(principal), { + error: e => `'${e.input}' is not a valid GUID, UPN or group mail nickname.` + }), + userId: z.uuid().optional(), + userName: z.string().refine(upn => validation.isValidUserPrincipalName(upn), { + error: e => `'${e.input}' is not a valid UPN.` + }).optional(), + administrativeUnitId: z.uuid().optional(), + administrativeUnitName: z.string().optional(), + applicationId: z.uuid().optional(), + applicationObjectId: z.uuid().optional(), + applicationName: z.string().optional(), + servicePrincipalId: z.uuid().optional(), + servicePrincipalName: z.string().optional(), + groupId: z.uuid().optional(), + groupName: z.string().optional() +}); declare type Options = z.infer; @@ -65,16 +50,17 @@ class EntraRoleAssignmentAddCommand extends GraphCommand { return 'Assign a Entra ID role to a user and specify the scope for which the user has been granted access'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.roleDefinitionId, options.roleDefinitionName].filter(o => o !== undefined).length === 1, { - message: 'Specify either roleDefinitionId or roleDefinitionName' + error: 'Specify either roleDefinitionId or roleDefinitionName' }) - .refine(options => Object.values([options.userId, options.userName, options.administrativeUnitId, options.administrativeUnitName, options.applicationId, options.applicationObjectId, options.applicationName, + .refine(options => Object.values([ + options.userId, options.userName, options.administrativeUnitId, options.administrativeUnitName, options.applicationId, options.applicationObjectId, options.applicationName, options.servicePrincipalId, options.servicePrincipalName, options.groupId, options.groupName]).filter(v => typeof v !== 'undefined').length < 2, { message: 'Provide value for only one of the following parameters: userId, userName, administrativeUnitId, administrativeUnitName, applicationId, applicationObjectId, applicationName, servicePrincipalId, servicePrincipalName, groupId or groupName' }); diff --git a/src/m365/entra/commands/roledefinition/roledefinition-add.spec.ts b/src/m365/entra/commands/roledefinition/roledefinition-add.spec.ts index ee576530f47..91d39649396 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-add.spec.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-add.spec.ts @@ -1,18 +1,17 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './roledefinition-add.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; +import commands from '../../commands.js'; +import command, { options } from './roledefinition-add.js'; describe(commands.ROLEDEFINITION_ADD, () => { const roleDefinitionResponse = { @@ -63,7 +62,7 @@ describe(commands.ROLEDEFINITION_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -72,7 +71,7 @@ describe(commands.ROLEDEFINITION_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -134,7 +133,7 @@ describe(commands.ROLEDEFINITION_ADD, () => { displayName: 'Custom Role', allowedResourceActions: "microsoft.directory/groups.unified/create,microsoft.directory/groups.unified/delete" }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(roleDefinitionResponse)); }); @@ -156,7 +155,7 @@ describe(commands.ROLEDEFINITION_ADD, () => { verbose: true }); await command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(roleDefinitionWithDetailsResponse)); }); @@ -178,7 +177,7 @@ describe(commands.ROLEDEFINITION_ADD, () => { allowedResourceActions: "microsoft.directory/groups.unified/create" }); await assert.rejects(command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! }), new CommandError('Invalid request')); }); }); \ No newline at end of file diff --git a/src/m365/entra/commands/roledefinition/roledefinition-add.ts b/src/m365/entra/commands/roledefinition/roledefinition-add.ts index 5af5e485278..361bf2d59e6 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-add.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-add.ts @@ -1,21 +1,19 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { Logger } from '../../../../cli/Logger.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { UnifiedRoleDefinition } from '@microsoft/microsoft-graph-types'; -const options = globalOptionsZod - .extend({ - displayName: zod.alias('n', z.string()), - allowedResourceActions: zod.alias('a', z.string().transform((value) => value.split(',').map(String))), - description: zod.alias('d', z.string().optional()), - enabled: zod.alias('e', z.boolean().optional()), - version: zod.alias('v', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + displayName: z.string().alias('n'), + allowedResourceActions: z.string().transform((value) => value.split(',').map(String)).alias('a'), + description: z.string().optional().alias('d'), + enabled: z.boolean().optional().alias('e'), + version: z.string().optional().alias('v') +}); declare type Options = z.infer; @@ -32,7 +30,7 @@ class EntraRoleDefinitionAddCommand extends GraphCommand { return 'Creates a custom Microsoft Entra ID role definition'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/entra/commands/roledefinition/roledefinition-get.spec.ts b/src/m365/entra/commands/roledefinition/roledefinition-get.spec.ts index 34cb0ce2825..6485a93416b 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-get.spec.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-get.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './roledefinition-get.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { formatting } from '../../../../utils/formatting.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; +import commands from '../../commands.js'; +import command, { options } from './roledefinition-get.js'; describe(commands.ROLEDEFINITION_GET, () => { const roleId = 'abcd1234-de71-4623-b4af-96380a352509'; @@ -52,7 +51,7 @@ describe(commands.ROLEDEFINITION_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -61,7 +60,7 @@ describe(commands.ROLEDEFINITION_GET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/roledefinition/roledefinition-get.ts b/src/m365/entra/commands/roledefinition/roledefinition-get.ts index 3bdf6ffe951..7245fd02e84 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-get.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-get.ts @@ -4,17 +4,15 @@ import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { roleDefinition } from '../../../../utils/roleDefinition.js'; import { validation } from '../../../../utils/validation.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().optional()), - displayName: zod.alias('n', z.string().optional()), - properties: zod.alias('p', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + displayName: z.string().optional().alias('n'), + properties: z.string().optional().alias('p') +}); declare type Options = z.infer; @@ -31,22 +29,22 @@ class EntraRoleDefinitionGetCommand extends GraphCommand { return 'Gets a specific Microsoft Entra ID role definition'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.id !== !options.displayName, { - message: 'Specify either id or displayName, but not both' + error: 'Specify either id or displayName, but not both' }) .refine(options => options.id || options.displayName, { - message: 'Specify either id or displayName' + error: 'Specify either id or displayName' }) - .refine(options => (!options.id && !options.displayName) || options.displayName || (options.id && validation.isValidGuid(options.id)), options => ({ - message: `The '${options.id}' must be a valid GUID`, + .refine(options => (!options.id && !options.displayName) || options.displayName || (options.id && validation.isValidGuid(options.id)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['id'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/entra/commands/roledefinition/roledefinition-list.ts b/src/m365/entra/commands/roledefinition/roledefinition-list.ts index 846291ef071..ce0c2bf8564 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-list.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-list.ts @@ -5,14 +5,12 @@ import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; -const options = globalOptionsZod - .extend({ - properties: zod.alias('p', z.string().optional()), - filter: zod.alias('f', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + properties: z.string().optional().alias('p'), + filter: z.string().optional().alias('f') +}); declare type Options = z.infer; @@ -33,7 +31,7 @@ class EntraRoleDefinitionListCommand extends GraphCommand { return ['id', 'displayName', 'isBuiltIn', 'isEnabled']; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/entra/commands/roledefinition/roledefinition-remove.spec.ts b/src/m365/entra/commands/roledefinition/roledefinition-remove.spec.ts index 8e140f8e893..63600aad22e 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-remove.spec.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-remove.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; +import { roleDefinition } from '../../../../utils/roleDefinition.js'; import { session } from '../../../../utils/session.js'; -import command from './roledefinition-remove.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; -import { roleDefinition } from '../../../../utils/roleDefinition.js'; +import commands from '../../commands.js'; +import command, { options } from './roledefinition-remove.js'; describe(commands.ROLEDEFINITION_REMOVE, () => { const roleId = 'abcd1234-de71-4623-b4af-96380a352509'; @@ -23,7 +22,7 @@ describe(commands.ROLEDEFINITION_REMOVE, () => { let logger: Logger; let promptIssued: boolean; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -32,7 +31,7 @@ describe(commands.ROLEDEFINITION_REMOVE, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/entra/commands/roledefinition/roledefinition-remove.ts b/src/m365/entra/commands/roledefinition/roledefinition-remove.ts index ed79eb4a228..e1a08117705 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-remove.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-remove.ts @@ -3,19 +3,17 @@ import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { roleDefinition } from '../../../../utils/roleDefinition.js'; import { validation } from '../../../../utils/validation.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { cli } from '../../../../cli/cli.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().optional()), - displayName: zod.alias('n', z.string().optional()), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + displayName: z.string().optional().alias('n'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -32,22 +30,22 @@ class EntraRoleDefinitionRemoveCommand extends GraphCommand { return 'Removes a specific Microsoft Entra ID role definition'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.id !== !options.displayName, { - message: 'Specify either id or displayName, but not both' + error: 'Specify either id or displayName, but not both' }) .refine(options => options.id || options.displayName, { - message: 'Specify either id or displayName' + error: 'Specify either id or displayName' }) - .refine(options => (!options.id && !options.displayName) || options.displayName || (options.id && validation.isValidGuid(options.id)), options => ({ - message: `The '${options.id}' must be a valid GUID`, + .refine(options => (!options.id && !options.displayName) || options.displayName || (options.id && validation.isValidGuid(options.id)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['id'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/entra/commands/roledefinition/roledefinition-set.spec.ts b/src/m365/entra/commands/roledefinition/roledefinition-set.spec.ts index fe94a6a00d8..00c931cad9c 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-set.spec.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-set.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; +import { roleDefinition } from '../../../../utils/roleDefinition.js'; import { session } from '../../../../utils/session.js'; -import command from './roledefinition-set.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; -import { roleDefinition } from '../../../../utils/roleDefinition.js'; +import commands from '../../commands.js'; +import command, { options } from './roledefinition-set.js'; describe(commands.ROLEDEFINITION_SET, () => { const roleId = 'abcd1234-de71-4623-b4af-96380a352509'; @@ -22,7 +21,7 @@ describe(commands.ROLEDEFINITION_SET, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -31,7 +30,7 @@ describe(commands.ROLEDEFINITION_SET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -103,7 +102,7 @@ describe(commands.ROLEDEFINITION_SET, () => { }); const parsedSchema = commandOptionsSchema.safeParse({ id: roleId, allowedResourceActions: "microsoft.directory/groups.unified/create,microsoft.directory/groups.unified/delete" }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(patchRequestStub.called); }); @@ -128,7 +127,7 @@ describe(commands.ROLEDEFINITION_SET, () => { verbose: true }); await command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! }); assert(patchRequestStub.called); }); @@ -150,7 +149,7 @@ describe(commands.ROLEDEFINITION_SET, () => { allowedResourceActions: "microsoft.directory/groups.unified/create" }); await assert.rejects(command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! }), new CommandError('Invalid request')); }); }); \ No newline at end of file diff --git a/src/m365/entra/commands/roledefinition/roledefinition-set.ts b/src/m365/entra/commands/roledefinition/roledefinition-set.ts index f0c8df0af71..72c0baec0a0 100644 --- a/src/m365/entra/commands/roledefinition/roledefinition-set.ts +++ b/src/m365/entra/commands/roledefinition/roledefinition-set.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -9,17 +8,16 @@ import { roleDefinition } from '../../../../utils/roleDefinition.js'; import { validation } from '../../../../utils/validation.js'; import { UnifiedRoleDefinition } from '@microsoft/microsoft-graph-types'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().optional()), - displayName: zod.alias('n', z.string().optional()), - newDisplayName: z.string().optional(), - allowedResourceActions: zod.alias('a', z.string().optional()), - description: zod.alias('d', z.string().optional()), - enabled: zod.alias('e', z.boolean().optional()), - version: zod.alias('v', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + displayName: z.string().optional().alias('n'), + newDisplayName: z.string().optional(), + allowedResourceActions: z.string().optional().alias('a'), + description: z.string().optional().alias('d'), + enabled: z.boolean().optional().alias('e'), + version: z.string().optional().alias('v') +}); declare type Options = z.infer; @@ -36,24 +34,24 @@ class EntraRoleDefinitionSetCommand extends GraphCommand { return 'Updates a custom Microsoft Entra ID role definition'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.id !== !options.displayName, { - message: 'Specify either id or displayName, but not both' + error: 'Specify either id or displayName, but not both' }) .refine(options => options.id || options.displayName, { - message: 'Specify either id or displayName' + error: 'Specify either id or displayName' }) - .refine(options => (!options.id && !options.displayName) || options.displayName || (options.id && validation.isValidGuid(options.id)), options => ({ - message: `The '${options.id}' must be a valid GUID`, + .refine(options => (!options.id && !options.displayName) || options.displayName || (options.id && validation.isValidGuid(options.id)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['id'] - })) + }) .refine(options => Object.values([options.newDisplayName, options.description, options.allowedResourceActions, options.enabled, options.version]).filter(v => typeof v !== 'undefined').length > 0, { - message: 'Provide value for at least one of the following parameters: newDisplayName, description, allowedResourceActions, enabled or version' + error: 'Provide value for at least one of the following parameters: newDisplayName, description, allowedResourceActions, enabled or version' }); } diff --git a/src/m365/entra/commands/rolepermission/rolepermission-list.spec.ts b/src/m365/entra/commands/rolepermission/rolepermission-list.spec.ts index d00613f5b77..b6126e2db95 100644 --- a/src/m365/entra/commands/rolepermission/rolepermission-list.spec.ts +++ b/src/m365/entra/commands/rolepermission/rolepermission-list.spec.ts @@ -1,18 +1,17 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './rolepermission-list.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; +import commands from '../../commands.js'; +import command, { options } from './rolepermission-list.js'; describe(commands.ROLEDEFINITION_LIST, () => { const resourceNamespace = 'microsoft.directory'; @@ -75,7 +74,7 @@ describe(commands.ROLEDEFINITION_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -84,7 +83,7 @@ describe(commands.ROLEDEFINITION_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -142,7 +141,7 @@ describe(commands.ROLEDEFINITION_LIST, () => { verbose: true }); await command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(resourceActions)); @@ -165,7 +164,7 @@ describe(commands.ROLEDEFINITION_LIST, () => { verbose: true }); await command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(filteredResourceActions)); @@ -184,7 +183,7 @@ describe(commands.ROLEDEFINITION_LIST, () => { verbose: true }); await assert.rejects( - command.action(logger, { options: parsedSchema.data }), + command.action(logger, { options: parsedSchema.data! }), new CommandError('An error has occurred') ); }); diff --git a/src/m365/entra/commands/rolepermission/rolepermission-list.ts b/src/m365/entra/commands/rolepermission/rolepermission-list.ts index 8f2c53a2b0d..ad6874984fc 100644 --- a/src/m365/entra/commands/rolepermission/rolepermission-list.ts +++ b/src/m365/entra/commands/rolepermission/rolepermission-list.ts @@ -4,15 +4,13 @@ import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { UnifiedRbacResourceAction } from '@microsoft/microsoft-graph-types'; -const options = globalOptionsZod - .extend({ - resourceNamespace: zod.alias('n', z.string()), - privileged: zod.alias('p', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + resourceNamespace: z.string().alias('n'), + privileged: z.boolean().optional().alias('p') +}); declare type Options = z.infer; @@ -37,7 +35,7 @@ class EntraRolePermissionListCommand extends GraphCommand { return ['id', 'name', 'actionVerb', 'isPrivileged']; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/entra/commands/user/user-session-revoke.spec.ts b/src/m365/entra/commands/user/user-session-revoke.spec.ts index c88bc6b24cb..d6d5817d76a 100644 --- a/src/m365/entra/commands/user/user-session-revoke.spec.ts +++ b/src/m365/entra/commands/user/user-session-revoke.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './user-session-revoke.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; -import { formatting } from '../../../../utils/formatting.js'; +import commands from '../../commands.js'; +import command, { options } from './user-session-revoke.js'; describe(commands.USER_SESSION_REVOKE, () => { const userId = 'abcd1234-de71-4623-b4af-96380a352509'; @@ -23,7 +22,7 @@ describe(commands.USER_SESSION_REVOKE, () => { let logger: Logger; let promptIssued: boolean; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -32,7 +31,7 @@ describe(commands.USER_SESSION_REVOKE, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -104,7 +103,7 @@ describe(commands.USER_SESSION_REVOKE, () => { it('prompts before revoking all sign-in sessions when confirm option not passed', async () => { const parsedSchema = commandOptionsSchema.safeParse({ userId: userId }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(promptIssued); }); @@ -113,7 +112,7 @@ describe(commands.USER_SESSION_REVOKE, () => { const postStub = sinon.stub(request, 'post').resolves(); const parsedSchema = commandOptionsSchema.safeParse({ userId: userId }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(postStub.notCalled); }); @@ -129,7 +128,7 @@ describe(commands.USER_SESSION_REVOKE, () => { }); const parsedSchema = commandOptionsSchema.safeParse({ userId: userId, force: true, verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(postStub.calledOnce); }); @@ -148,7 +147,7 @@ describe(commands.USER_SESSION_REVOKE, () => { sinon.stub(cli, 'promptForConfirmation').resolves(true); const parsedSchema = commandOptionsSchema.safeParse({ userName: userName }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(postRequestStub.calledOnce); }); @@ -166,7 +165,7 @@ describe(commands.USER_SESSION_REVOKE, () => { const parsedSchema = commandOptionsSchema.safeParse({ userId: userId }); await assert.rejects( - command.action(logger, { options: parsedSchema.data }), + command.action(logger, { options: parsedSchema.data! }), new CommandError(`Resource '${userId}' does not exist or one of its queried reference-property objects are not present.`) ); }); diff --git a/src/m365/entra/commands/user/user-session-revoke.ts b/src/m365/entra/commands/user/user-session-revoke.ts index a5f4bdd5cca..943bbd4f0b1 100644 --- a/src/m365/entra/commands/user/user-session-revoke.ts +++ b/src/m365/entra/commands/user/user-session-revoke.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { validation } from '../../../../utils/validation.js'; @@ -9,17 +8,14 @@ import request, { CliRequestOptions } from '../../../../request.js'; import { cli } from '../../../../cli/cli.js'; import { formatting } from '../../../../utils/formatting.js'; -const options = globalOptionsZod - .extend({ - userId: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional()), - userName: zod.alias('n', z.string().refine(name => validation.isValidUserPrincipalName(name), name => ({ - message: `'${name}' is not a valid UPN.` - })).optional()), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + userId: z.uuid().optional().alias('i'), + userName: z.string().refine(name => validation.isValidUserPrincipalName(name), { + error: e => `'${e.input}' is not a valid UPN.` + }).optional().alias('n'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -34,13 +30,13 @@ class EntraUserSessionRevokeCommand extends GraphCommand { public get description(): string { return 'Revokes all sign-in sessions for a given user'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.userId, options.userName].filter(o => o !== undefined).length === 1, { - message: `Specify either 'userId' or 'userName'.` + error: `Specify either 'userId' or 'userName'.` }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/exo/commands/approleassignment/approleassignment-add.spec.ts b/src/m365/exo/commands/approleassignment/approleassignment-add.spec.ts index 5b3251073cb..a1a074ec50e 100644 --- a/src/m365/exo/commands/approleassignment/approleassignment-add.spec.ts +++ b/src/m365/exo/commands/approleassignment/approleassignment-add.spec.ts @@ -1,25 +1,24 @@ +import { AdministrativeUnit, UnifiedRoleAssignment, UnifiedRoleDefinition } from '@microsoft/microsoft-graph-types'; 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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { customAppScope } from '../../../../utils/customAppScope.js'; +import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; +import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; +import { entraUser } from '../../../../utils/entraUser.js'; import { pid } from '../../../../utils/pid.js'; +import { roleDefinition } from '../../../../utils/roleDefinition.js'; import { session } from '../../../../utils/session.js'; -import { cli } from '../../../../cli/cli.js'; -import command from './approleassignment-add.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; -import { roleDefinition } from '../../../../utils/roleDefinition.js'; -import { AdministrativeUnit, UnifiedRoleAssignment, UnifiedRoleDefinition } from '@microsoft/microsoft-graph-types'; -import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; -import { entraUser } from '../../../../utils/entraUser.js'; -import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; -import { entraGroup } from '../../../../utils/entraGroup.js'; -import { customAppScope } from '../../../../utils/customAppScope.js'; +import commands from '../../commands.js'; +import command, { options } from './approleassignment-add.js'; describe(commands.APPROLEASSIGNMENT_ADD, () => { const principalId = 'fc33aa61-cf0e-46b6-9506-f633347202ab'; @@ -86,7 +85,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -95,7 +94,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -966,7 +965,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeTenant)); }); @@ -993,7 +992,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeTenant)); }); @@ -1020,7 +1019,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeTenant)); }); @@ -1047,7 +1046,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeUser)); }); @@ -1075,7 +1074,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeUser)); }); @@ -1102,7 +1101,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeGroup)); }); @@ -1130,7 +1129,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeGroup)); }); @@ -1157,7 +1156,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeAdministrativeUnit)); }); @@ -1185,7 +1184,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeAdministrativeUnit)); }); @@ -1212,7 +1211,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeCustom)); }); @@ -1240,7 +1239,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { verbose: true }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWithExactly(unifiedRoleAssignmentScopeCustom)); }); @@ -1262,7 +1261,7 @@ describe(commands.APPROLEASSIGNMENT_ADD, () => { scope: 'tenant' }); await assert.rejects(command.action(logger, { - options: result.data + options: result.data! }), new CommandError('Invalid request')); }); }); diff --git a/src/m365/exo/commands/approleassignment/approleassignment-add.ts b/src/m365/exo/commands/approleassignment/approleassignment-add.ts index 1ba2c36edb7..c44988cd491 100644 --- a/src/m365/exo/commands/approleassignment/approleassignment-add.ts +++ b/src/m365/exo/commands/approleassignment/approleassignment-add.ts @@ -10,26 +10,24 @@ import { entraUser } from '../../../../utils/entraUser.js'; import { entraGroup } from '../../../../utils/entraGroup.js'; import { entraAdministrativeUnit } from '../../../../utils/entraAdministrativeUnit.js'; import { entraServicePrincipal } from '../../../../utils/entraServicePrincipal.js'; -import { zod } from '../../../../utils/zod.js'; import { customAppScope } from '../../../../utils/customAppScope.js'; -const options = globalOptionsZod - .extend({ - roleDefinitionId: z.string().optional(), - roleDefinitionName: z.string().optional(), - principalId: z.string().optional(), - principalName: z.string().optional(), - scope: zod.alias('s', z.enum(['tenant', 'user', 'group', 'administrativeUnit', 'custom'])), - userId: z.string().optional(), - userName: z.string().optional(), - groupId: z.string().optional(), - groupName: z.string().optional(), - administrativeUnitId: z.string().optional(), - administrativeUnitName: z.string().optional(), - customAppScopeId: z.string().optional(), - customAppScopeName: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + roleDefinitionId: z.string().optional(), + roleDefinitionName: z.string().optional(), + principalId: z.string().optional(), + principalName: z.string().optional(), + scope: z.enum(['tenant', 'user', 'group', 'administrativeUnit', 'custom']).alias('s'), + userId: z.string().optional(), + userName: z.string().optional(), + groupId: z.string().optional(), + groupName: z.string().optional(), + administrativeUnitId: z.string().optional(), + administrativeUnitName: z.string().optional(), + customAppScopeId: z.string().optional(), + customAppScopeName: z.string().optional() +}); declare type Options = z.infer; @@ -46,32 +44,32 @@ class ExoAppRoleAssignmentAddCommand extends GraphCommand { return `Grant permissions to an application that's accessing data in Exchange Online and specify which mailboxes an app can access.`; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.roleDefinitionId !== !options.roleDefinitionName, { - message: 'Specify either roleDefinitionId or roleDefinitionName, but not both' + error: 'Specify either roleDefinitionId or roleDefinitionName, but not both' }) .refine(options => options.roleDefinitionId || options.roleDefinitionName, { - message: 'Specify either roleDefinitionId or roleDefinitionName' + error: 'Specify either roleDefinitionId or roleDefinitionName' }) - .refine(options => (!options.roleDefinitionId && !options.roleDefinitionName) || options.roleDefinitionName || (options.roleDefinitionId && validation.isValidGuid(options.roleDefinitionId)), options => ({ - message: `The '${options.roleDefinitionId}' must be a valid GUID`, + .refine(options => (!options.roleDefinitionId && !options.roleDefinitionName) || options.roleDefinitionName || (options.roleDefinitionId && validation.isValidGuid(options.roleDefinitionId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['roleDefinitionId'] - })) + }) .refine(options => !options.principalId !== !options.principalName, { - message: 'Specify either principalId or principalName, but not both' + error: 'Specify either principalId or principalName, but not both' }) .refine(options => options.principalId || options.principalName, { - message: 'Specify either principalId or principalName' + error: 'Specify either principalId or principalName' }) - .refine(options => (!options.principalId && !options.principalName) || options.principalName || (options.principalId && validation.isValidGuid(options.principalId)), options => ({ - message: `The '${options.principalId}' must be a valid GUID`, + .refine(options => (!options.principalId && !options.principalName) || options.principalName || (options.principalId && validation.isValidGuid(options.principalId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['principalId'] - })) + }) .refine(options => options.scope !== 'tenant' || Object.values([options.userId, options.userName, options.groupId, options.groupName, options.administrativeUnitId, options.administrativeUnitName, options.customAppScopeId, options.customAppScopeName]).filter(v => typeof v !== 'undefined').length === 0, { message: "When the scope is set to 'tenant' then do not specify neither userId, userName, groupId, groupName, administrativeUnitId, administrativeUnitName, customAppScopeId nor customAppScopeName", path: ['scope'] @@ -88,14 +86,14 @@ class ExoAppRoleAssignmentAddCommand extends GraphCommand { message: "When the scope is set to 'user' specify either userId or userName", path: ['scope'] }) - .refine(options => options.scope !== 'user' || (!options.userId && !options.userName) || options.userName || (options.userId && validation.isValidGuid(options.userId)), options => ({ - message: `The '${options.userId}' must be a valid GUID`, + .refine(options => options.scope !== 'user' || (!options.userId && !options.userName) || options.userName || (options.userId && validation.isValidGuid(options.userId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['userId'] - })) - .refine(options => options.scope !== 'user' || (!options.userId && !options.userName) || options.userId || (options.userName && validation.isValidUserPrincipalName(options.userName)), options => ({ - message: `The '${options.userId}' must be a valid GUID`, + }) + .refine(options => options.scope !== 'user' || (!options.userId && !options.userName) || options.userId || (options.userName && validation.isValidUserPrincipalName(options.userName)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['userName'] - })) + }) .refine(options => options.scope !== 'group' || Object.values([options.userId, options.userName, options.administrativeUnitId, options.administrativeUnitName, options.customAppScopeId, options.customAppScopeName]).filter(v => typeof v !== 'undefined').length === 0, { message: "When the scope is set to 'group' then do not specify userId, userName, administrativeUnitId, administrativeUnitName, customAppScopeId nor customAppScopeName", path: ['scope'] @@ -108,10 +106,10 @@ class ExoAppRoleAssignmentAddCommand extends GraphCommand { message: "When the scope is set to 'group' specify either groupId or groupName", path: ['scope'] }) - .refine(options => options.scope !== 'group' || (!options.groupId && !options.groupName) || options.groupName ||(options.groupId && validation.isValidGuid(options.groupId)), options => ({ - message: `The '${options.groupId}' must be a valid GUID`, + .refine(options => options.scope !== 'group' || (!options.groupId && !options.groupName) || options.groupName || (options.groupId && validation.isValidGuid(options.groupId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['groupId'] - })) + }) .refine(options => options.scope !== 'administrativeUnit' || Object.values([options.userId, options.userName, options.groupId, options.groupName, options.customAppScopeId, options.customAppScopeName]).filter(v => typeof v !== 'undefined').length === 0, { message: "When the scope is set to 'administrativeUnit' then do not specify userId, userName, groupId, groupName, customAppScopeId nor customAppScopeName", path: ['scope'] @@ -124,10 +122,10 @@ class ExoAppRoleAssignmentAddCommand extends GraphCommand { message: "When the scope is set to 'administrativeUnit' specify either administrativeUnitId or administrativeUnitName", path: ['scope'] }) - .refine(options => options.scope !== 'administrativeUnit' || (!options.administrativeUnitId && !options.administrativeUnitName) || options.administrativeUnitName || (options.administrativeUnitId && validation.isValidGuid(options.administrativeUnitId)), options => ({ - message: `The '${options.administrativeUnitId}' must be a valid GUID`, + .refine(options => options.scope !== 'administrativeUnit' || (!options.administrativeUnitId && !options.administrativeUnitName) || options.administrativeUnitName || (options.administrativeUnitId && validation.isValidGuid(options.administrativeUnitId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['administrativeUnitId'] - })) + }) .refine(options => options.scope !== 'custom' || Object.values([options.userId, options.userName, options.groupId, options.groupName, options.administrativeUnitId, options.administrativeUnitName]).filter(v => typeof v !== 'undefined').length === 0, { message: "When the scope is set to 'custom' then do not specify userId, userName, groupId, groupName, administrativeUnitId nor administrativeUnitName", path: ['scope'] @@ -140,10 +138,10 @@ class ExoAppRoleAssignmentAddCommand extends GraphCommand { message: "When the scope is set to 'custom' specify either customAppScopeId or customAppScopeName", path: ['scope'] }) - .refine(options => options.scope !== 'custom' || (!options.customAppScopeId && !options.customAppScopeName) || options.customAppScopeName || (options.customAppScopeId && validation.isValidGuid(options.customAppScopeId)), options => ({ - message: `The '${options.customAppScopeId}' must be a valid GUID`, + .refine(options => options.scope !== 'custom' || (!options.customAppScopeId && !options.customAppScopeName) || options.customAppScopeName || (options.customAppScopeId && validation.isValidGuid(options.customAppScopeId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['customAppScopeId'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/flow/commands/environment/environment-get.spec.ts b/src/m365/flow/commands/environment/environment-get.spec.ts index 12c7de8de0c..6fb1828a2c2 100644 --- a/src/m365/flow/commands/environment/environment-get.spec.ts +++ b/src/m365/flow/commands/environment/environment-get.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,7 +11,7 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './environment-get.js'; +import command, { options } from './environment-get.js'; import { FlowEnvironmentDetails } from './FlowEnvironmentDetails.js'; describe(commands.ENVIRONMENT_GET, () => { @@ -20,7 +19,7 @@ describe(commands.ENVIRONMENT_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const flowResponse: FlowEnvironmentDetails = { name: "Default-d87a7535-dd31-4437-bfe1-95340acd55c5", location: "europe", @@ -89,7 +88,7 @@ describe(commands.ENVIRONMENT_GET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/flow/commands/environment/environment-get.ts b/src/m365/flow/commands/environment/environment-get.ts index 76805e8b158..45f3d80780b 100644 --- a/src/m365/flow/commands/environment/environment-get.ts +++ b/src/m365/flow/commands/environment/environment-get.ts @@ -3,17 +3,15 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; -import { zod } from '../../../../utils/zod.js'; import PowerAutomateCommand from '../../../base/PowerAutomateCommand.js'; import commands from '../../commands.js'; import { FlowEnvironmentDetails } from './FlowEnvironmentDetails.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string().optional()), - default: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().optional().alias('n'), + default: z.boolean().optional() +}); declare type Options = z.infer; @@ -30,14 +28,14 @@ class FlowEnvironmentGetCommand extends PowerAutomateCommand { return 'Gets information about the specified Microsoft Flow environment'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !!options.name !== !!options.default, { - message: `Specify either name or default, but not both.` + error: `Specify either name or default, but not both.` }); } diff --git a/src/m365/flow/commands/environment/environment-list.spec.ts b/src/m365/flow/commands/environment/environment-list.spec.ts index 6360dcf90fc..dce0537327d 100644 --- a/src/m365/flow/commands/environment/environment-list.spec.ts +++ b/src/m365/flow/commands/environment/environment-list.spec.ts @@ -1,6 +1,8 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; @@ -9,17 +11,14 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './environment-list.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import command, { options } from './environment-list.js'; describe(commands.ENVIRONMENT_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -28,7 +27,7 @@ describe(commands.ENVIRONMENT_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/flow/commands/environment/environment-list.ts b/src/m365/flow/commands/environment/environment-list.ts index c0c856458e9..89b8e7c1eb4 100644 --- a/src/m365/flow/commands/environment/environment-list.ts +++ b/src/m365/flow/commands/environment/environment-list.ts @@ -6,7 +6,7 @@ import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -const options = globalOptionsZod.strict(); +export const options = z.strictObject({ ...globalOptionsZod.shape }); class FlowEnvironmentListCommand extends PowerAutomateCommand { public get name(): string { @@ -17,7 +17,7 @@ class FlowEnvironmentListCommand extends PowerAutomateCommand { return 'Lists Microsoft Flow environments in the current tenant'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/flow/commands/recyclebinitem/recyclebinitem-list.ts b/src/m365/flow/commands/recyclebinitem/recyclebinitem-list.ts index 052f97f9dfd..3fa6472a4eb 100644 --- a/src/m365/flow/commands/recyclebinitem/recyclebinitem-list.ts +++ b/src/m365/flow/commands/recyclebinitem/recyclebinitem-list.ts @@ -1,18 +1,16 @@ import PowerAutomateCommand from '../../../base/PowerAutomateCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { formatting } from '../../../../utils/formatting.js'; import { odata } from '../../../../utils/odata.js'; import { cli } from '../../../../cli/cli.js'; -const options = globalOptionsZod - .extend({ - environmentName: zod.alias('e', z.string()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + environmentName: z.string().alias('e') +}); declare type Options = z.infer; interface CommandArgs { @@ -32,7 +30,7 @@ class FlowRecycleBinItemListCommand extends PowerAutomateCommand { return ['name', 'displayName']; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.spec.ts b/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.spec.ts index ba00ab8ba77..f5f8247e32a 100644 --- a/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.spec.ts +++ b/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.spec.ts @@ -2,19 +2,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './recyclebinitem-restore.js'; -import { accessToken } from '../../../../utils/accessToken.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; +import command, { options } from './recyclebinitem-restore.js'; describe(commands.OWNER_LIST, () => { const environmentName = 'Default-d87a7535-dd31-4437-bfe1-95340acd55c6'; @@ -24,7 +23,7 @@ describe(commands.OWNER_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -34,7 +33,7 @@ describe(commands.OWNER_LIST, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.ts b/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.ts index 5dd9d42f614..992232d13e5 100644 --- a/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.ts +++ b/src/m365/flow/commands/recyclebinitem/recyclebinitem-restore.ts @@ -1,23 +1,16 @@ import PowerAutomateCommand from '../../../base/PowerAutomateCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; -import { validation } from '../../../../utils/validation.js'; import { formatting } from '../../../../utils/formatting.js'; import request, { CliRequestOptions } from '../../../../request.js'; -const options = globalOptionsZod - .extend({ - environmentName: zod.alias('e', z.string()), - flowName: zod.alias('n', z.string() - .refine(name => validation.isValidGuid(name), name => ({ - message: `'${name}' is not a valid GUID.` - })) - ) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + environmentName: z.string().alias('e'), + flowName: z.uuid().alias('n') +}); declare type Options = z.infer; interface CommandArgs { @@ -33,7 +26,7 @@ class FlowRecycleBinItemRestoreCommand extends PowerAutomateCommand { return 'Restores a soft-deleted Power Automate flow'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/graph/commands/directoryextension/directoryextension-add.spec.ts b/src/m365/graph/commands/directoryextension/directoryextension-add.spec.ts index e43f3bc7f52..f884d8dca3d 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-add.spec.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-add.spec.ts @@ -1,19 +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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { entraApp } from '../../../../utils/entraApp.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import { cli } from '../../../../cli/cli.js'; -import command from './directoryextension-add.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { entraApp } from '../../../../utils/entraApp.js'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command, { options } from './directoryextension-add.js'; describe(commands.DIRECTORYEXTENSION_ADD, () => { const appId = '7f5df2f4-9ed6-4df7-86d7-eefbfc4ab091'; @@ -61,7 +60,7 @@ describe(commands.DIRECTORYEXTENSION_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -70,7 +69,7 @@ describe(commands.DIRECTORYEXTENSION_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -243,7 +242,7 @@ describe(commands.DIRECTORYEXTENSION_ADD, () => { targetObjects: 'User', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -264,7 +263,7 @@ describe(commands.DIRECTORYEXTENSION_ADD, () => { targetObjects: 'User', isMultiValued: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(responseForMultiValued)); }); @@ -284,7 +283,7 @@ describe(commands.DIRECTORYEXTENSION_ADD, () => { dataType: 'Boolean', targetObjects: 'User,Application,Device' }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(responseWithMultipleTargets)); }); @@ -306,6 +305,6 @@ describe(commands.DIRECTORYEXTENSION_ADD, () => { dataType: 'Boolean', targetObjects: 'User,Application,Device' }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('An extension property exists with the name extension_7f5df2f49ed64df786d7eefbfc4ab091_ForServiceUseOnly.')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('An extension property exists with the name extension_7f5df2f49ed64df786d7eefbfc4ab091_ForServiceUseOnly.')); }); }); \ No newline at end of file diff --git a/src/m365/graph/commands/directoryextension/directoryextension-add.ts b/src/m365/graph/commands/directoryextension/directoryextension-add.ts index 4ae0107b366..6a66e74d4f1 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-add.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-add.ts @@ -2,25 +2,23 @@ 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 commands from '../../commands.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { ExtensionProperty } from '@microsoft/microsoft-graph-types'; import { validation } from '../../../../utils/validation.js'; import { entraApp } from '../../../../utils/entraApp.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string()), - appId: z.string().optional(), - appObjectId: z.string().optional(), - appName: z.string().optional(), - dataType: z.enum(['Binary', 'Boolean', 'DateTime', 'Integer', 'LargeInteger', 'String']), - targetObjects: z.string().transform((value) => value.split(',').map(String)) - .pipe(z.enum(['User', 'Group', 'Application', 'AdministrativeUnit', 'Device', 'Organization']).array()), - isMultiValued: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().alias('n'), + appId: z.string().optional(), + appObjectId: z.string().optional(), + appName: z.string().optional(), + dataType: z.enum(['Binary', 'Boolean', 'DateTime', 'Integer', 'LargeInteger', 'String']), + targetObjects: z.string().transform((value) => value.split(',').map(String)) + .pipe(z.enum(['User', 'Group', 'Application', 'AdministrativeUnit', 'Device', 'Organization']).array()), + isMultiValued: z.boolean().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -36,25 +34,25 @@ class GraphDirectoryExtensionAddCommand extends GraphCommand { return 'Creates a new directory extension'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => Object.values([options.appId, options.appObjectId, options.appName]).filter(v => typeof v !== 'undefined').length === 1, { - message: 'Specify either appId, appObjectId or appName, but not multiple' + error: 'Specify either appId, appObjectId or appName, but not multiple' }) .refine(options => (!options.appId && !options.appObjectId && !options.appName) || options.appObjectId || options.appName || - (options.appId && validation.isValidGuid(options.appId)), options => ({ - message: `The '${options.appId}' must be a valid GUID`, + (options.appId && validation.isValidGuid(options.appId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['appId'] - })) + }) .refine(options => (!options.appId && !options.appObjectId && !options.appName) || options.appId || options.appName || - (options.appObjectId && validation.isValidGuid(options.appObjectId)), options => ({ - message: `The '${options.appObjectId}' must be a valid GUID`, + (options.appObjectId && validation.isValidGuid(options.appObjectId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['appObjectId'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/graph/commands/directoryextension/directoryextension-get.spec.ts b/src/m365/graph/commands/directoryextension/directoryextension-get.spec.ts index b15c1f7cb8d..9439186716c 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-get.spec.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-get.spec.ts @@ -1,20 +1,19 @@ 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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { directoryExtension } from '../../../../utils/directoryExtension.js'; +import { entraApp } from '../../../../utils/entraApp.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import { cli } from '../../../../cli/cli.js'; -import command from './directoryextension-get.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { entraApp } from '../../../../utils/entraApp.js'; -import { CommandError } from '../../../../Command.js'; -import { directoryExtension } from '../../../../utils/directoryExtension.js'; +import commands from '../../commands.js'; +import command, { options } from './directoryextension-get.js'; describe(commands.DIRECTORYEXTENSION_GET, () => { const appId = '7f5df2f4-9ed6-4df7-86d7-eefbfc4ab091'; @@ -40,7 +39,7 @@ describe(commands.DIRECTORYEXTENSION_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -49,7 +48,7 @@ describe(commands.DIRECTORYEXTENSION_GET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -177,7 +176,7 @@ describe(commands.DIRECTORYEXTENSION_GET, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(response)); }); @@ -199,7 +198,7 @@ describe(commands.DIRECTORYEXTENSION_GET, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(response)); }); @@ -221,7 +220,7 @@ describe(commands.DIRECTORYEXTENSION_GET, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(response)); }); @@ -246,7 +245,7 @@ describe(commands.DIRECTORYEXTENSION_GET, () => { }); await assert.rejects( - command.action(logger, { options: parsedSchema.data }), + command.action(logger, { options: parsedSchema.data! }), new CommandError(`Resource '${appObjectId}' does not exist or one of its queried reference-property objects are not present.`) ); }); @@ -272,7 +271,7 @@ describe(commands.DIRECTORYEXTENSION_GET, () => { }); await assert.rejects( - command.action(logger, { options: parsedSchema.data }), + command.action(logger, { options: parsedSchema.data! }), new CommandError(`Resource '${extensionId}' does not exist or one of its queried reference-property objects are not present.`) ); }); diff --git a/src/m365/graph/commands/directoryextension/directoryextension-get.ts b/src/m365/graph/commands/directoryextension/directoryextension-get.ts index c27b77951ba..d51007d9ecd 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-get.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-get.ts @@ -2,28 +2,19 @@ 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 commands from '../../commands.js'; import GraphCommand from '../../../base/GraphCommand.js'; -import { validation } from '../../../../utils/validation.js'; import { entraApp } from '../../../../utils/entraApp.js'; import { directoryExtension } from '../../../../utils/directoryExtension.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional()), - name: zod.alias('n', z.string().optional()), - appId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - appObjectId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - appName: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + name: z.string().optional().alias('n'), + appId: z.uuid().optional(), + appObjectId: z.uuid().optional(), + appName: z.string().optional() +}); declare type Options = z.infer; @@ -40,20 +31,20 @@ class GraphDirectoryExtensionGetCommand extends GraphCommand { return 'Retrieves the definition of a directory extension'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.id !== !options.name, { - message: 'Specify either id or name, but not both' + error: 'Specify either id or name, but not both' }) .refine(options => options.id || options.name, { - message: 'Specify either id or name' + error: 'Specify either id or name' }) .refine(options => Object.values([options.appId, options.appObjectId, options.appName]).filter(v => typeof v !== 'undefined').length === 1, { - message: 'Specify either appId, appObjectId or appName, but not multiple' + error: 'Specify either appId, appObjectId or appName, but not multiple' }); } diff --git a/src/m365/graph/commands/directoryextension/directoryextension-list.spec.ts b/src/m365/graph/commands/directoryextension/directoryextension-list.spec.ts index 319cbbcde27..fef86f10c74 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-list.spec.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-list.spec.ts @@ -1,18 +1,17 @@ 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 { 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 commands from '../../commands.js'; import { sinonUtil } from './../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import command from './directoryextension-list.js'; +import command, { options } from './directoryextension-list.js'; describe(commands.DIRECTORYEXTENSION_LIST, () => { const appId = 'fd918e4b-c821-4efb-b50a-5eddd23afc6f'; @@ -23,7 +22,7 @@ describe(commands.DIRECTORYEXTENSION_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const response = { "value": [ @@ -49,7 +48,7 @@ describe(commands.DIRECTORYEXTENSION_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -144,7 +143,7 @@ describe(commands.DIRECTORYEXTENSION_LIST, () => { const parsedSchema = commandOptionsSchema.safeParse({ }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(response.value)); }); @@ -161,7 +160,7 @@ describe(commands.DIRECTORYEXTENSION_LIST, () => { appObjectId: appObjectId }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(response.value)); }); @@ -188,7 +187,7 @@ describe(commands.DIRECTORYEXTENSION_LIST, () => { appId: appId }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(response.value)); }); @@ -215,7 +214,7 @@ describe(commands.DIRECTORYEXTENSION_LIST, () => { appName: appName }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledWith(response.value)); }); @@ -238,7 +237,7 @@ describe(commands.DIRECTORYEXTENSION_LIST, () => { }); await assert.rejects( - command.action(logger, { options: parsedSchema.data }), + command.action(logger, { options: parsedSchema.data! }), new CommandError(`Resource '${appObjectId}' does not exist or one of its queried reference-property objects are not present.`) ); }); diff --git a/src/m365/graph/commands/directoryextension/directoryextension-list.ts b/src/m365/graph/commands/directoryextension/directoryextension-list.ts index b6ee0e475e3..c8d48b3f3ac 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-list.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-list.ts @@ -4,22 +4,16 @@ import { globalOptionsZod } from '../../../../Command.js'; import request, { CliRequestOptions } from '../../../../request.js'; import commands from '../../commands.js'; import GraphCommand from '../../../base/GraphCommand.js'; -import { validation } from '../../../../utils/validation.js'; import { entraApp } from '../../../../utils/entraApp.js'; import { odata } from '../../../../utils/odata.js'; import { ExtensionProperty } from '@microsoft/microsoft-graph-types'; -const options = globalOptionsZod - .extend({ - appId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - appObjectId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - appName: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + appId: z.uuid().optional(), + appObjectId: z.uuid().optional(), + appName: z.string().optional() +}); declare type Options = z.infer; @@ -40,15 +34,15 @@ class GraphDirectoryExtensionListCommand extends GraphCommand { return ['id', 'name', 'appDisplayName']; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => ([options.appId, options.appObjectId, options.appName].filter(x => x !== undefined).length <= 1), { - message: 'Specify either appId, appObjectId, or appName, but not multiple.' + error: 'Specify either appId, appObjectId, or appName, but not multiple.' }); } diff --git a/src/m365/graph/commands/directoryextension/directoryextension-remove.spec.ts b/src/m365/graph/commands/directoryextension/directoryextension-remove.spec.ts index 3ebac1508d8..6d153b1b95c 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-remove.spec.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-remove.spec.ts @@ -1,20 +1,19 @@ 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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { directoryExtension } from '../../../../utils/directoryExtension.js'; +import { entraApp } from '../../../../utils/entraApp.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import { cli } from '../../../../cli/cli.js'; -import command from './directoryextension-remove.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { entraApp } from '../../../../utils/entraApp.js'; -import { CommandError } from '../../../../Command.js'; -import { directoryExtension } from '../../../../utils/directoryExtension.js'; +import commands from '../../commands.js'; +import command, { options } from './directoryextension-remove.js'; describe(commands.DIRECTORYEXTENSION_REMOVE, () => { const appId = '7f5df2f4-9ed6-4df7-86d7-eefbfc4ab091'; @@ -27,7 +26,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { let logger: Logger; let promptIssued: boolean; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -36,7 +35,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -161,7 +160,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { appId: appId, name: extensionName }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(promptIssued); }); @@ -173,7 +172,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { appId: appId, name: extensionName }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteSpy.notCalled); }); @@ -193,7 +192,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteRequestStub.called); }); @@ -216,7 +215,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteRequestStub.called); }); @@ -241,7 +240,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteRequestStub.called); }); @@ -269,7 +268,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { }); await assert.rejects( - command.action(logger, { options: parsedSchema.data }), + command.action(logger, { options: parsedSchema.data! }), new CommandError(`Resource '${appObjectId}' does not exist or one of its queried reference-property objects are not present.`) ); }); @@ -299,7 +298,7 @@ describe(commands.DIRECTORYEXTENSION_REMOVE, () => { }); await assert.rejects( - command.action(logger, { options: parsedSchema.data }), + command.action(logger, { options: parsedSchema.data! }), new CommandError(`Resource '${extensionId}' does not exist or one of its queried reference-property objects are not present.`) ); }); diff --git a/src/m365/graph/commands/directoryextension/directoryextension-remove.ts b/src/m365/graph/commands/directoryextension/directoryextension-remove.ts index 712da20014d..0570060d1d7 100644 --- a/src/m365/graph/commands/directoryextension/directoryextension-remove.ts +++ b/src/m365/graph/commands/directoryextension/directoryextension-remove.ts @@ -2,37 +2,28 @@ 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 commands from '../../commands.js'; import GraphCommand from '../../../base/GraphCommand.js'; -import { validation } from '../../../../utils/validation.js'; import { entraApp } from '../../../../utils/entraApp.js'; import { cli } from '../../../../cli/cli.js'; import { directoryExtension } from '../../../../utils/directoryExtension.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional()), - name: zod.alias('n', z.string().optional()), - appId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - appObjectId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - appName: z.string().optional(), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + name: z.string().optional().alias('n'), + appId: z.uuid().optional(), + appObjectId: z.uuid().optional(), + appName: z.string().optional(), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; interface CommandArgs { options: Options; } -class GraphDirectoryExtensionRemoveCommand extends GraphCommand{ +class GraphDirectoryExtensionRemoveCommand extends GraphCommand { public get name(): string { return commands.DIRECTORYEXTENSION_REMOVE; } @@ -41,20 +32,20 @@ class GraphDirectoryExtensionRemoveCommand extends GraphCommand{ return 'Removes a directory extension'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.id !== !options.name, { - message: 'Specify either id or name, but not both' + error: 'Specify either id or name, but not both' }) .refine(options => options.id || options.name, { - message: 'Specify either id or name' + error: 'Specify either id or name' }) .refine(options => Object.values([options.appId, options.appObjectId, options.appName]).filter(v => typeof v !== 'undefined').length === 1, { - message: 'Specify either appId, appObjectId or appName, but not multiple' + error: 'Specify either appId, appObjectId or appName, but not multiple' }); } diff --git a/src/m365/graph/commands/openextension/openextension-add.spec.ts b/src/m365/graph/commands/openextension/openextension-add.spec.ts index b77851f2d37..be09ed3a4d0 100644 --- a/src/m365/graph/commands/openextension/openextension-add.spec.ts +++ b/src/m365/graph/commands/openextension/openextension-add.spec.ts @@ -1,18 +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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.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 { cli } from '../../../../cli/cli.js'; -import command from './openextension-add.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command from './openextension-add.js'; +import { options } from './openextension-get.js'; describe(commands.OPENEXTENSION_ADD, () => { const resourceId = 'f4099688-dd3f-4a55-a9f5-ddd7417c227a'; @@ -41,7 +41,7 @@ describe(commands.OPENEXTENSION_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -50,7 +50,7 @@ describe(commands.OPENEXTENSION_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -235,7 +235,7 @@ describe(commands.OPENEXTENSION_ADD, () => { language: 'English', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -257,7 +257,7 @@ describe(commands.OPENEXTENSION_ADD, () => { language: 'English', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -279,7 +279,7 @@ describe(commands.OPENEXTENSION_ADD, () => { language: 'English', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -301,7 +301,7 @@ describe(commands.OPENEXTENSION_ADD, () => { language: 'English', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -331,7 +331,7 @@ describe(commands.OPENEXTENSION_ADD, () => { supportedSystem: 'Linux', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(responseWithJsonObject)); }); @@ -359,6 +359,6 @@ describe(commands.OPENEXTENSION_ADD, () => { language: 'English', verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('An extension already exists with given id.')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('An extension already exists with given id.')); }); }); diff --git a/src/m365/graph/commands/openextension/openextension-add.ts b/src/m365/graph/commands/openextension/openextension-add.ts index c5a338c782d..b7f29d0d72e 100644 --- a/src/m365/graph/commands/openextension/openextension-add.ts +++ b/src/m365/graph/commands/openextension/openextension-add.ts @@ -9,13 +9,12 @@ import request, { CliRequestOptions } from '../../../../request.js'; import { Extension } from '@microsoft/microsoft-graph-types'; import { optionsUtils } from '../../../../utils/optionsUtils.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string()), - resourceId: zod.alias('i', z.string()), - resourceType: zod.alias('t', z.enum(['user', 'group', 'device', 'organization'])) - }) - .passthrough(); +const options = z.looseObject({ + ...globalOptionsZod.shape, + name: z.string().alias('n'), + resourceId: z.string().alias('i'), + resourceType: z.enum(['user', 'group', 'device', 'organization']).alias('t') +}); declare type Options = z.infer; @@ -32,20 +31,20 @@ class GraphOpenExtensionAddCommand extends GraphCommand { return 'Adds an open extension to a resource'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema - .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), options => ({ - message: `The '${options.resourceId}' must be a valid GUID`, + .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['resourceId'] - })) - .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), options => ({ - message: `The '${options.resourceId}' must be a valid GUID or user principal name`, + }) + .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), { + error: e => `The '${e.input}' must be a valid GUID or user principal name`, path: ['resourceId'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/graph/commands/openextension/openextension-get.spec.ts b/src/m365/graph/commands/openextension/openextension-get.spec.ts index 9c76b38357f..a7ae3a347c9 100644 --- a/src/m365/graph/commands/openextension/openextension-get.spec.ts +++ b/src/m365/graph/commands/openextension/openextension-get.spec.ts @@ -1,18 +1,17 @@ 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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.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 { cli } from '../../../../cli/cli.js'; -import command from './openextension-get.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command, { options } from './openextension-get.js'; describe(commands.OPENEXTENSION_GET, () => { const resourceId = 'f4099688-dd3f-4a55-a9f5-ddd7417c227a'; @@ -33,7 +32,7 @@ describe(commands.OPENEXTENSION_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -42,7 +41,7 @@ describe(commands.OPENEXTENSION_GET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -203,7 +202,7 @@ describe(commands.OPENEXTENSION_GET, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -223,7 +222,7 @@ describe(commands.OPENEXTENSION_GET, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -243,7 +242,7 @@ describe(commands.OPENEXTENSION_GET, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -263,7 +262,7 @@ describe(commands.OPENEXTENSION_GET, () => { verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -289,6 +288,6 @@ describe(commands.OPENEXTENSION_GET, () => { verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('Extension with given id not found.')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('Extension with given id not found.')); }); }); diff --git a/src/m365/graph/commands/openextension/openextension-get.ts b/src/m365/graph/commands/openextension/openextension-get.ts index 70e4b793102..08f34660373 100644 --- a/src/m365/graph/commands/openextension/openextension-get.ts +++ b/src/m365/graph/commands/openextension/openextension-get.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { validation } from '../../../../utils/validation.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -8,13 +7,12 @@ import commands from '../../commands.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { Extension } from '@microsoft/microsoft-graph-types'; -const options = globalOptionsZod - .extend({ - resourceId: zod.alias('i', z.string()), - resourceType: zod.alias('t', z.enum(['user', 'group', 'device', 'organization'])), - name: zod.alias('n', z.string()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + resourceId: z.string().alias('i'), + resourceType: z.enum(['user', 'group', 'device', 'organization']).alias('t'), + name: z.string().alias('n') +}); declare type Options = z.infer; @@ -31,20 +29,20 @@ class GraphOpenExtensionGetCommand extends GraphCommand { return 'Retrieves a specific open extension for a resource'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema - .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), options => ({ - message: `The '${options.resourceId}' must be a valid GUID`, + .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['resourceId'] - })) - .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), options => ({ - message: `The '${options.resourceId}' must be a valid GUID or user principal name`, + }) + .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), { + error: e => `The '${e.input}' must be a valid GUID or user principal name`, path: ['resourceId'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/graph/commands/openextension/openextension-list.spec.ts b/src/m365/graph/commands/openextension/openextension-list.spec.ts index b1977f2b998..26395d2e5d3 100644 --- a/src/m365/graph/commands/openextension/openextension-list.spec.ts +++ b/src/m365/graph/commands/openextension/openextension-list.spec.ts @@ -1,18 +1,17 @@ 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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.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 { cli } from '../../../../cli/cli.js'; -import command from './openextension-list.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command, { options } from './openextension-list.js'; describe(commands.OPENEXTENSION_LIST, () => { const resourceId = 'f4099688-dd3f-4a55-a9f5-ddd7417c227a'; @@ -30,7 +29,7 @@ describe(commands.OPENEXTENSION_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -39,7 +38,7 @@ describe(commands.OPENEXTENSION_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -177,7 +176,7 @@ describe(commands.OPENEXTENSION_LIST, () => { resourceType: 'user', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly([response])); }); @@ -199,7 +198,7 @@ describe(commands.OPENEXTENSION_LIST, () => { resourceType: 'group', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly([response])); }); @@ -221,7 +220,7 @@ describe(commands.OPENEXTENSION_LIST, () => { resourceType: 'device', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly([response])); }); @@ -243,7 +242,7 @@ describe(commands.OPENEXTENSION_LIST, () => { resourceType: 'organization', verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly([response])); }); @@ -267,6 +266,6 @@ describe(commands.OPENEXTENSION_LIST, () => { resourceType: 'organization', verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('Parent Object Not Found')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('Parent Object Not Found')); }); }); \ No newline at end of file diff --git a/src/m365/graph/commands/openextension/openextension-list.ts b/src/m365/graph/commands/openextension/openextension-list.ts index c1d0258ba87..f2fc5065b55 100644 --- a/src/m365/graph/commands/openextension/openextension-list.ts +++ b/src/m365/graph/commands/openextension/openextension-list.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { validation } from '../../../../utils/validation.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -9,12 +8,11 @@ import { CliRequestOptions } from '../../../../request.js'; import { Extension } from '@microsoft/microsoft-graph-types'; import { odata } from '../../../../utils/odata.js'; -const options = globalOptionsZod - .extend({ - resourceId: zod.alias('i', z.string()), - resourceType: zod.alias('t', z.enum(['user', 'group', 'device', 'organization'])) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + resourceId: z.string().alias('i'), + resourceType: z.enum(['user', 'group', 'device', 'organization']).alias('t') +}); declare type Options = z.infer; interface CommandArgs { @@ -34,22 +32,22 @@ class GraphOpenExtensionListCommand extends GraphCommand { return ['id', 'extensionName']; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || - (options.resourceId && validation.isValidGuid(options.resourceId)), options => ({ - message: `The '${options.resourceId}' must be a valid GUID`, + (options.resourceId && validation.isValidGuid(options.resourceId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['resourceId'] - })) + }) .refine(options => options.resourceType !== 'user' || - (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), options => ({ - message: `The '${options.resourceId}' must be a valid GUID or user principal name`, + (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), { + error: e => `The '${e.input}' must be a valid GUID or user principal name`, path: ['resourceId'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/graph/commands/openextension/openextension-remove.spec.ts b/src/m365/graph/commands/openextension/openextension-remove.spec.ts index 5539767ded4..c4304541355 100644 --- a/src/m365/graph/commands/openextension/openextension-remove.spec.ts +++ b/src/m365/graph/commands/openextension/openextension-remove.spec.ts @@ -1,18 +1,17 @@ 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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.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 { cli } from '../../../../cli/cli.js'; -import command from './openextension-remove.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command, { options } from './openextension-remove.js'; describe(commands.OPENEXTENSION_REMOVE, () => { const resourceId = 'f4099688-dd3f-4a55-a9f5-ddd7417c227a'; @@ -22,7 +21,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { let logger: Logger; let promptIssued: boolean; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -31,7 +30,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -140,7 +139,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { name: 'com.contoso.roamingSettings' }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(promptIssued); }); @@ -153,7 +152,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { name: 'com.contoso.roamingSettings' }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteSpy.notCalled); }); @@ -223,7 +222,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { force: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteRequestStub.calledOnce); }); @@ -244,7 +243,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { force: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteRequestStub.calledOnce); }); @@ -266,7 +265,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { name: 'com.contoso.roamingSettings' }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteRequestStub.calledOnce); }); @@ -286,7 +285,7 @@ describe(commands.OPENEXTENSION_REMOVE, () => { force: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(deleteRequestStub.calledOnce); }); @@ -312,6 +311,6 @@ describe(commands.OPENEXTENSION_REMOVE, () => { force: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('Extension with given id not found.')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('Extension with given id not found.')); }); }); diff --git a/src/m365/graph/commands/openextension/openextension-remove.ts b/src/m365/graph/commands/openextension/openextension-remove.ts index fb3f0437d21..7fd325f5be9 100644 --- a/src/m365/graph/commands/openextension/openextension-remove.ts +++ b/src/m365/graph/commands/openextension/openextension-remove.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { validation } from '../../../../utils/validation.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -8,14 +7,13 @@ import commands from '../../commands.js'; import { cli } from '../../../../cli/cli.js'; import request, { CliRequestOptions } from '../../../../request.js'; -const options = globalOptionsZod - .extend({ - resourceId: zod.alias('i', z.string()), - resourceType: zod.alias('t', z.enum(['user', 'group', 'device', 'organization'])), - name: zod.alias('n', z.string()), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + resourceId: z.string().alias('i'), + resourceType: z.enum(['user', 'group', 'device', 'organization']).alias('t'), + name: z.string().alias('n'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -32,20 +30,20 @@ class GraphOpenExtensionRemoveCommand extends GraphCommand { return 'Removes a specific open extension for a resource'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema - .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), options => ({ - message: `The '${options.resourceId}' must be a valid GUID`, + .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['resourceId'] - })) - .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), options => ({ - message: `The '${options.resourceId}' must be a valid GUID or user principal name`, + }) + .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), { + error: e => `The '${e.input}' must be a valid GUID or user principal name`, path: ['resourceId'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/graph/commands/openextension/openextension-set.spec.ts b/src/m365/graph/commands/openextension/openextension-set.spec.ts index 89ab4974f83..ea939f93158 100644 --- a/src/m365/graph/commands/openextension/openextension-set.spec.ts +++ b/src/m365/graph/commands/openextension/openextension-set.spec.ts @@ -1,18 +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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.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 { cli } from '../../../../cli/cli.js'; -import command from './openextension-set.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import { options } from './openextension-get.js'; +import command from './openextension-set.js'; describe(commands.OPENEXTENSION_SET, () => { const resourceId = 'f4099688-dd3f-4a55-a9f5-ddd7417c227a'; @@ -22,7 +22,7 @@ describe(commands.OPENEXTENSION_SET, () => { let log: any[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -31,7 +31,7 @@ describe(commands.OPENEXTENSION_SET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -240,7 +240,7 @@ describe(commands.OPENEXTENSION_SET, () => { id: "com.contoso.roamingSettings" }; - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, requestBody); }); @@ -285,7 +285,7 @@ describe(commands.OPENEXTENSION_SET, () => { extensionName: "com.contoso.roamingSettings", id: "com.contoso.roamingSettings" }; - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, requestBody); }); @@ -330,7 +330,7 @@ describe(commands.OPENEXTENSION_SET, () => { extensionName: "com.contoso.roamingSettings", id: "com.contoso.roamingSettings" }; - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, requestBody); }); @@ -373,7 +373,7 @@ describe(commands.OPENEXTENSION_SET, () => { id: "com.contoso.roamingSettings" }; - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, requestBody); }); @@ -424,7 +424,7 @@ describe(commands.OPENEXTENSION_SET, () => { id: "com.contoso.roamingSettings" }; - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, requestBody); }); @@ -450,6 +450,6 @@ describe(commands.OPENEXTENSION_SET, () => { color: 'blue', verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('Extension with given id not found.')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('Extension with given id not found.')); }); }); diff --git a/src/m365/graph/commands/openextension/openextension-set.ts b/src/m365/graph/commands/openextension/openextension-set.ts index eb45a53a65b..60803ba67d1 100644 --- a/src/m365/graph/commands/openextension/openextension-set.ts +++ b/src/m365/graph/commands/openextension/openextension-set.ts @@ -1,22 +1,21 @@ +import { Extension } from '@microsoft/microsoft-graph-types'; import { z } from 'zod'; +import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +import { optionsUtils } from '../../../../utils/optionsUtils.js'; import { validation } from '../../../../utils/validation.js'; +import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; -import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; -import request, { CliRequestOptions } from '../../../../request.js'; -import { Extension } from '@microsoft/microsoft-graph-types'; -import { optionsUtils } from '../../../../utils/optionsUtils.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string()), - resourceId: zod.alias('i', z.string()), - resourceType: zod.alias('t', z.enum(['user', 'group', 'device', 'organization'])), - keepUnchangedProperties: zod.alias('k', z.boolean().optional()) - }) - .passthrough(); +const options = z.looseObject({ + ...globalOptionsZod.shape, + name: z.string().alias('n'), + resourceId: z.string().alias('i'), + resourceType: z.enum(['user', 'group', 'device', 'organization']).alias('t'), + keepUnchangedProperties: z.boolean().optional().alias('k') +}); declare type Options = z.infer; @@ -35,20 +34,20 @@ class GraphOpenExtensionSetCommand extends GraphCommand { return 'Updates an open extension for a resource'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema - .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), options => ({ - message: `The '${options.resourceId}' must be a valid GUID`, + .refine(options => options.resourceType !== 'group' && options.resourceType !== 'device' && options.resourceType !== 'organization' || (options.resourceId && validation.isValidGuid(options.resourceId)), { + error: e => `The '${e.input}' must be a valid GUID`, path: ['resourceId'] - })) - .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), options => ({ - message: `The '${options.resourceId}' must be a valid GUID or user principal name`, + }) + .refine(options => options.resourceType !== 'user' || (options.resourceId && (validation.isValidGuid(options.resourceId) || validation.isValidUserPrincipalName(options.resourceId))), { + error: e => `The '${e.input}' must be a valid GUID or user principal name`, path: ['resourceId'] - })); + }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/onedrive/commands/onedrive-list.spec.ts b/src/m365/onedrive/commands/onedrive-list.spec.ts index 95329806d51..7a836b02a2a 100644 --- a/src/m365/onedrive/commands/onedrive-list.spec.ts +++ b/src/m365/onedrive/commands/onedrive-list.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -14,14 +13,14 @@ import { session } from '../../../utils/session.js'; import { sinonUtil } from '../../../utils/sinonUtil.js'; import { spo } from '../../../utils/spo.js'; import commands from '../commands.js'; -import command from './onedrive-list.js'; +import command, { options } from './onedrive-list.js'; describe(commands.LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -32,7 +31,7 @@ describe(commands.LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.spoUrl = 'https://contoso.sharepoint.com'; }); diff --git a/src/m365/onedrive/commands/onedrive-list.ts b/src/m365/onedrive/commands/onedrive-list.ts index 67faf7b232c..7a79e2aa8e6 100644 --- a/src/m365/onedrive/commands/onedrive-list.ts +++ b/src/m365/onedrive/commands/onedrive-list.ts @@ -9,7 +9,7 @@ import { SiteProperties } from "../../spo/commands/site/SiteProperties.js"; import { SPOSitePropertiesEnumerable } from "../../spo/commands/site/SPOSitePropertiesEnumerable.js"; import commands from "../commands.js"; -const options = globalOptionsZod.strict(); +export const options = z.strictObject({ ...globalOptionsZod.shape }); class OneDriveListCommand extends SpoCommand { private allSites?: SiteProperties[]; @@ -22,7 +22,7 @@ class OneDriveListCommand extends SpoCommand { return "Retrieves a list of OneDrive sites"; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/outlook/commands/mail/mail-searchfolder-add.spec.ts b/src/m365/outlook/commands/mail/mail-searchfolder-add.spec.ts index d704843ebba..d34b9ec0665 100644 --- a/src/m365/outlook/commands/mail/mail-searchfolder-add.spec.ts +++ b/src/m365/outlook/commands/mail/mail-searchfolder-add.spec.ts @@ -1,19 +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 { Logger } from '../../../../cli/Logger.js'; -import commands from '../../commands.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './mail-searchfolder-add.js'; -import { cli } from '../../../../cli/cli.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import request from '../../../../request.js'; -import { CommandError } from '../../../../Command.js'; -import { accessToken } from '../../../../utils/accessToken.js'; +import commands from '../../commands.js'; +import command, { options } from './mail-searchfolder-add.js'; describe(commands.MAIL_SEARCHFOLDER_ADD, () => { const userId = 'ae0e8388-cd70-427f-9503-c57498ee3337'; @@ -59,7 +58,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -74,7 +73,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { }; } commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -177,7 +176,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { messageFilter: filterQuery, sourceFoldersIds: sourceFolderId1 }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -199,7 +198,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { messageFilter: filterQuery, sourceFoldersIds: sourceFolderId1 }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(response)); }); @@ -223,7 +222,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { includeNestedFolders: true, verbose: true }); - await command.action(logger, { options: parsedSchema.data }); + await command.action(logger, { options: parsedSchema.data! }); assert(loggerLogSpy.calledOnceWithExactly(responseWithNestedFolders)); }); @@ -238,7 +237,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { includeNestedFolders: true, verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('When running with application permissions either userId or userName is required')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('When running with application permissions either userId or userName is required')); }); it('fails creating a mail search folder for signed-in user if userId is specified', async () => { @@ -250,7 +249,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { includeNestedFolders: true, verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('You can create mail search folder for other users only if CLI is authenticated in app-only mode')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('You can create mail search folder for other users only if CLI is authenticated in app-only mode')); }); it('fails creating a mail search folder for signed-in user if userName is specified', async () => { @@ -262,7 +261,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { includeNestedFolders: true, verbose: true }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('You can create mail search folder for other users only if CLI is authenticated in app-only mode')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('You can create mail search folder for other users only if CLI is authenticated in app-only mode')); }); it('correctly handles error when invalid folder id is specified', async () => { @@ -280,7 +279,7 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { messageFilter: filterQuery, sourceFoldersIds: 'foo' }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError('Id is malformed.')); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError('Id is malformed.')); }); it('correctly handles error when invalid query is specified', async () => { @@ -298,6 +297,6 @@ describe(commands.MAIL_SEARCHFOLDER_ADD, () => { messageFilter: "contais(subject, 'CLI for Microsoft 365')", sourceFoldersIds: 'foo' }); - await assert.rejects(command.action(logger, { options: parsedSchema.data }), new CommandError(`An unknown function with name 'contais' was found. This may also be a function import or a key lookup on a navigation property, which is not allowed.`)); + await assert.rejects(command.action(logger, { options: parsedSchema.data! }), new CommandError(`An unknown function with name 'contais' was found. This may also be a function import or a key lookup on a navigation property, which is not allowed.`)); }); }); \ No newline at end of file diff --git a/src/m365/outlook/commands/mail/mail-searchfolder-add.ts b/src/m365/outlook/commands/mail/mail-searchfolder-add.ts index 5b527db84a5..d84a1e78a15 100644 --- a/src/m365/outlook/commands/mail/mail-searchfolder-add.ts +++ b/src/m365/outlook/commands/mail/mail-searchfolder-add.ts @@ -5,27 +5,22 @@ import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { validation } from '../../../../utils/validation.js'; import { accessToken } from '../../../../utils/accessToken.js'; import auth from '../../../../Auth.js'; -const options = globalOptionsZod - .extend({ - userId: zod.alias('i', z.string() - .refine(userId => validation.isValidGuid(userId), userId => ({ - message: `'${userId}' is not a valid GUID.` - })).optional()), - userName: zod.alias('n', z.string() - .refine(userName => validation.isValidUserPrincipalName(userName), userName => ({ - message: `'${userName}' is not a valid UPN.` - })).optional()), - folderName: z.string(), - messageFilter: z.string(), - sourceFoldersIds: z.string().transform((value) => value.split(',')).pipe(z.string().array()), - includeNestedFolders: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + userId: z.uuid().optional().alias('i'), + userName: z.string() + .refine(userName => validation.isValidUserPrincipalName(userName), { + error: e => `'${e.input}' is not a valid UPN.` + }).optional().alias('n'), + folderName: z.string(), + messageFilter: z.string(), + sourceFoldersIds: z.string().transform((value) => value.split(',')).pipe(z.string().array()), + includeNestedFolders: z.boolean().optional() +}); declare type Options = z.infer; @@ -42,14 +37,14 @@ class OutlookMailSearchFolderAddCommand extends GraphCommand { return `Creates a new mail search folder in the user's mailbox`; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !(options.userId && options.userName), { - message: 'Specify either userId or userName, but not both' + error: 'Specify either userId or userName, but not both' }); } diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts index d0580507107..2dba58a97b9 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './mailbox-settings-get.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; -import { accessToken } from '../../../../utils/accessToken.js'; +import commands from '../../commands.js'; +import command, { options } from './mailbox-settings-get.js'; describe(commands.MAILBOX_SETTINGS_GET, () => { const userId = 'abcd1234-de71-4623-b4af-96380a352509'; @@ -63,7 +62,7 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { let logger: Logger; let commandInfo: CommandInfo; let loggerLogSpy: sinon.SinonSpy; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -78,7 +77,7 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { }; } commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -166,7 +165,7 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); }); @@ -189,7 +188,7 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); }); @@ -212,7 +211,7 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); }); @@ -223,17 +222,17 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { const result = commandOptionsSchema.safeParse({ verbose: true }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('When running with application permissions either userId or userName is required')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('When running with application permissions either userId or userName is required')); }); it('fails retrieve mailbox settings of the signed-in user if userId is specified', async () => { const result = commandOptionsSchema.safeParse({ userId: userId, verbose: true }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('You can retrieve mailbox settings of other users only if CLI is authenticated in app-only mode')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('You can retrieve mailbox settings of other users only if CLI is authenticated in app-only mode')); }); it('fails retrieve mailbox settings of the signed-in user if userName is specified', async () => { const result = commandOptionsSchema.safeParse({ userName: userName, verbose: true }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('You can retrieve mailbox settings of other users only if CLI is authenticated in app-only mode')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('You can retrieve mailbox settings of other users only if CLI is authenticated in app-only mode')); }); it('correctly handles API OData error', async () => { @@ -248,6 +247,6 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { } }); const result = commandOptionsSchema.safeParse({ verbose: true }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('Invalid request')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('Invalid request')); }); }); \ No newline at end of file diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts index 03e119ea595..9fa3388ca2a 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; @@ -10,16 +9,13 @@ import { MailboxSettings } from '@microsoft/microsoft-graph-types'; import { accessToken } from '../../../../utils/accessToken.js'; import auth from '../../../../Auth.js'; -const options = globalOptionsZod - .extend({ - userId: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional()), - userName: zod.alias('n', z.string().refine(name => validation.isValidUserPrincipalName(name), name => ({ - message: `'${name}' is not a valid UPN.` - })).optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + userId: z.uuid().optional().alias('i'), + userName: z.string().refine(name => validation.isValidUserPrincipalName(name), { + error: e => `'${e.input}' is not a valid UPN.` + }).optional().alias('n') +}); declare type Options = z.infer; @@ -36,14 +32,14 @@ class OutlookMailboxSettingsGetCommand extends GraphCommand { return `Get the user's mailbox settings`; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !(options.userId && options.userName), { - message: 'Specify either userId or userName, but not both' + error: 'Specify either userId or userName, but not both' }); } diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts index 4861d739e02..6493af60576 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import commands from '../../commands.js'; -import request from '../../../../request.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; -import command from './mailbox-settings-set.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; -import { accessToken } from '../../../../utils/accessToken.js'; +import commands from '../../commands.js'; +import command, { options } from './mailbox-settings-set.js'; describe(commands.MAILBOX_SETTINGS_SET, () => { const userId = 'abcd1234-de71-4623-b4af-96380a352509'; @@ -63,7 +62,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { let logger: Logger; let commandInfo: CommandInfo; let loggerLogSpy: sinon.SinonSpy; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -78,7 +77,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }; } commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -244,7 +243,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); }); @@ -271,7 +270,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -306,7 +305,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -340,7 +339,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { verbose: true }); - await command.action(logger, { options: result.data }); + await command.action(logger, { options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { workingHours: { @@ -365,7 +364,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -389,7 +388,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -413,7 +412,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -437,7 +436,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -461,7 +460,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -485,7 +484,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -511,7 +510,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -537,7 +536,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -563,7 +562,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -589,7 +588,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); await command.action(logger, { - options: result.data + options: result.data! }); assert.deepStrictEqual(patchStub.lastCall.args[0].data, { @@ -605,17 +604,17 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { const result = commandOptionsSchema.safeParse({ timeFormat: 'HH:mm', verbose: true }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('When running with application permissions either userId or userName is required')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('When running with application permissions either userId or userName is required')); }); it('fails updating mailbox settings of the signed-in user if userId is specified', async () => { const result = commandOptionsSchema.safeParse({ userId: userId, timeFormat: 'HH:mm', verbose: true }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('You can update mailbox settings of other users only if CLI is authenticated in app-only mode')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('You can update mailbox settings of other users only if CLI is authenticated in app-only mode')); }); it('fails updating mailbox settings of the signed-in user if userName is specified', async () => { const result = commandOptionsSchema.safeParse({ userName: userName, timeFormat: 'HH:mm', verbose: true }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('You can update mailbox settings of other users only if CLI is authenticated in app-only mode')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('You can update mailbox settings of other users only if CLI is authenticated in app-only mode')); }); it('correctly handles API OData error', async () => { @@ -630,6 +629,6 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { } }); const result = commandOptionsSchema.safeParse({ dateFormat: 'dd.MM.yyy' }); - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('Invalid request')); + await assert.rejects(command.action(logger, { options: result.data! }), new CommandError('Invalid request')); }); }); \ No newline at end of file diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts index 8c941d4ef27..e0093c80d72 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; @@ -10,33 +9,32 @@ import { MailboxSettings } from '@microsoft/microsoft-graph-types'; import { accessToken } from '../../../../utils/accessToken.js'; import auth from '../../../../Auth.js'; -const options = globalOptionsZod - .extend({ - userId: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional()), - userName: zod.alias('n', z.string().refine(name => validation.isValidUserPrincipalName(name), name => ({ - message: `'${name}' is not a valid UPN.` - })).optional()), - dateFormat: z.string().optional(), - timeFormat: z.string().optional(), - timeZone: z.string().optional(), - language: z.string().optional(), - delegateMeetingMessageDeliveryOptions: z.enum(['sendToDelegateAndInformationToPrincipal', 'sendToDelegateAndPrincipal', 'sendToDelegateOnly']).optional(), - workingDays: z.string().transform((value) => value.split(',')).pipe(z.enum(['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']).array()).optional(), - workingHoursStartTime: z.string().optional(), - workingHoursEndTime: z.string().optional(), - workingHoursTimeZone: z.string().optional(), - autoReplyExternalAudience: z.enum(['none', 'all', 'contactsOnly']).optional(), - autoReplyExternalMessage: z.string().optional(), - autoReplyInternalMessage: z.string().optional(), - autoReplyStartDateTime: z.string().optional(), - autoReplyStartTimeZone: z.string().optional(), - autoReplyEndDateTime: z.string().optional(), - autoReplyEndTimeZone: z.string().optional(), - autoReplyStatus: z.enum(['disabled', 'scheduled', 'alwaysEnabled']).optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + userId: z.string().refine(id => validation.isValidGuid(id), { + error: e => `'${e.input}' is not a valid GUID.` + }).optional().alias('i'), + userName: z.string().refine(name => validation.isValidUserPrincipalName(name), { + error: e => `'${e.input}' is not a valid UPN.` + }).optional().alias('n'), + dateFormat: z.string().optional(), + timeFormat: z.string().optional(), + timeZone: z.string().optional(), + language: z.string().optional(), + delegateMeetingMessageDeliveryOptions: z.enum(['sendToDelegateAndInformationToPrincipal', 'sendToDelegateAndPrincipal', 'sendToDelegateOnly']).optional(), + workingDays: z.string().transform((value) => value.split(',')).pipe(z.enum(['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']).array()).optional(), + workingHoursStartTime: z.string().optional(), + workingHoursEndTime: z.string().optional(), + workingHoursTimeZone: z.string().optional(), + autoReplyExternalAudience: z.enum(['none', 'all', 'contactsOnly']).optional(), + autoReplyExternalMessage: z.string().optional(), + autoReplyInternalMessage: z.string().optional(), + autoReplyStartDateTime: z.string().optional(), + autoReplyStartTimeZone: z.string().optional(), + autoReplyEndDateTime: z.string().optional(), + autoReplyEndTimeZone: z.string().optional(), + autoReplyStatus: z.enum(['disabled', 'scheduled', 'alwaysEnabled']).optional() +}); declare type Options = z.infer; @@ -53,20 +51,21 @@ class OutlookMailboxSettingsSetCommand extends GraphCommand { return 'Updates user mailbox settings'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !(options.userId && options.userName), { - message: 'Specify either userId or userName, but not both' + error: 'Specify either userId or userName, but not both' }) - .refine(options => [options.workingDays, options.workingHoursStartTime, options.workingHoursEndTime, options.workingHoursTimeZone, + .refine(options => [ + options.workingDays, options.workingHoursStartTime, options.workingHoursEndTime, options.workingHoursTimeZone, options.autoReplyStatus, options.autoReplyExternalAudience, options.autoReplyExternalMessage, options.autoReplyInternalMessage, options.autoReplyStartDateTime, options.autoReplyStartTimeZone, options.autoReplyEndDateTime, options.autoReplyEndTimeZone, options.timeFormat, options.timeZone, options.dateFormat, options.delegateMeetingMessageDeliveryOptions, options.language].filter(o => o !== undefined).length > 0, { - message: 'Specify at least one of the following options: workingDays, workingHoursStartTime, workingHoursEndTime, workingHoursTimeZone, autoReplyStatus, autoReplyExternalAudience, autoReplyExternalMessage, autoReplyInternalMessage, autoReplyStartDateTime, autoReplyStartTimeZone, autoReplyEndDateTime, autoReplyEndTimeZone, timeFormat, timeZone, dateFormat, delegateMeetingMessageDeliveryOptions, or language' + error: 'Specify at least one of the following options: workingDays, workingHoursStartTime, workingHoursEndTime, workingHoursTimeZone, autoReplyStatus, autoReplyExternalAudience, autoReplyExternalMessage, autoReplyInternalMessage, autoReplyStartDateTime, autoReplyStartTimeZone, autoReplyEndDateTime, autoReplyEndTimeZone, timeFormat, timeZone, dateFormat, delegateMeetingMessageDeliveryOptions, or language' }); } diff --git a/src/m365/outlook/commands/roomlist/roomlist-list.spec.ts b/src/m365/outlook/commands/roomlist/roomlist-list.spec.ts index 2afb39fac72..c0bfd663c9e 100644 --- a/src/m365/outlook/commands/roomlist/roomlist-list.spec.ts +++ b/src/m365/outlook/commands/roomlist/roomlist-list.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,14 +11,14 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './roomlist-list.js'; +import command, { options } from './roomlist-list.js'; describe(commands.ROOMLIST_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const jsonOutput = { "value": [ @@ -62,7 +61,7 @@ describe(commands.ROOMLIST_LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/outlook/commands/roomlist/roomlist-list.ts b/src/m365/outlook/commands/roomlist/roomlist-list.ts index 6ad46f838a2..339766a2730 100644 --- a/src/m365/outlook/commands/roomlist/roomlist-list.ts +++ b/src/m365/outlook/commands/roomlist/roomlist-list.ts @@ -6,7 +6,7 @@ import { odata } from '../../../../utils/odata.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = z.strictObject({ ...globalOptionsZod.shape }); class OutlookRoomListListCommand extends GraphCommand { public get name(): string { @@ -17,7 +17,7 @@ class OutlookRoomListListCommand extends GraphCommand { return 'Get a collection of available roomlists'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/pa/commands/environment/environment-get.spec.ts b/src/m365/pa/commands/environment/environment-get.spec.ts index 98d8b22c146..de46f489a2c 100644 --- a/src/m365/pa/commands/environment/environment-get.spec.ts +++ b/src/m365/pa/commands/environment/environment-get.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -8,19 +7,19 @@ import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { accessToken } from '../../../../utils/accessToken.js'; import commands from '../../commands.js'; -import command from './environment-get.js'; +import command, { options } from './environment-get.js'; describe(commands.ENVIRONMENT_GET, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -30,7 +29,7 @@ describe(commands.ENVIRONMENT_GET, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/pa/commands/environment/environment-get.ts b/src/m365/pa/commands/environment/environment-get.ts index 22be4e624e6..83ae4a497a5 100644 --- a/src/m365/pa/commands/environment/environment-get.ts +++ b/src/m365/pa/commands/environment/environment-get.ts @@ -3,16 +3,14 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; -import { zod } from '../../../../utils/zod.js'; import PowerAppsCommand from '../../../base/PowerAppsCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string().optional()), - default: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().optional().alias('n'), + default: z.boolean().optional() +}); declare type Options = z.infer; @@ -29,14 +27,14 @@ class PaEnvironmentGetCommand extends PowerAppsCommand { return 'Gets information about the specified Microsoft Power Apps environment'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !!options.name !== !!options.default, { - message: `Specify either name or default, but not both.` + error: `Specify either name or default, but not both.` }); } diff --git a/src/m365/pa/commands/environment/environment-list.spec.ts b/src/m365/pa/commands/environment/environment-list.spec.ts index 1b9930f6a8f..30683097f0b 100644 --- a/src/m365/pa/commands/environment/environment-list.spec.ts +++ b/src/m365/pa/commands/environment/environment-list.spec.ts @@ -1,26 +1,25 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './environment-list.js'; -import { accessToken } from '../../../../utils/accessToken.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import command, { options } from './environment-list.js'; describe(commands.ENVIRONMENT_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -30,7 +29,7 @@ describe(commands.ENVIRONMENT_LIST, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/pa/commands/environment/environment-list.ts b/src/m365/pa/commands/environment/environment-list.ts index 9481817297e..252bff26429 100644 --- a/src/m365/pa/commands/environment/environment-list.ts +++ b/src/m365/pa/commands/environment/environment-list.ts @@ -5,7 +5,7 @@ import commands from '../../commands.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -const options = globalOptionsZod.strict(); +export const options = z.strictObject({ ...globalOptionsZod.shape }); class PaEnvironmentListCommand extends PowerAppsCommand { public get name(): string { @@ -16,7 +16,7 @@ class PaEnvironmentListCommand extends PowerAppsCommand { return 'Lists Microsoft Power Apps environments in the current tenant'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/planner/commands/task/task-reference-add.spec.ts b/src/m365/planner/commands/task/task-reference-add.spec.ts index 8ae600e41aa..1e71c4b8d3b 100644 --- a/src/m365/planner/commands/task/task-reference-add.spec.ts +++ b/src/m365/planner/commands/task/task-reference-add.spec.ts @@ -9,7 +9,6 @@ import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; - import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; import command from './task-reference-add.js'; diff --git a/src/m365/planner/commands/tenant/tenant-settings-list.spec.ts b/src/m365/planner/commands/tenant/tenant-settings-list.spec.ts index 0e89017dddd..9db5981e915 100644 --- a/src/m365/planner/commands/tenant/tenant-settings-list.spec.ts +++ b/src/m365/planner/commands/tenant/tenant-settings-list.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,7 +11,7 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './tenant-settings-list.js'; +import command, { options } from './tenant-settings-list.js'; describe(commands.TENANT_SETTINGS_LIST, () => { @@ -29,7 +28,7 @@ describe(commands.TENANT_SETTINGS_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -39,7 +38,7 @@ describe(commands.TENANT_SETTINGS_LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/planner/commands/tenant/tenant-settings-list.ts b/src/m365/planner/commands/tenant/tenant-settings-list.ts index 6080ab97d26..f1615c74fba 100644 --- a/src/m365/planner/commands/tenant/tenant-settings-list.ts +++ b/src/m365/planner/commands/tenant/tenant-settings-list.ts @@ -5,7 +5,7 @@ import request, { CliRequestOptions } from '../../../../request.js'; import PlannerCommand from '../../../base/PlannerCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); class PlannerTenantSettingsListCommand extends PlannerCommand { public get name(): string { diff --git a/src/m365/pp/commands/environment/environment-get.spec.ts b/src/m365/pp/commands/environment/environment-get.spec.ts index ad4d6f9637b..dbce5f1633d 100644 --- a/src/m365/pp/commands/environment/environment-get.spec.ts +++ b/src/m365/pp/commands/environment/environment-get.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -8,13 +7,13 @@ import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { accessToken } from '../../../../utils/accessToken.js'; import commands from '../../commands.js'; -import command from './environment-get.js'; +import command, { options } from './environment-get.js'; describe(commands.ENVIRONMENT_GET, () => { const environmentName = 'Default-de347bc8-1aeb-4406-8cb3-97db021cadb4'; @@ -33,7 +32,7 @@ describe(commands.ENVIRONMENT_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -43,7 +42,7 @@ describe(commands.ENVIRONMENT_GET, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/pp/commands/environment/environment-get.ts b/src/m365/pp/commands/environment/environment-get.ts index 01dc58a9fde..9ad4b7d198c 100644 --- a/src/m365/pp/commands/environment/environment-get.ts +++ b/src/m365/pp/commands/environment/environment-get.ts @@ -3,17 +3,15 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; -import { zod } from '../../../../utils/zod.js'; import PowerPlatformCommand from '../../../base/PowerPlatformCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string().optional()), - default: z.boolean().optional(), - asAdmin: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().optional().alias('n'), + default: z.boolean().optional(), + asAdmin: z.boolean().optional() +}); declare type Options = z.infer; @@ -30,14 +28,14 @@ class PpEnvironmentGetCommand extends PowerPlatformCommand { return 'Gets information about the specified Power Platform environment'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !!options.name !== !!options.default, { - message: `Specify either name or default, but not both.` + error: `Specify either name or default, but not both.` }); } diff --git a/src/m365/pp/commands/environment/environment-list.spec.ts b/src/m365/pp/commands/environment/environment-list.spec.ts index b58dff26c86..7f3bc94ef53 100644 --- a/src/m365/pp/commands/environment/environment-list.spec.ts +++ b/src/m365/pp/commands/environment/environment-list.spec.ts @@ -1,26 +1,25 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './environment-list.js'; -import { accessToken } from '../../../../utils/accessToken.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; -import { z } from 'zod'; +import command, { options } from './environment-list.js'; describe(commands.ENVIRONMENT_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -30,7 +29,7 @@ describe(commands.ENVIRONMENT_LIST, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/pp/commands/environment/environment-list.ts b/src/m365/pp/commands/environment/environment-list.ts index c0f7df9d617..7517b16634e 100644 --- a/src/m365/pp/commands/environment/environment-list.ts +++ b/src/m365/pp/commands/environment/environment-list.ts @@ -5,11 +5,10 @@ import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -const options = globalOptionsZod - .extend({ - asAdmin: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + asAdmin: z.boolean().optional() +}); declare type Options = z.infer; @@ -26,7 +25,7 @@ class PpEnvironmentListCommand extends PowerPlatformCommand { return 'Lists Microsoft Power Platform environments'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/pp/commands/gateway/gateway-list.spec.ts b/src/m365/pp/commands/gateway/gateway-list.spec.ts index 27f34cd31be..d6e6652fc32 100644 --- a/src/m365/pp/commands/gateway/gateway-list.spec.ts +++ b/src/m365/pp/commands/gateway/gateway-list.spec.ts @@ -1,26 +1,25 @@ 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 { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { cli } from '../../../../cli/cli.js'; import commands from '../../commands.js'; -import command from './gateway-list.js'; -import { accessToken } from '../../../../utils/accessToken.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import command, { options } from './gateway-list.js'; describe(commands.GATEWAY_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -30,7 +29,7 @@ describe(commands.GATEWAY_LIST, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/pp/commands/gateway/gateway-list.ts b/src/m365/pp/commands/gateway/gateway-list.ts index e6d03f018ef..44128d5e87f 100644 --- a/src/m365/pp/commands/gateway/gateway-list.ts +++ b/src/m365/pp/commands/gateway/gateway-list.ts @@ -5,7 +5,7 @@ import request from '../../../../request.js'; import PowerBICommand from '../../../base/PowerBICommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); class PpGatewayListCommand extends PowerBICommand { public get name(): string { diff --git a/src/m365/pp/commands/tenant/tenant-settings-list.spec.ts b/src/m365/pp/commands/tenant/tenant-settings-list.spec.ts index ae529e5e452..e0d20e72855 100644 --- a/src/m365/pp/commands/tenant/tenant-settings-list.spec.ts +++ b/src/m365/pp/commands/tenant/tenant-settings-list.spec.ts @@ -1,19 +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 { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { cli } from '../../../../cli/cli.js'; import commands from '../../commands.js'; -import command from './tenant-settings-list.js'; -import { accessToken } from '../../../../utils/accessToken.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import command, { options } from './tenant-settings-list.js'; describe(commands.TENANT_SETTINGS_LIST, () => { const successResponse = { @@ -51,12 +50,11 @@ describe(commands.TENANT_SETTINGS_LIST, () => { } }; - let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -66,7 +64,7 @@ describe(commands.TENANT_SETTINGS_LIST, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/pp/commands/tenant/tenant-settings-list.ts b/src/m365/pp/commands/tenant/tenant-settings-list.ts index 0cc70055b11..426ecd70de7 100644 --- a/src/m365/pp/commands/tenant/tenant-settings-list.ts +++ b/src/m365/pp/commands/tenant/tenant-settings-list.ts @@ -5,7 +5,7 @@ import request, { CliRequestOptions } from '../../../../request.js'; import PowerPlatformCommand from '../../../base/PowerPlatformCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); class PpTenantSettingsListCommand extends PowerPlatformCommand { public get name(): string { diff --git a/src/m365/pp/commands/website/website-get.spec.ts b/src/m365/pp/commands/website/website-get.spec.ts index 0cdccdec80b..3a8fdcb0918 100644 --- a/src/m365/pp/commands/website/website-get.spec.ts +++ b/src/m365/pp/commands/website/website-get.spec.ts @@ -1,19 +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 { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; +import { powerPlatform } from '../../../../utils/powerPlatform.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './website-get.js'; -import { powerPlatform } from '../../../../utils/powerPlatform.js'; -import { accessToken } from '../../../../utils/accessToken.js'; +import command, { options } from './website-get.js'; const environment = 'Default-727dc1e9-3cd1-4d1f-8102-ab5c936e52f0'; const powerPageResponse = { @@ -51,7 +50,7 @@ describe(commands.WEBSITE_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -61,7 +60,7 @@ describe(commands.WEBSITE_GET, () => { sinon.stub(accessToken, 'assertAccessTokenType').returns(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/pp/commands/website/website-get.ts b/src/m365/pp/commands/website/website-get.ts index ec48af266b0..7ac25e1f2a9 100644 --- a/src/m365/pp/commands/website/website-get.ts +++ b/src/m365/pp/commands/website/website-get.ts @@ -2,22 +2,21 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import { powerPlatform } from '../../../../utils/powerPlatform.js'; import { validation } from '../../../../utils/validation.js'; -import { zod } from '../../../../utils/zod.js'; import PowerPlatformCommand from '../../../base/PowerPlatformCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; -const options = globalOptionsZod - .extend({ - url: zod.alias('u', z.string().optional() - .refine(url => url === undefined || validation.isValidPowerPagesUrl(url) === true, url => ({ - message: `'${url}' is not a valid Power Pages URL.` - })) - ), - id: zod.alias('i', z.string().uuid().optional()), - name: zod.alias('n', z.string().optional()), - environmentName: zod.alias('e', z.string()) - }).strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + url: z.string().optional() + .refine(url => url === undefined || validation.isValidPowerPagesUrl(url) === true, { + error: e => `'${e.input}' is not a valid Power Pages URL.` + }) + .alias('u'), + id: z.uuid().optional().alias('i'), + name: z.string().optional().alias('n'), + environmentName: z.string().alias('e') +}); declare type Options = z.infer; interface CommandArgs { @@ -33,14 +32,14 @@ class PpWebSiteGetCommand extends PowerPlatformCommand { return 'Gets information about the specified Power Pages website.'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.url, options.id, options.name].filter(x => x !== undefined).length === 1, { - message: `Specify either url, id or name, but not multiple.` + error: `Specify either url, id or name, but not multiple.` }); } diff --git a/src/m365/purview/commands/retentionevent/retentionevent-list.spec.ts b/src/m365/purview/commands/retentionevent/retentionevent-list.spec.ts index b99def1d6b0..3c97d66a7ec 100644 --- a/src/m365/purview/commands/retentionevent/retentionevent-list.spec.ts +++ b/src/m365/purview/commands/retentionevent/retentionevent-list.spec.ts @@ -1,18 +1,17 @@ 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 { CommandError } from '../../../../Command.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 commands from '../../commands.js'; -import command from './retentionevent-list.js'; +import command, { options } from './retentionevent-list.js'; describe(commands.RETENTIONEVENTTYPE_GET, () => { @@ -55,7 +54,7 @@ describe(commands.RETENTIONEVENTTYPE_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -65,7 +64,7 @@ describe(commands.RETENTIONEVENTTYPE_GET, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.accessTokens[auth.defaultResource] = { expiresOn: 'abc', accessToken: 'abc' diff --git a/src/m365/purview/commands/retentionevent/retentionevent-list.ts b/src/m365/purview/commands/retentionevent/retentionevent-list.ts index 2c0af0d1dae..75da89b3432 100644 --- a/src/m365/purview/commands/retentionevent/retentionevent-list.ts +++ b/src/m365/purview/commands/retentionevent/retentionevent-list.ts @@ -5,7 +5,7 @@ import { odata } from '../../../../utils/odata.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = z.strictObject({ ...globalOptionsZod.shape }); class PurviewRetentionEventListCommand extends GraphCommand { public get name(): string { @@ -16,7 +16,7 @@ class PurviewRetentionEventListCommand extends GraphCommand { return 'Get a list of retention events'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.spec.ts b/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.spec.ts index 156b00829cd..492562e235e 100644 --- a/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.spec.ts +++ b/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.spec.ts @@ -1,18 +1,17 @@ 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 { CommandError } from '../../../../Command.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 commands from '../../commands.js'; -import command from './retentioneventtype-list.js'; +import command, { options } from './retentioneventtype-list.js'; describe(commands.RETENTIONEVENTTYPE_LIST, () => { @@ -50,7 +49,7 @@ describe(commands.RETENTIONEVENTTYPE_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -60,7 +59,7 @@ describe(commands.RETENTIONEVENTTYPE_LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.accessTokens[(command as any).resource] = { accessToken: 'abc', expiresOn: new Date() diff --git a/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.ts b/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.ts index 8ea5a9aa2cf..d5effdb4a48 100644 --- a/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.ts +++ b/src/m365/purview/commands/retentioneventtype/retentioneventtype-list.ts @@ -5,7 +5,7 @@ import { odata } from '../../../../utils/odata.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); class PurviewRetentionEventTypeListCommand extends GraphCommand { public get name(): string { diff --git a/src/m365/purview/commands/retentionlabel/retentionlabel-list.spec.ts b/src/m365/purview/commands/retentionlabel/retentionlabel-list.spec.ts index 52c38986c43..2119060c59f 100644 --- a/src/m365/purview/commands/retentionlabel/retentionlabel-list.spec.ts +++ b/src/m365/purview/commands/retentionlabel/retentionlabel-list.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,7 +11,7 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './retentionlabel-list.js'; +import command, { options } from './retentionlabel-list.js'; describe(commands.RETENTIONLABEL_LIST, () => { @@ -61,7 +60,7 @@ describe(commands.RETENTIONLABEL_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -71,7 +70,7 @@ describe(commands.RETENTIONLABEL_LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.accessTokens[(command as any).resource] = { accessToken: 'abc', expiresOn: new Date() diff --git a/src/m365/purview/commands/retentionlabel/retentionlabel-list.ts b/src/m365/purview/commands/retentionlabel/retentionlabel-list.ts index 4bd1b7eaa5c..5a7c6dbf3bc 100644 --- a/src/m365/purview/commands/retentionlabel/retentionlabel-list.ts +++ b/src/m365/purview/commands/retentionlabel/retentionlabel-list.ts @@ -5,7 +5,7 @@ import { odata } from '../../../../utils/odata.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod.strict(); +export const options = globalOptionsZod.strict(); class PurviewRetentionLabelListCommand extends GraphCommand { public get name(): string { diff --git a/src/m365/purview/commands/retentionlabel/retentionlabel-set.spec.ts b/src/m365/purview/commands/retentionlabel/retentionlabel-set.spec.ts index 0cfce32ee66..f7dbd79352e 100644 --- a/src/m365/purview/commands/retentionlabel/retentionlabel-set.spec.ts +++ b/src/m365/purview/commands/retentionlabel/retentionlabel-set.spec.ts @@ -8,7 +8,6 @@ import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; - import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; import command from './retentionlabel-set.js'; diff --git a/src/m365/spe/commands/container/container-add.spec.ts b/src/m365/spe/commands/container/container-add.spec.ts index 75422e6f463..4f0aa54157a 100644 --- a/src/m365/spe/commands/container/container-add.spec.ts +++ b/src/m365/spe/commands/container/container-add.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from "../../../../cli/CommandInfo.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 { cli } from '../../../../cli/cli.js'; -import commands from '../../commands.js'; -import command from './container-add.js'; -import { z } from 'zod'; -import { CommandError } from '../../../../Command.js'; import { spe } from '../../../../utils/spe.js'; +import commands from '../../commands.js'; +import command, { options } from './container-add.js'; describe(commands.CONTAINER_ADD, () => { const spoAdminUrl = 'https://contoso-admin.sharepoint.com'; @@ -40,7 +39,7 @@ describe(commands.CONTAINER_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -53,7 +52,7 @@ describe(commands.CONTAINER_ADD, () => { auth.connection.active = true; auth.connection.spoUrl = spoAdminUrl.replace('-admin.sharepoint.com', '.sharepoint.com'); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spe/commands/container/container-add.ts b/src/m365/spe/commands/container/container-add.ts index 3eb4f5b002f..abf38e6f8f0 100644 --- a/src/m365/spe/commands/container/container-add.ts +++ b/src/m365/spe/commands/container/container-add.ts @@ -1,6 +1,5 @@ import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { validation } from '../../../../utils/validation.js'; @@ -8,23 +7,19 @@ import { spe } from '../../../../utils/spe.js'; import GraphCommand from '../../../base/GraphCommand.js'; import request, { CliRequestOptions } from '../../../../request.js'; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string()), - description: zod.alias('d', z.string()).optional(), - containerTypeId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - containerTypeName: z.string().optional(), - ocrEnabled: z.boolean().optional(), - itemMajorVersionLimit: z.number() - .refine(numb => validation.isValidPositiveInteger(numb), numb => ({ - message: `'${numb}' is not a valid positive integer.` - })).optional(), - itemVersioningEnabled: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().alias('n'), + description: z.string().optional().alias('d'), + containerTypeId: z.uuid().optional(), + containerTypeName: z.string().optional(), + ocrEnabled: z.boolean().optional(), + itemMajorVersionLimit: z.number() + .refine(numb => validation.isValidPositiveInteger(numb), { + error: e => `'${e.input}' is not a valid positive integer.` + }).optional(), + itemVersioningEnabled: z.boolean().optional() +}); declare type Options = z.infer; @@ -41,14 +36,14 @@ class SpeContainerAddCommand extends GraphCommand { return 'Creates a new container'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine((options: Options) => [options.containerTypeId, options.containerTypeName].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: containerTypeId or containerTypeName.' + error: 'Use one of the following options: containerTypeId or containerTypeName.' }); } diff --git a/src/m365/spe/commands/container/container-permission-list.ts b/src/m365/spe/commands/container/container-permission-list.ts index 9204cf1b6e2..63111bab0ae 100644 --- a/src/m365/spe/commands/container/container-permission-list.ts +++ b/src/m365/spe/commands/container/container-permission-list.ts @@ -1,6 +1,5 @@ import { cli } from '../../../../cli/cli.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import commands from '../../commands.js'; @@ -8,11 +7,10 @@ import GraphCommand from '../../../base/GraphCommand.js'; import { odata } from '../../../../utils/odata.js'; import { formatting } from '../../../../utils/formatting.js'; -const options = globalOptionsZod - .extend({ - containerId: zod.alias('i', z.string()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + containerId: z.string().alias('i') +}); declare type Options = z.infer; interface CommandArgs { @@ -32,7 +30,7 @@ class SpeContainerPermissionListCommand extends GraphCommand { return ['id', 'userPrincipalName', 'roles']; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spe/commands/container/container-recyclebinitem-list.spec.ts b/src/m365/spe/commands/container/container-recyclebinitem-list.spec.ts index 18f8078c511..974bdeb28ea 100644 --- a/src/m365/spe/commands/container/container-recyclebinitem-list.spec.ts +++ b/src/m365/spe/commands/container/container-recyclebinitem-list.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.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 commands from '../../commands.js'; -import command from './container-recyclebinitem-list.js'; import { spe } from '../../../../utils/spe.js'; -import { CommandError } from '../../../../Command.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; +import commands from '../../commands.js'; +import command, { options } from './container-recyclebinitem-list.js'; describe(commands.CONTAINER_RECYCLEBINITEM_LIST, () => { const containerTypeId = 'dda3cb36-a16a-40b9-8f04-b01e39fc035d'; @@ -42,7 +41,7 @@ describe(commands.CONTAINER_RECYCLEBINITEM_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -54,7 +53,7 @@ describe(commands.CONTAINER_RECYCLEBINITEM_LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spe/commands/container/container-recyclebinitem-list.ts b/src/m365/spe/commands/container/container-recyclebinitem-list.ts index 71e71f9edfd..fd15ea3ccf5 100644 --- a/src/m365/spe/commands/container/container-recyclebinitem-list.ts +++ b/src/m365/spe/commands/container/container-recyclebinitem-list.ts @@ -2,20 +2,15 @@ import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; -import { validation } from '../../../../utils/validation.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { spe } from '../../../../utils/spe.js'; import { odata } from '../../../../utils/odata.js'; -const options = globalOptionsZod - .extend({ - containerTypeId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - containerTypeName: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + containerTypeId: z.uuid().optional(), + containerTypeName: z.string().optional() +}); declare type Options = z.infer; @@ -32,7 +27,7 @@ class SpeContainerRecycleBinItemListCommand extends GraphCommand { return 'Lists deleted containers of a specific container type'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } @@ -40,10 +35,10 @@ class SpeContainerRecycleBinItemListCommand extends GraphCommand { return ['id', 'displayName']; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine((options: Options) => [options.containerTypeId, options.containerTypeName].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: containerTypeId or containerTypeName.' + error: 'Use one of the following options: containerTypeId or containerTypeName.' }); } diff --git a/src/m365/spe/commands/container/container-recyclebinitem-restore.spec.ts b/src/m365/spe/commands/container/container-recyclebinitem-restore.spec.ts index 75e9fcf56b6..6aa46601425 100644 --- a/src/m365/spe/commands/container/container-recyclebinitem-restore.spec.ts +++ b/src/m365/spe/commands/container/container-recyclebinitem-restore.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from "../../../../cli/CommandInfo.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 { cli } from '../../../../cli/cli.js'; -import commands from '../../commands.js'; -import command from './container-recyclebinitem-restore.js'; import { spe } from '../../../../utils/spe.js'; -import { z } from 'zod'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command, { options } from './container-recyclebinitem-restore.js'; describe(commands.CONTAINER_RECYCLEBINITEM_RESTORE, () => { const containerTypeId = 'c6f08d91-77fa-485f-9369-f246ec0fc19c'; @@ -35,7 +34,7 @@ describe(commands.CONTAINER_RECYCLEBINITEM_RESTORE, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let loggerLogSpy: sinon.SinonSpy; before(() => { @@ -46,7 +45,7 @@ describe(commands.CONTAINER_RECYCLEBINITEM_RESTORE, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spe/commands/container/container-recyclebinitem-restore.ts b/src/m365/spe/commands/container/container-recyclebinitem-restore.ts index 9f3781d2026..79484bfd62c 100644 --- a/src/m365/spe/commands/container/container-recyclebinitem-restore.ts +++ b/src/m365/spe/commands/container/container-recyclebinitem-restore.ts @@ -2,7 +2,6 @@ import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; -import { validation } from '../../../../utils/validation.js'; import GraphCommand from '../../../base/GraphCommand.js'; import { SpeContainer, spe } from '../../../../utils/spe.js'; import { odata } from '../../../../utils/odata.js'; @@ -10,17 +9,13 @@ import { formatting } from '../../../../utils/formatting.js'; import { cli } from '../../../../cli/cli.js'; import request, { CliRequestOptions } from '../../../../request.js'; -const options = globalOptionsZod - .extend({ - containerTypeId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - containerTypeName: z.string().optional(), - id: z.string().optional(), - name: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + containerTypeId: z.uuid().optional(), + containerTypeName: z.string().optional(), + id: z.string().optional(), + name: z.string().optional() +}); declare type Options = z.infer; @@ -37,20 +32,20 @@ class SpeContainerRecycleBinItemRestoreCommand extends GraphCommand { return 'Restores a deleted container'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine((options: Options) => [options.id, options.name].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: id or name.' + error: 'Use one of the following options: id or name.' }) .refine((options: Options) => !options.name || [options.containerTypeId, options.containerTypeName].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options when specifying the container name: containerTypeId or containerTypeName.' + error: 'Use one of the following options when specifying the container name: containerTypeId or containerTypeName.' }) .refine((options: Options) => options.name || [options.containerTypeId, options.containerTypeName].filter(o => o !== undefined).length === 0, { - message: 'Options containerTypeId and containerTypeName are only required when restoring a container by name.' + error: 'Options containerTypeId and containerTypeName are only required when restoring a container by name.' }); } diff --git a/src/m365/spe/commands/container/container-remove.spec.ts b/src/m365/spe/commands/container/container-remove.spec.ts index dcdaad689a8..cf6503d98df 100644 --- a/src/m365/spe/commands/container/container-remove.spec.ts +++ b/src/m365/spe/commands/container/container-remove.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from "../../../../cli/CommandInfo.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 { cli } from '../../../../cli/cli.js'; -import commands from '../../commands.js'; -import command from './container-remove.js'; import { spe } from '../../../../utils/spe.js'; -import { z } from 'zod'; -import { CommandError } from '../../../../Command.js'; +import commands from '../../commands.js'; +import command, { options } from './container-remove.js'; describe(commands.CONTAINER_REMOVE, () => { const containerTypeId = 'c6f08d91-77fa-485f-9369-f246ec0fc19c'; @@ -24,7 +23,7 @@ describe(commands.CONTAINER_REMOVE, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let confirmationPromptStub: sinon.SinonStub; before(() => { @@ -38,7 +37,7 @@ describe(commands.CONTAINER_REMOVE, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spe/commands/container/container-remove.ts b/src/m365/spe/commands/container/container-remove.ts index 8b07a41c0a1..99483355327 100644 --- a/src/m365/spe/commands/container/container-remove.ts +++ b/src/m365/spe/commands/container/container-remove.ts @@ -1,27 +1,21 @@ import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; -import { validation } from '../../../../utils/validation.js'; import { spe } from '../../../../utils/spe.js'; import GraphCommand from '../../../base/GraphCommand.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { cli } from '../../../../cli/cli.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string()).optional(), - name: zod.alias('n', z.string()).optional(), - containerTypeId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - containerTypeName: z.string().optional(), - recycle: z.boolean().optional(), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.string().optional().alias('i'), + name: z.string().optional().alias('n'), + containerTypeId: z.uuid().optional(), + containerTypeName: z.string().optional(), + recycle: z.boolean().optional(), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -38,20 +32,20 @@ class SpeContainerRemoveCommand extends GraphCommand { return 'Removes a container'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine((options: Options) => [options.id, options.name].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: id or name.' + error: 'Use one of the following options: id or name.' }) .refine((options: Options) => !options.name || [options.containerTypeId, options.containerTypeName].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options when specifying the container name: containerTypeId or containerTypeName.' + error: 'Use one of the following options when specifying the container name: containerTypeId or containerTypeName.' }) .refine((options: Options) => options.name || [options.containerTypeId, options.containerTypeName].filter(o => o !== undefined).length === 0, { - message: 'Options containerTypeId and containerTypeName are only required when deleting a container by name.' + error: 'Options containerTypeId and containerTypeName are only required when deleting a container by name.' }); } diff --git a/src/m365/spe/commands/containertype/containertype-add.spec.ts b/src/m365/spe/commands/containertype/containertype-add.spec.ts index a4d9aeafdd7..5a3415b6861 100644 --- a/src/m365/spe/commands/containertype/containertype-add.spec.ts +++ b/src/m365/spe/commands/containertype/containertype-add.spec.ts @@ -1,25 +1,24 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './containertype-add.js'; -import { cli } from '../../../../cli/cli.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { accessToken } from '../../../../utils/accessToken.js'; +import command, { options } from './containertype-add.js'; describe(commands.CONTAINERTYPE_ADD, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let commandInfo: CommandInfo; @@ -57,7 +56,7 @@ describe(commands.CONTAINERTYPE_ADD, () => { auth.connection.active = true; auth.connection.appId = 'a0de833a-3629-489a-8fc8-4dd0c431878c'; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spe/commands/containertype/containertype-add.ts b/src/m365/spe/commands/containertype/containertype-add.ts index 4659ed98468..5d99ecd90c4 100644 --- a/src/m365/spe/commands/containertype/containertype-add.ts +++ b/src/m365/spe/commands/containertype/containertype-add.ts @@ -1,7 +1,6 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { validation } from '../../../../utils/validation.js'; import GraphDelegatedCommand from '../../../base/GraphDelegatedCommand.js'; @@ -10,34 +9,30 @@ import Auth from '../../../../Auth.js'; const consumingTenantOverridablesOptions = ['urlTemplate', 'isDiscoverabilityEnabled', 'isSearchEnabled', 'isItemVersioningEnabled', 'itemMajorVersionLimit', 'maxStoragePerContainerInBytes']; -const options = globalOptionsZod - .extend({ - name: zod.alias('n', z.string()), - appId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - billingType: z.enum(['standard', 'trial', 'directToCustomer']).default('standard'), - consumingTenantOverridables: z.string() - .refine(values => values.split(',').every(v => consumingTenantOverridablesOptions.includes(v.trim())), values => ({ - message: `'${values}' is not a valid value. Valid options are: ${consumingTenantOverridablesOptions.join(', ')}.` - })).optional(), - isDiscoverabilityEnabled: z.boolean().optional(), - isItemVersioningEnabled: z.boolean().optional(), - isSearchEnabled: z.boolean().optional(), - isSharingRestricted: z.boolean().optional(), - itemMajorVersionLimit: z.number() - .refine(n => validation.isValidPositiveInteger(n), n => ({ - message: `'${n}' is not a valid positive integer.` - })).optional(), - maxStoragePerContainerInBytes: z.number() - .refine(n => validation.isValidPositiveInteger(n), n => ({ - message: `'${n}' is not a valid positive integer.` - })).optional(), - sharingCapability: z.enum(['disabled', 'externalUserSharingOnly', 'externalUserAndGuestSharing', 'existingExternalUserSharingOnly']).optional(), - urlTemplate: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + name: z.string().alias('n'), + appId: z.uuid().optional(), + billingType: z.enum(['standard', 'trial', 'directToCustomer']).default('standard'), + consumingTenantOverridables: z.string() + .refine(values => values.split(',').every(v => consumingTenantOverridablesOptions.includes(v.trim())), { + error: e => `'${e.input}' is not a valid value. Valid options are: ${consumingTenantOverridablesOptions.join(', ')}.` + }).optional(), + isDiscoverabilityEnabled: z.boolean().optional(), + isItemVersioningEnabled: z.boolean().optional(), + isSearchEnabled: z.boolean().optional(), + isSharingRestricted: z.boolean().optional(), + itemMajorVersionLimit: z.number() + .refine(n => validation.isValidPositiveInteger(n), { + error: e => `'${e.input}' is not a valid positive integer.` + }).optional(), + maxStoragePerContainerInBytes: z.number() + .refine(n => validation.isValidPositiveInteger(n), { + error: e => `'${e.input}' is not a valid positive integer.` + }).optional(), + sharingCapability: z.enum(['disabled', 'externalUserSharingOnly', 'externalUserAndGuestSharing', 'existingExternalUserSharingOnly']).optional(), + urlTemplate: z.string().optional() +}); declare type Options = z.infer; @@ -54,14 +49,14 @@ class SpeContainerTypeAddCommand extends GraphDelegatedCommand { return 'Creates a new container type'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => !options.itemMajorVersionLimit || options.isItemVersioningEnabled !== false, { - message: `Cannot set itemMajorVersionLimit when isItemVersioningEnabled is false.` + error: `Cannot set itemMajorVersionLimit when isItemVersioningEnabled is false.` }); } diff --git a/src/m365/spe/commands/containertype/containertype-get.spec.ts b/src/m365/spe/commands/containertype/containertype-get.spec.ts index d4981f320f8..5acd8325cc1 100644 --- a/src/m365/spe/commands/containertype/containertype-get.spec.ts +++ b/src/m365/spe/commands/containertype/containertype-get.spec.ts @@ -1,29 +1,28 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from "../../../../cli/CommandInfo.js"; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { cli } from '../../../../cli/cli.js'; import commands from '../../commands.js'; -import command from './containertype-get.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; -import { formatting } from '../../../../utils/formatting.js'; -import { accessToken } from '../../../../utils/accessToken.js'; +import command, { options } from './containertype-get.js'; describe(commands.CONTAINERTYPE_GET, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; - const containerTypeId = '3ec7c59d-ef31-0752-1ab5-5c343a5e8557'; + const containerTypeId = '11335700-9a00-4c00-84dd-0c210f203f01'; const containerTypeName = 'SharePoint Embedded Free Trial Container Type'; const containerTypeResponse = { "id": containerTypeId, @@ -54,7 +53,7 @@ describe(commands.CONTAINERTYPE_GET, () => { auth.connection.active = true; auth.connection.spoUrl = 'https://contoso.sharepoint.com'; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; sinon.stub(accessToken, 'assertAccessTokenType').withArgs('delegated').returns(); }); diff --git a/src/m365/spe/commands/containertype/containertype-get.ts b/src/m365/spe/commands/containertype/containertype-get.ts index 0c2b3f81fb1..2932701a05e 100644 --- a/src/m365/spe/commands/containertype/containertype-get.ts +++ b/src/m365/spe/commands/containertype/containertype-get.ts @@ -1,9 +1,7 @@ import { Logger } from '../../../../cli/Logger.js'; -import { validation } from '../../../../utils/validation.js'; import commands from '../../commands.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import GraphDelegatedCommand from '../../../base/GraphDelegatedCommand.js'; import { formatting } from '../../../../utils/formatting.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -11,17 +9,11 @@ import { odata } from '../../../../utils/odata.js'; import { cli } from '../../../../cli/cli.js'; import { SpeContainerType } from '../../../../utils/spe.js'; -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })) - .optional() - ), - name: zod.alias('n', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + name: z.string().optional().alias('n') +}); declare type Options = z.infer; @@ -38,14 +30,14 @@ class SpeContainerTypeGetCommand extends GraphDelegatedCommand { return 'Gets a container type'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.id, options.name].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: id or name.' + error: 'Use one of the following options: id or name.' }); } diff --git a/src/m365/spe/commands/containertype/containertype-remove.spec.ts b/src/m365/spe/commands/containertype/containertype-remove.spec.ts index a8024c90caf..cdde647c433 100644 --- a/src/m365/spe/commands/containertype/containertype-remove.spec.ts +++ b/src/m365/spe/commands/containertype/containertype-remove.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from "../../../../cli/CommandInfo.js"; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; +import config from '../../../../config.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 { cli } from '../../../../cli/cli.js'; import commands from '../../commands.js'; -import command from './containertype-remove.js'; -import { z } from 'zod'; -import { CommandError } from '../../../../Command.js'; -import config from '../../../../config.js'; +import command, { options } from './containertype-remove.js'; describe(commands.CONTAINERTYPE_REMOVE, () => { const spoAdminUrl = 'https://contoso-admin.sharepoint.com'; @@ -23,7 +22,7 @@ describe(commands.CONTAINERTYPE_REMOVE, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let confirmationPromptStub: sinon.SinonStub; const CsomContainerTypeResponse = [ @@ -68,7 +67,7 @@ describe(commands.CONTAINERTYPE_REMOVE, () => { auth.connection.active = true; auth.connection.spoUrl = spoAdminUrl.replace('-admin.sharepoint.com', '.sharepoint.com'); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spe/commands/containertype/containertype-remove.ts b/src/m365/spe/commands/containertype/containertype-remove.ts index 5696384076a..9a8b159228d 100644 --- a/src/m365/spe/commands/containertype/containertype-remove.ts +++ b/src/m365/spe/commands/containertype/containertype-remove.ts @@ -1,12 +1,10 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; import { ClientSvcResponse, ClientSvcResponseContents, spo } from '../../../../utils/spo.js'; import { cli } from '../../../../cli/cli.js'; -import { validation } from '../../../../utils/validation.js'; import request, { CliRequestOptions } from '../../../../request.js'; import config from '../../../../config.js'; import { formatting } from '../../../../utils/formatting.js'; @@ -16,18 +14,12 @@ interface CsomContainerType { ContainerTypeId: string; } -const options = globalOptionsZod - .extend({ - id: zod.alias('i', z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })) - .optional() - ), - name: zod.alias('n', z.string().optional()), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.uuid().optional().alias('i'), + name: z.string().optional().alias('n'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -45,14 +37,14 @@ class SpeContainerTypeRemoveCommand extends SpoCommand { return 'Remove a specific container type'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.id, options.name].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: id, name.' + error: 'Use one of the following options: id, name.' }); } diff --git a/src/m365/spfx/commands/project/project-upgrade.spec.ts b/src/m365/spfx/commands/project/project-upgrade.spec.ts index a02ce33345c..5686c0c8644 100644 --- a/src/m365/spfx/commands/project/project-upgrade.spec.ts +++ b/src/m365/spfx/commands/project/project-upgrade.spec.ts @@ -2,7 +2,6 @@ import assert from 'assert'; import fs from 'fs'; import path from 'path'; import sinon from 'sinon'; -import { z } from 'zod'; import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -15,14 +14,14 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; import { Manifest, Project, VsCode } from './project-model/index.js'; -import command from './project-upgrade.js'; +import command, { options } from './project-upgrade.js'; import { Finding, FindingToReport } from './report-model/index.js'; describe(commands.PROJECT_UPGRADE, () => { let log: any[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let trackEvent: any; let telemetryCommandName: any; let packagesDevExact: string[]; @@ -41,7 +40,7 @@ describe(commands.PROJECT_UPGRADE, () => { sinon.stub(session, 'getId').returns(''); project141webPartNoLib = (command as any).getProject(projectPath); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spfx/commands/project/project-upgrade.ts b/src/m365/spfx/commands/project/project-upgrade.ts index 18387a09979..db2eea7882b 100644 --- a/src/m365/spfx/commands/project/project-upgrade.ts +++ b/src/m365/spfx/commands/project/project-upgrade.ts @@ -8,7 +8,6 @@ import { Logger } from '../../../../cli/Logger.js'; import Command, { CommandError, globalOptionsZod } from '../../../../Command.js'; import { fsUtil } from '../../../../utils/fsUtil.js'; import { packageManager } from '../../../../utils/packageManager.js'; -import { zod } from '../../../../utils/zod.js'; import { Dictionary, Hash } from '../../../../utils/types.js'; import commands from '../../commands.js'; import { BaseProjectCommand } from './base-project-command.js'; @@ -18,15 +17,14 @@ import { Finding, FindingToReport, FindingTour, FindingTourStep } from './report import { ReportData, ReportDataModification } from './report-model/ReportData.js'; import { Rule } from './Rule.js'; -const options = globalOptionsZod - .extend({ - packageManager: z.enum(['npm', 'pnpm', 'yarn']).default('npm'), - preview: z.boolean().optional(), - toVersion: zod.alias('v', z.string().optional()), - shell: z.enum(['bash', 'powershell', 'cmd']).default('powershell'), - output: z.enum(['json', 'text', 'md', 'tour', 'csv', 'none']).optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + packageManager: z.enum(['npm', 'pnpm', 'yarn']).default('npm'), + preview: z.boolean().optional(), + toVersion: z.string().optional().alias('v'), + shell: z.enum(['bash', 'powershell', 'cmd']).default('powershell'), + output: z.enum(['json', 'text', 'md', 'tour', 'csv', 'none']).optional() +}); declare type Options = z.infer; @@ -108,7 +106,7 @@ class SpfxProjectUpgradeCommand extends BaseProjectCommand { return ['json', 'text', 'md', 'tour']; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spo/commands/contenttype/contenttype-field-remove.spec.ts b/src/m365/spo/commands/contenttype/contenttype-field-remove.spec.ts index 345c8946ade..4af70b06e63 100644 --- a/src/m365/spo/commands/contenttype/contenttype-field-remove.spec.ts +++ b/src/m365/spo/commands/contenttype/contenttype-field-remove.spec.ts @@ -12,9 +12,9 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import { spo } from '../../../../utils/spo.js'; import commands from '../../commands.js'; - import command from './contenttype-field-remove.js'; import { settingsNames } from '../../../../settingsNames.js'; + const WEB_URL = 'https://contoso.sharepoint.com'; const FIELD_LINK_ID = "5ee2dd25-d941-455a-9bdb-7f2c54aed11b"; const CONTENT_TYPE_ID = "0x0100558D85B7216F6A489A499DB361E1AE2F"; diff --git a/src/m365/spo/commands/contenttypehub/contenttypehub-get.spec.ts b/src/m365/spo/commands/contenttypehub/contenttypehub-get.spec.ts index da4f6bcf037..9ed3b25bad5 100644 --- a/src/m365/spo/commands/contenttypehub/contenttypehub-get.spec.ts +++ b/src/m365/spo/commands/contenttypehub/contenttypehub-get.spec.ts @@ -11,7 +11,6 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import { spo } from '../../../../utils/spo.js'; import commands from '../../commands.js'; - import command from './contenttypehub-get.js'; describe(commands.CONTENTTYPEHUB_GET, () => { diff --git a/src/m365/spo/commands/file/file-version-keep.spec.ts b/src/m365/spo/commands/file/file-version-keep.spec.ts index d6801fd512f..da6b0b4ff2d 100644 --- a/src/m365/spo/commands/file/file-version-keep.spec.ts +++ b/src/m365/spo/commands/file/file-version-keep.spec.ts @@ -11,15 +11,14 @@ import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { z } from 'zod'; import commands from '../../commands.js'; -import command from './file-version-keep.js'; +import command, { options } from './file-version-keep.js'; describe(commands.FILE_VERSION_KEEP, () => { let log: any[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const validWebUrl = "https://contoso.sharepoint.com"; const validFileUrl = "/Shared Documents/Document.docx"; const validFileId = "7a9b8bb6-d5c4-4de9-ab76-5210a7879e89"; @@ -31,7 +30,7 @@ describe(commands.FILE_VERSION_KEEP, () => { sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/spo/commands/file/file-version-keep.ts b/src/m365/spo/commands/file/file-version-keep.ts index 0664a739066..2042c1c252e 100644 --- a/src/m365/spo/commands/file/file-version-keep.ts +++ b/src/m365/spo/commands/file/file-version-keep.ts @@ -3,29 +3,23 @@ import { Logger } from '../../../../cli/Logger.js'; import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { validation } from '../../../../utils/validation.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; import { odata } from '../../../../utils/odata.js'; -export const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - fileUrl: z.string().optional(), - fileId: zod.alias('i', z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional() - ), - label: z.string() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + fileUrl: z.string().optional(), + fileId: z.uuid().optional().alias('i'), + label: z.string() +}); declare type Options = z.infer; @@ -42,14 +36,14 @@ class SpoFileVersionKeepCommand extends SpoCommand { return 'Ensure that a specific file version will never expire'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.fileUrl, options.fileId].filter(o => o !== undefined).length === 1, { - message: `Specify 'fileUrl' or 'fileId', but not both.` + error: `Specify 'fileUrl' or 'fileId', but not both.` }); } diff --git a/src/m365/spo/commands/homesite/homesite-add.spec.ts b/src/m365/spo/commands/homesite/homesite-add.spec.ts index 9d333ab23f0..d084875db3e 100644 --- a/src/m365/spo/commands/homesite/homesite-add.spec.ts +++ b/src/m365/spo/commands/homesite/homesite-add.spec.ts @@ -2,25 +2,24 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from '../homesite/homesite-add.js'; -import { z } from 'zod'; -import { entraGroup } from '../../../../utils/entraGroup.js'; +import command, { options } from '../homesite/homesite-add.js'; describe(commands.HOMESITE_ADD, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const homeSite = "https://contoso.sharepoint.com/sites/testcomms"; const homeSites = { @@ -61,7 +60,7 @@ describe(commands.HOMESITE_ADD, () => { auth.connection.active = true; auth.connection.spoUrl = 'https://contoso.sharepoint.com'; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/homesite/homesite-add.ts b/src/m365/spo/commands/homesite/homesite-add.ts index 14801b055b1..331821bfcbb 100644 --- a/src/m365/spo/commands/homesite/homesite-add.ts +++ b/src/m365/spo/commands/homesite/homesite-add.ts @@ -1,5 +1,4 @@ import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import { spo } from '../../../../utils/spo.js'; @@ -9,26 +8,24 @@ import commands from '../../commands.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { entraGroup } from '../../../../utils/entraGroup.js'; -const options = globalOptionsZod - .extend({ - url: zod.alias('u', z.string() - .refine((url: string) => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - audienceIds: z.string() - .refine(audiences => validation.isValidGuidArray(audiences) === true, audiences => ({ - message: `The following GUIDs are invalid: ${validation.isValidGuidArray(audiences)}.` - })).optional(), - audienceNames: z.string().optional(), - vivaConnectionsDefaultStart: z.boolean().optional(), - isInDraftMode: z.boolean().optional(), - order: z.number() - .refine(order => validation.isValidPositiveInteger(order) === true, order => ({ - message: `'${order}' is not a positive integer.` - })).optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + url: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }).alias('u'), + audienceIds: z.string() + .refine(audiences => validation.isValidGuidArray(audiences) === true, { + error: e => `The following GUIDs are invalid: ${e.input}.` + }).optional(), + audienceNames: z.string().optional(), + vivaConnectionsDefaultStart: z.boolean().optional(), + isInDraftMode: z.boolean().optional(), + order: z.number() + .refine(order => validation.isValidPositiveInteger(order) === true, { + error: e => `'${e.input}' is not a positive integer.` + }).optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -44,11 +41,11 @@ class SpoHomeSiteAddCommand extends SpoCommand { return 'Adds a home site'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine( (options: Options) => [options.audienceIds, options.audienceNames].filter(o => o !== undefined).length <= 1, diff --git a/src/m365/spo/commands/homesite/homesite-get.spec.ts b/src/m365/spo/commands/homesite/homesite-get.spec.ts index 942833f0e12..3c052a590a0 100644 --- a/src/m365/spo/commands/homesite/homesite-get.spec.ts +++ b/src/m365/spo/commands/homesite/homesite-get.spec.ts @@ -1,18 +1,17 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import { telemetry } from '../../../../telemetry.js'; +import { odata } from '../../../../utils/odata.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './homesite-get.js'; -import { odata } from '../../../../utils/odata.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; -import { z } from 'zod'; +import command, { options } from './homesite-get.js'; describe(commands.HOMESITE_GET, () => { const spoAdminUrl = 'https://contoso-admin.sharepoint.com'; @@ -59,7 +58,7 @@ describe(commands.HOMESITE_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -70,7 +69,7 @@ describe(commands.HOMESITE_GET, () => { auth.connection.spoUrl = 'https://contoso.sharepoint.com'; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/homesite/homesite-get.ts b/src/m365/spo/commands/homesite/homesite-get.ts index 9a590a31b2d..758149ef7fd 100644 --- a/src/m365/spo/commands/homesite/homesite-get.ts +++ b/src/m365/spo/commands/homesite/homesite-get.ts @@ -1,5 +1,4 @@ import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { globalOptionsZod } from '../../../../Command.js'; import { validation } from '../../../../utils/validation.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -9,15 +8,14 @@ import commands from '../../commands.js'; import { odata } from '../../../../utils/odata.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; -const options = globalOptionsZod - .extend({ - url: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + url: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u') +}); declare type Options = z.infer; interface CommandArgs { @@ -33,7 +31,7 @@ class SpoHomeSiteGetCommand extends SpoCommand { return 'Gets information about a home site'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spo/commands/homesite/homesite-remove.spec.ts b/src/m365/spo/commands/homesite/homesite-remove.spec.ts index 8176bb86831..80929b332db 100644 --- a/src/m365/spo/commands/homesite/homesite-remove.spec.ts +++ b/src/m365/spo/commands/homesite/homesite-remove.spec.ts @@ -2,6 +2,7 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; @@ -11,16 +12,14 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import { spo } from '../../../../utils/spo.js'; import commands from '../../commands.js'; -import command from './homesite-remove.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { z } from 'zod'; +import command, { options } from './homesite-remove.js'; describe(commands.HOMESITE_REMOVE, () => { let log: any[]; let logger: Logger; let promptIssued: boolean = false; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const siteId = '00000000-0000-0000-0000-000000000010'; before(() => { @@ -31,7 +30,7 @@ describe(commands.HOMESITE_REMOVE, () => { auth.connection.active = true; auth.connection.spoUrl = 'https://contoso.sharepoint.com'; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/homesite/homesite-remove.ts b/src/m365/spo/commands/homesite/homesite-remove.ts index d0ed1ff295d..e20d2f99f23 100644 --- a/src/m365/spo/commands/homesite/homesite-remove.ts +++ b/src/m365/spo/commands/homesite/homesite-remove.ts @@ -1,5 +1,4 @@ import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { globalOptionsZod } from '../../../../Command.js'; import { validation } from '../../../../utils/validation.js'; import { cli } from '../../../../cli/cli.js'; @@ -9,16 +8,15 @@ import { spo } from '../../../../utils/spo.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - url: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + url: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; interface CommandArgs { @@ -34,7 +32,7 @@ class SpoHomeSiteRemoveCommand extends SpoCommand { return 'Removes a Home Site'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spo/commands/homesite/homesite-set.spec.ts b/src/m365/spo/commands/homesite/homesite-set.spec.ts index df8e2fc8fa7..0836231e25b 100644 --- a/src/m365/spo/commands/homesite/homesite-set.spec.ts +++ b/src/m365/spo/commands/homesite/homesite-set.spec.ts @@ -1,25 +1,24 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import { CommandError } from '../../../../Command.js'; import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './homesite-set.js'; -import { z } from 'zod'; -import { entraGroup } from '../../../../utils/entraGroup.js'; +import command, { options } from './homesite-set.js'; describe(commands.HOMESITE_SET, () => { let log: any[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const siteUrl = 'https://contoso.sharepoint.com/sites/Work'; const spoAdminUrl = 'https://contoso-admin.sharepoint.com'; @@ -52,7 +51,7 @@ describe(commands.HOMESITE_SET, () => { auth.connection.active = true; auth.connection.spoUrl = 'https://contoso.sharepoint.com'; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/homesite/homesite-set.ts b/src/m365/spo/commands/homesite/homesite-set.ts index bdeabff465e..8968f555c84 100644 --- a/src/m365/spo/commands/homesite/homesite-set.ts +++ b/src/m365/spo/commands/homesite/homesite-set.ts @@ -1,5 +1,4 @@ import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { globalOptionsZod } from '../../../../Command.js'; import { Logger } from '../../../../cli/Logger.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -9,28 +8,27 @@ import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; import { entraGroup } from '../../../../utils/entraGroup.js'; -const optionsSchema = globalOptionsZod - .extend({ - url: zod.alias('u', z.string() - .refine((url: string) => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - vivaConnectionsDefaultStart: z.boolean().optional(), - draftMode: z.boolean().optional(), - audienceIds: z.string() - .refine(audiences => validation.isValidGuidArray(audiences) === true, audiences => ({ - message: `The following GUIDs are invalid: ${validation.isValidGuidArray(audiences)}.` - })).optional(), - audienceNames: z.string().optional(), - targetedLicenseType: z.enum(['everyone', 'frontLineWorkers', 'informationWorkers']).optional(), - order: z.number() - .refine(order => validation.isValidPositiveInteger(order) === true, order => ({ - message: `'${order}' is not a positive integer.` - })).optional() - }); - -type Options = z.infer; +export const options = z.strictObject({ + ...globalOptionsZod.shape, + url: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }).alias('u'), + vivaConnectionsDefaultStart: z.boolean().optional(), + draftMode: z.boolean().optional(), + audienceIds: z.string() + .refine(audiences => validation.isValidGuidArray(audiences) === true, { + error: e => `The following GUIDs are invalid: ${e.input}.` + }).optional(), + audienceNames: z.string().optional(), + targetedLicenseType: z.enum(['everyone', 'frontLineWorkers', 'informationWorkers']).optional(), + order: z.number() + .refine(order => validation.isValidPositiveInteger(order) === true, { + error: e => `'${e.input}' is not a positive integer.` + }).optional() +}); + +type Options = z.infer; interface CommandArgs { options: Options; @@ -45,12 +43,12 @@ class SpoHomeSiteSetCommand extends SpoCommand { return 'Updates an existing SharePoint home site.'; } - public get schema(): z.ZodTypeAny { - return optionsSchema; + public get schema(): z.ZodType { + return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine( (options: Options) => [options.audienceIds, options.audienceNames].filter(o => o !== undefined).length <= 1, diff --git a/src/m365/spo/commands/list/list-defaultvalue-clear.spec.ts b/src/m365/spo/commands/list/list-defaultvalue-clear.spec.ts index 6d831abbeaf..c900b3167a0 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-clear.spec.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-clear.spec.ts @@ -1,26 +1,25 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './list-defaultvalue-clear.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; -import { formatting } from '../../../../utils/formatting.js'; -import { CommandError } from '../../../../Command.js'; +import command, { options } from './list-defaultvalue-clear.js'; describe(commands.LIST_DEFAULTVALUE_CLEAR, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let confirmationPromptStub: sinon.SinonStub; let putStub: sinon.SinonStub; @@ -41,7 +40,7 @@ describe(commands.LIST_DEFAULTVALUE_CLEAR, () => { sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/spo/commands/list/list-defaultvalue-clear.ts b/src/m365/spo/commands/list/list-defaultvalue-clear.ts index 8dc2792ddcb..30e98e7dc28 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-clear.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-clear.ts @@ -1,7 +1,6 @@ import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { DOMParser } from '@xmldom/xmldom'; @@ -11,25 +10,20 @@ import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; import { cli } from '../../../../cli/cli.js'; -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - listId: zod.alias('i', z.string().optional() - .refine(id => id === undefined || validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })) - ), - listTitle: zod.alias('t', z.string().optional()), - listUrl: z.string().optional(), - fieldName: z.string().optional(), - folderUrl: z.string().optional(), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + listId: z.uuid().optional().alias('i'), + listTitle: z.string().optional().alias('t'), + listUrl: z.string().optional(), + fieldName: z.string().optional(), + folderUrl: z.string().optional(), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -46,17 +40,17 @@ class SpoListDefaultValueClearCommand extends SpoCommand { return 'Clears default column values for a specific document library'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.listId, options.listTitle, options.listUrl].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: listId, listTitle, listUrl.' + error: 'Use one of the following options: listId, listTitle, listUrl.' }) .refine(options => (options.fieldName !== undefined) !== (options.folderUrl !== undefined) || (options.fieldName === undefined && options.folderUrl === undefined), { - message: `Specify 'fieldName' or 'folderUrl', but not both.` + error: `Specify 'fieldName' or 'folderUrl', but not both.` }); } diff --git a/src/m365/spo/commands/list/list-defaultvalue-list.spec.ts b/src/m365/spo/commands/list/list-defaultvalue-list.spec.ts index 7e74db4d3c2..c766b60771b 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-list.spec.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-list.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './list-defaultvalue-list.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; -import { formatting } from '../../../../utils/formatting.js'; -import { CommandError } from '../../../../Command.js'; +import command, { options } from './list-defaultvalue-list.js'; describe(commands.LIST_DEFAULTVALUE_LIST, () => { const siteUrl = 'https://contoso.sharepoint.com/sites/marketing'; @@ -69,7 +68,7 @@ describe(commands.LIST_DEFAULTVALUE_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -78,7 +77,7 @@ describe(commands.LIST_DEFAULTVALUE_LIST, () => { sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/spo/commands/list/list-defaultvalue-list.ts b/src/m365/spo/commands/list/list-defaultvalue-list.ts index 9a17e695c11..9149b0ee71d 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-list.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-list.ts @@ -1,7 +1,6 @@ import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { DOMParser } from '@xmldom/xmldom'; @@ -16,23 +15,18 @@ interface DefaultColumnValue { folderUrl: string } -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - listId: zod.alias('i', z.string().optional() - .refine(id => id === undefined || validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })) - ), - listTitle: zod.alias('t', z.string().optional()), - listUrl: z.string().optional(), - folderUrl: z.string().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + listId: z.uuid().optional().alias('i'), + listTitle: z.string().optional().alias('t'), + listUrl: z.string().optional(), + folderUrl: z.string().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -48,14 +42,14 @@ class SpoListDefaultValueListCommand extends SpoCommand { return 'Retrieves default column values for a specific document library'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.listId, options.listTitle, options.listUrl].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: listId, listTitle, listUrl.' + error: 'Use one of the following options: listId, listTitle, listUrl.' }); } diff --git a/src/m365/spo/commands/list/list-defaultvalue-remove.spec.ts b/src/m365/spo/commands/list/list-defaultvalue-remove.spec.ts index 7d48d382cf3..d9a106a5787 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-remove.spec.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-remove.spec.ts @@ -1,19 +1,18 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './list-defaultvalue-remove.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; -import { formatting } from '../../../../utils/formatting.js'; -import { CommandError } from '../../../../Command.js'; +import command, { options } from './list-defaultvalue-remove.js'; describe(commands.LIST_DEFAULTVALUE_REMOVE, () => { const siteUrl = 'https://contoso.sharepoint.com/sites/Marketing'; @@ -31,7 +30,7 @@ describe(commands.LIST_DEFAULTVALUE_REMOVE, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let confirmationPromptStub: sinon.SinonStub; before(() => { @@ -41,7 +40,7 @@ describe(commands.LIST_DEFAULTVALUE_REMOVE, () => { sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/spo/commands/list/list-defaultvalue-remove.ts b/src/m365/spo/commands/list/list-defaultvalue-remove.ts index a2ed7c57ba2..77a32a0360f 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-remove.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-remove.ts @@ -1,7 +1,6 @@ import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { DOMParser } from '@xmldom/xmldom'; @@ -11,25 +10,20 @@ import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; import { cli } from '../../../../cli/cli.js'; -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - listId: zod.alias('i', z.string().optional() - .refine(id => id === undefined || validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })) - ), - listTitle: zod.alias('t', z.string().optional()), - listUrl: z.string().optional(), - fieldName: z.string(), - folderUrl: z.string().optional(), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + listId: z.uuid().optional().alias('i'), + listTitle: z.string().optional().alias('t'), + listUrl: z.string().optional(), + fieldName: z.string(), + folderUrl: z.string().optional(), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -51,14 +45,14 @@ class SpoListDefaultValueRemoveCommand extends SpoCommand { return 'Removes a specific default column value for a specific document library'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.listId, options.listTitle, options.listUrl].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: listId, listTitle, listUrl.' + error: 'Use one of the following options: listId, listTitle, listUrl.' }); } diff --git a/src/m365/spo/commands/list/list-defaultvalue-set.spec.ts b/src/m365/spo/commands/list/list-defaultvalue-set.spec.ts index 757d5f27f0d..4de31468fbf 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-set.spec.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-set.spec.ts @@ -1,20 +1,19 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import commands from '../../commands.js'; -import command from './list-defaultvalue-set.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; -import { formatting } from '../../../../utils/formatting.js'; -import { CommandError } from '../../../../Command.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; +import commands from '../../commands.js'; +import command, { options } from './list-defaultvalue-set.js'; describe(commands.LIST_DEFAULTVALUE_SET, () => { const siteUrl = 'https://contoso.sharepoint.com/sites/Marketing'; @@ -31,7 +30,7 @@ describe(commands.LIST_DEFAULTVALUE_SET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -40,7 +39,7 @@ describe(commands.LIST_DEFAULTVALUE_SET, () => { sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/spo/commands/list/list-defaultvalue-set.ts b/src/m365/spo/commands/list/list-defaultvalue-set.ts index 3c06ca3962b..f4ac8997e25 100644 --- a/src/m365/spo/commands/list/list-defaultvalue-set.ts +++ b/src/m365/spo/commands/list/list-defaultvalue-set.ts @@ -1,7 +1,6 @@ import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { DOMParser } from '@xmldom/xmldom'; @@ -10,27 +9,22 @@ import { urlUtil } from '../../../../utils/urlUtil.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - listId: zod.alias('i', z.string().optional() - .refine(id => id === undefined || validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })) - ), - listTitle: zod.alias('t', z.string().optional()), - listUrl: z.string().optional(), - fieldName: z.string(), - fieldValue: z.string() - .refine(value => value !== '', `The value cannot be empty. Use 'spo list defaultvalue remove' to remove a default column value.`), - folderUrl: z.string().optional() - .refine(url => url === undefined || (!url.includes('#') && !url.includes('%')), 'Due to limitations in SharePoint Online, setting default column values for folders with a # or % character in their path is not supported.') - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + listId: z.uuid().optional().alias('i'), + listTitle: z.string().optional().alias('t'), + listUrl: z.string().optional(), + fieldName: z.string(), + fieldValue: z.string() + .refine(value => value !== '', `The value cannot be empty. Use 'spo list defaultvalue remove' to remove a default column value.`), + folderUrl: z.string().optional() + .refine(url => url === undefined || (!url.includes('#') && !url.includes('%')), 'Due to limitations in SharePoint Online, setting default column values for folders with a # or % character in their path is not supported.') +}); declare type Options = z.infer; @@ -47,14 +41,14 @@ class SpoListDefaultValueSetCommand extends SpoCommand { return 'Sets default column values for a specific document library'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.listId, options.listTitle, options.listUrl].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: listId, listTitle, listUrl.' + error: 'Use one of the following options: listId, listTitle, listUrl.' }); } diff --git a/src/m365/spo/commands/list/list-view-add.spec.ts b/src/m365/spo/commands/list/list-view-add.spec.ts index 213ced78bc6..888477b113e 100644 --- a/src/m365/spo/commands/list/list-view-add.spec.ts +++ b/src/m365/spo/commands/list/list-view-add.spec.ts @@ -12,9 +12,8 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; -import { z } from 'zod'; import commands from '../../commands.js'; -import command from './list-view-add.js'; +import command, { options } from './list-view-add.js'; describe(commands.LIST_VIEW_ADD, () => { @@ -48,7 +47,7 @@ describe(commands.LIST_VIEW_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -57,7 +56,7 @@ describe(commands.LIST_VIEW_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/list/list-view-add.ts b/src/m365/spo/commands/list/list-view-add.ts index f8562e85b5c..d50f114b81b 100644 --- a/src/m365/spo/commands/list/list-view-add.ts +++ b/src/m365/spo/commands/list/list-view-add.ts @@ -7,49 +7,45 @@ import { validation } from '../../../../utils/validation.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; - -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, { - message: 'webUrl is not a valid SharePoint site URL.' - })), - listId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - listTitle: z.string().optional(), - listUrl: z.string().optional(), - title: z.string().min(1, 'Cannot be empty.'), - fields: z.string().optional(), - viewQuery: z.string().optional(), - personal: z.boolean().optional(), - default: z.boolean().optional(), - paged: z.boolean().optional(), - rowLimit: z.number().int().positive().optional(), - customFormatter: z.string() - .refine(formatter => { - try { - JSON.parse(formatter); - return true; - } - catch { - return false; - } - }, { - message: 'Custom formatter must be a valid JSON string.' - }) - .optional(), - type: z.enum(['list', 'calendar', 'gallery', 'kanban']).optional(), - calendarStartDateField: z.string().min(1, 'Cannot be empty.').optional(), - calendarEndDateField: z.string().min(1, 'Cannot be empty.').optional(), - calendarTitleField: z.string().min(1, 'Cannot be empty.').optional(), - calendarSubTitleField: z.string().min(1, 'Cannot be empty.').optional(), - calendarDefaultLayout: z.enum(['month', 'week', 'workWeek', 'day']).optional(), - kanbanBucketField: z.string().min(1, 'Cannot be empty.').optional() - }) - .strict(); + +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: 'webUrl is not a valid SharePoint site URL.' + }) + .alias('u'), + listId: z.uuid().optional(), + listTitle: z.string().optional(), + listUrl: z.string().optional(), + title: z.string().min(1, 'Cannot be empty.'), + fields: z.string().optional(), + viewQuery: z.string().optional(), + personal: z.boolean().optional(), + default: z.boolean().optional(), + paged: z.boolean().optional(), + rowLimit: z.number().int().positive().optional(), + customFormatter: z.string() + .refine(formatter => { + try { + JSON.parse(formatter); + return true; + } + catch { + return false; + } + }, { + error: 'Custom formatter must be a valid JSON string.' + }) + .optional(), + type: z.enum(['list', 'calendar', 'gallery', 'kanban']).optional(), + calendarStartDateField: z.string().min(1, 'Cannot be empty.').optional(), + calendarEndDateField: z.string().min(1, 'Cannot be empty.').optional(), + calendarTitleField: z.string().min(1, 'Cannot be empty.').optional(), + calendarSubTitleField: z.string().min(1, 'Cannot be empty.').optional(), + calendarDefaultLayout: z.enum(['month', 'week', 'workWeek', 'day']).optional(), + kanbanBucketField: z.string().min(1, 'Cannot be empty.').optional() +}); declare type Options = z.infer; @@ -66,32 +62,32 @@ class SpoListViewAddCommand extends SpoCommand { return 'Adds a new view to a SharePoint list'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine((options: Options) => [options.listId, options.listTitle, options.listUrl].filter(o => o !== undefined).length === 1, { - message: 'Use one of the following options: listId, listTitle, or listUrl.' + error: 'Use one of the following options: listId, listTitle, or listUrl.' }) .refine((options: Options) => !options.personal || !options.default, { - message: 'Default view cannot be a personal view.' + error: 'Default view cannot be a personal view.' }) .refine((options: Options) => options.type !== 'calendar' || [options.calendarStartDateField, options.calendarEndDateField, options.calendarTitleField].filter(o => o === undefined).length === 0, { - message: 'When type is calendar, do specify calendarStartDateField, calendarEndDateField, and calendarTitleField.' + error: 'When type is calendar, do specify calendarStartDateField, calendarEndDateField, and calendarTitleField.' }) .refine((options: Options) => options.type === 'calendar' || [options.calendarStartDateField, options.calendarEndDateField, options.calendarTitleField].filter(o => o === undefined).length === 3, { - message: 'When type is not calendar, do not specify calendarStartDateField, calendarEndDateField, and calendarTitleField.' + error: 'When type is not calendar, do not specify calendarStartDateField, calendarEndDateField, and calendarTitleField.' }) .refine((options: Options) => options.type !== 'kanban' || options.kanbanBucketField !== undefined, { - message: 'When type is kanban, do specify kanbanBucketField.' + error: 'When type is kanban, do specify kanbanBucketField.' }) .refine((options: Options) => options.type === 'kanban' || options.kanbanBucketField === undefined, { - message: 'When type is not kanban, do not specify kanbanBucketField.' + error: 'When type is not kanban, do not specify kanbanBucketField.' }) .refine((options: Options) => options.type === 'calendar' || options.fields !== undefined, { - message: 'When type is not calendar, do specify fields.' + error: 'When type is not calendar, do specify fields.' }); } diff --git a/src/m365/spo/commands/page/page-control-remove.spec.ts b/src/m365/spo/commands/page/page-control-remove.spec.ts index 82e337390fc..68814b20149 100644 --- a/src/m365/spo/commands/page/page-control-remove.spec.ts +++ b/src/m365/spo/commands/page/page-control-remove.spec.ts @@ -1,20 +1,19 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from "../../../../cli/CommandInfo.js"; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { cli } from '../../../../cli/cli.js'; import commands from '../../commands.js'; -import command from './page-control-remove.js'; -import { z } from 'zod'; -import { CommandError } from '../../../../Command.js'; +import command, { options } from './page-control-remove.js'; import { Page } from './Page.js'; -import { formatting } from '../../../../utils/formatting.js'; describe(commands.PAGE_CONTROL_REMOVE, () => { const spRootUrl = 'https://contoso.sharepoint.com'; @@ -32,7 +31,7 @@ describe(commands.PAGE_CONTROL_REMOVE, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let loggerLogSpy: sinon.SinonSpy; let confirmationPromptStub: sinon.SinonStub; let pagePublishStub: sinon.SinonStub; @@ -46,7 +45,7 @@ describe(commands.PAGE_CONTROL_REMOVE, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/page/page-control-remove.ts b/src/m365/spo/commands/page/page-control-remove.ts index a3b2de82bf5..f53f8696e9a 100644 --- a/src/m365/spo/commands/page/page-control-remove.ts +++ b/src/m365/spo/commands/page/page-control-remove.ts @@ -4,7 +4,6 @@ import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { validation } from '../../../../utils/validation.js'; import SpoCommand from '../../../base/SpoCommand.js'; -import { zod } from '../../../../utils/zod.js'; import { cli } from '../../../../cli/cli.js'; import request, { CliRequestOptions } from '../../../../request.js'; import { formatting } from '../../../../utils/formatting.js'; @@ -14,21 +13,18 @@ import { PageControl } from './PageControl.js'; import { ClientSideControl } from './ClientSideControl.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string()) - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint URL.` - })), - pageName: zod.alias('n', z.string()), - id: zod.alias('i', z.string()) - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })), - draft: z.boolean().optional(), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint URL.` + }) + .alias('u'), + pageName: z.string().alias('n'), + id: z.uuid().alias('i'), + draft: z.boolean().optional(), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -45,7 +41,7 @@ class SpoPageControlRemoveCommand extends SpoCommand { return 'Removes a control from a modern page'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spo/commands/page/page-get.spec.ts b/src/m365/spo/commands/page/page-get.spec.ts index fd15e9e5271..7761067e334 100644 --- a/src/m365/spo/commands/page/page-get.spec.ts +++ b/src/m365/spo/commands/page/page-get.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,7 +11,7 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './page-get.js'; +import command, { options } from './page-get.js'; import { classicPage, controlsMock, pageListItemMock, sectionMock } from './page-get.mock.js'; describe(commands.PAGE_GET, () => { @@ -20,7 +19,7 @@ describe(commands.PAGE_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -29,7 +28,7 @@ describe(commands.PAGE_GET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/page/page-get.ts b/src/m365/spo/commands/page/page-get.ts index 363b0bc2f41..018c46cb6d3 100644 --- a/src/m365/spo/commands/page/page-get.ts +++ b/src/m365/spo/commands/page/page-get.ts @@ -1,5 +1,4 @@ import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -9,18 +8,17 @@ import { validation } from '../../../../utils/validation.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - name: zod.alias('n', z.string()).optional(), - default: z.boolean().optional(), - metadataOnly: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + name: z.string().optional().alias('n'), + default: z.boolean().optional(), + metadataOnly: z.boolean().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -36,14 +34,14 @@ class SpoPageGetCommand extends SpoCommand { return 'Gets information about the specific modern page'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.name, options.default].filter(x => x !== undefined).length === 1, { - message: `Specify either name or default, but not both.` + error: `Specify either name or default, but not both.` }); } diff --git a/src/m365/spo/commands/page/page-publish.spec.ts b/src/m365/spo/commands/page/page-publish.spec.ts index 72356df0829..a7880bc563d 100644 --- a/src/m365/spo/commands/page/page-publish.spec.ts +++ b/src/m365/spo/commands/page/page-publish.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -8,13 +7,13 @@ import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { formatting } from '../../../../utils/formatting.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import commands from '../../commands.js'; -import command from './page-publish.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; -import { formatting } from '../../../../utils/formatting.js'; +import commands from '../../commands.js'; +import command, { options } from './page-publish.js'; describe(commands.PAGE_PUBLISH, () => { let log: string[]; @@ -22,7 +21,7 @@ describe(commands.PAGE_PUBLISH, () => { let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; let postStub: sinon.SinonStub; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const webUrl = 'https://contoso.sharepoint.com/sites/Marketing'; const serverRelativeUrl = urlUtil.getServerRelativeSiteUrl(webUrl); @@ -35,7 +34,7 @@ describe(commands.PAGE_PUBLISH, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/page/page-publish.ts b/src/m365/spo/commands/page/page-publish.ts index 1ee85a471eb..98a86ba9d3f 100644 --- a/src/m365/spo/commands/page/page-publish.ts +++ b/src/m365/spo/commands/page/page-publish.ts @@ -1,5 +1,4 @@ import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; @@ -8,16 +7,15 @@ import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; import { Page } from './Page.js'; -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - name: zod.alias('n', z.string()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + name: z.string().alias('n') +}); declare type Options = z.infer; interface CommandArgs { @@ -33,7 +31,7 @@ class SpoPagePublishCommand extends SpoCommand { return 'Publishes a modern page'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spo/commands/page/page-section-remove.spec.ts b/src/m365/spo/commands/page/page-section-remove.spec.ts index 78aed0686b1..5cd58c0b694 100644 --- a/src/m365/spo/commands/page/page-section-remove.spec.ts +++ b/src/m365/spo/commands/page/page-section-remove.spec.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; -import { z } from 'zod'; import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -11,16 +10,16 @@ import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import commands from '../../commands.js'; -import command from './page-section-remove.js'; import { spo } from '../../../../utils/spo.js'; +import commands from '../../commands.js'; +import command, { options } from './page-section-remove.js'; import { mockBackgroundControlHTML, mockEmptyPage, mockOneColumnSectionHTML, mockPageSettingsHTML, mockThreeColumnSectionHTML, mockTwoColumnLeftSectionHTML, mockTwoColumnRightSectionHTML, mockTwoColumnsSectionHTML, mockVerticalSectionHTML } from './page.mock.js'; describe(commands.PAGE_SECTION_REMOVE, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const apiResponse = { "ListItemAllFields": { @@ -95,7 +94,7 @@ describe(commands.PAGE_SECTION_REMOVE, () => { }); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.schema!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/page/page-section-remove.ts b/src/m365/spo/commands/page/page-section-remove.ts index 5586d43d463..067a2b17a42 100644 --- a/src/m365/spo/commands/page/page-section-remove.ts +++ b/src/m365/spo/commands/page/page-section-remove.ts @@ -9,20 +9,18 @@ import commands from '../../commands.js'; import { Page } from './Page.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; - -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - pageName: zod.alias('n', z.string()), - section: zod.alias('s', z.number()), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); + +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + pageName: z.string().alias('n'), + section: z.number().alias('s'), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; @@ -39,7 +37,7 @@ class SpoPageSectionRemoveCommand extends SpoCommand { return 'Removes the specified section from the modern page'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spo/commands/site/site-add.spec.ts b/src/m365/spo/commands/site/site-add.spec.ts index 1170d54fbaf..e148c78d0c2 100644 --- a/src/m365/spo/commands/site/site-add.spec.ts +++ b/src/m365/spo/commands/site/site-add.spec.ts @@ -15,8 +15,8 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import { spo } from '../../../../utils/spo.js'; import commands from '../../commands.js'; - import command from './site-add.js'; + describe(commands.SITE_ADD, () => { let log: string[]; let logger: Logger; diff --git a/src/m365/spo/commands/site/site-appcatalog-list.ts b/src/m365/spo/commands/site/site-appcatalog-list.ts index 5d051e5d150..46d751887c6 100644 --- a/src/m365/spo/commands/site/site-appcatalog-list.ts +++ b/src/m365/spo/commands/site/site-appcatalog-list.ts @@ -1,17 +1,15 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import { odata } from '../../../../utils/odata.js'; import { spo } from '../../../../utils/spo.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - excludeDeletedSites: zod.alias('excludeDeletedSites', z.boolean().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + excludeDeletedSites: z.boolean().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -27,7 +25,7 @@ class SpoSiteAppCatalogListCommand extends SpoCommand { return 'List all site collection app catalogs within the tenant'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/spo/commands/site/site-get.spec.ts b/src/m365/spo/commands/site/site-get.spec.ts index 5d5de3411a6..2c94bb16eae 100644 --- a/src/m365/spo/commands/site/site-get.spec.ts +++ b/src/m365/spo/commands/site/site-get.spec.ts @@ -1,6 +1,5 @@ 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'; @@ -12,14 +11,14 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './site-get.js'; +import command, { options } from './site-get.js'; describe(commands.SITE_GET, () => { let log: any[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -28,7 +27,7 @@ describe(commands.SITE_GET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spo/commands/site/site-get.ts b/src/m365/spo/commands/site/site-get.ts index cfea71cc08b..121e3e02cd7 100644 --- a/src/m365/spo/commands/site/site-get.ts +++ b/src/m365/spo/commands/site/site-get.ts @@ -3,17 +3,15 @@ import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; import request from '../../../../request.js'; import { validation } from '../../../../utils/validation.js'; -import { zod } from '../../../../utils/zod.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - url: zod.alias('u', z.string().refine(url => validation.isValidSharePointUrl(url) === true, { - message: 'Specify a valid SharePoint site URL' - })) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + url: z.string().refine(url => validation.isValidSharePointUrl(url) === true, { + error: 'Specify a valid SharePoint site URL' + }).alias('u') +}); declare type Options = z.infer; interface CommandArgs { @@ -29,7 +27,7 @@ class SpoSiteGetCommand extends SpoCommand { return 'Gets information about the specific site collection'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/spo/commands/site/site-sharingpermission-set.spec.ts b/src/m365/spo/commands/site/site-sharingpermission-set.spec.ts index 29dc4583873..531432aba4b 100644 --- a/src/m365/spo/commands/site/site-sharingpermission-set.spec.ts +++ b/src/m365/spo/commands/site/site-sharingpermission-set.spec.ts @@ -2,17 +2,16 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './site-sharingpermission-set.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; +import command, { options } from './site-sharingpermission-set.js'; describe(commands.SITE_SHARINGPERMISSION_SET, () => { const siteUrl = 'https://contoso.sharepoint.com/sites/marketing'; @@ -22,7 +21,7 @@ describe(commands.SITE_SHARINGPERMISSION_SET, () => { let loggerLogSpy: sinon.SinonSpy; let patchStub: sinon.SinonStub; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -31,7 +30,7 @@ describe(commands.SITE_SHARINGPERMISSION_SET, () => { sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/spo/commands/site/site-sharingpermission-set.ts b/src/m365/spo/commands/site/site-sharingpermission-set.ts index 9b33959fbaa..894289577cb 100644 --- a/src/m365/spo/commands/site/site-sharingpermission-set.ts +++ b/src/m365/spo/commands/site/site-sharingpermission-set.ts @@ -1,22 +1,19 @@ import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import commands from '../../commands.js'; import { validation } from '../../../../utils/validation.js'; import request, { CliRequestOptions } from '../../../../request.js'; -const options = globalOptionsZod - .extend({ - siteUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - capability: z.enum(['full', 'limited', 'ownersOnly']) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + siteUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }).alias('u'), + capability: z.enum(['full', 'limited', 'ownersOnly']) +}); declare type Options = z.infer; interface CommandArgs { @@ -32,7 +29,7 @@ class SpoSiteSharingPermissionSetCommand extends SpoCommand { return 'Controls how a site and its components can be shared'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } diff --git a/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts b/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts index 972c7d608a1..a5ab781fc1e 100644 --- a/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts +++ b/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts @@ -10,16 +10,15 @@ import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { z } from 'zod'; import commands from '../../commands.js'; -import command from './site-versionpolicy-get.js'; +import command, { options } from './site-versionpolicy-get.js'; describe(commands.SITE_VERSIONPOLICY_GET, () => { let log: any[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const validSiteUrl = "https://contoso.sharepoint.com"; before(() => { @@ -28,7 +27,7 @@ describe(commands.SITE_VERSIONPOLICY_GET, () => { sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; auth.connection.active = true; }); diff --git a/src/m365/spo/commands/site/site-versionpolicy-get.ts b/src/m365/spo/commands/site/site-versionpolicy-get.ts index 877eb808c67..62e976eea87 100644 --- a/src/m365/spo/commands/site/site-versionpolicy-get.ts +++ b/src/m365/spo/commands/site/site-versionpolicy-get.ts @@ -1,21 +1,18 @@ -import commands from '../../commands.js'; +import { z } from 'zod'; import { Logger } from '../../../../cli/Logger.js'; -import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; -import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; -import { validation } from '../../../../utils/validation.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { validation } from '../../../../utils/validation.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import commands from '../../commands.js'; -export const options = globalOptionsZod - .extend({ - siteUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + siteUrl: z.string().alias('u') + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) +}); declare type Options = z.infer; diff --git a/src/m365/spo/commands/web/web-alert-list.ts b/src/m365/spo/commands/web/web-alert-list.ts index 053e48dc134..e5a98ae2168 100644 --- a/src/m365/spo/commands/web/web-alert-list.ts +++ b/src/m365/spo/commands/web/web-alert-list.ts @@ -1,36 +1,29 @@ -import commands from '../../commands.js'; +import { z } from 'zod'; +import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; -import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; -import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; -import { validation } from '../../../../utils/validation.js'; -import { spo } from '../../../../utils/spo.js'; import { entraUser } from '../../../../utils/entraUser.js'; import { formatting } from '../../../../utils/formatting.js'; import { odata } from '../../../../utils/odata.js'; -import { cli } from '../../../../cli/cli.js'; +import { spo } from '../../../../utils/spo.js'; +import { validation } from '../../../../utils/validation.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import commands from '../../commands.js'; -export const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint site URL.` - }))), - listId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - listUrl: z.string().optional(), - listTitle: z.string().optional(), - userName: z.string().refine(upn => validation.isValidUserPrincipalName(upn), upn => ({ - message: `'${upn}' is not a valid UPN.` - })).optional(), - userId: z.string().refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string().alias('u') + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint site URL.` + }), + listId: z.uuid().optional(), + listUrl: z.string().optional(), + listTitle: z.string().optional(), + userName: z.string().optional().refine(upn => typeof upn === 'undefined' || validation.isValidUserPrincipalName(upn as string), { + error: e => `'${e.input}' is not a valid UPN.` + }), + userId: z.uuid().optional() +}); declare type Options = z.infer; @@ -51,17 +44,17 @@ class SpoWebAlertListCommand extends SpoCommand { return ['ID', 'Title', 'UserPrincipalName']; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.listId, options.listUrl, options.listTitle].filter(x => x !== undefined).length <= 1, { - message: `Specify either listId, listUrl, or listTitle, but not more than one.` + error: `Specify either listId, listUrl, or listTitle, but not more than one.` }) .refine(options => [options.userName, options.userId].filter(x => x !== undefined).length <= 1, { - message: `Specify either userName or userId, but not both.` + error: `Specify either userName or userId, but not both.` }); } diff --git a/src/m365/spo/commands/web/web-alert-remove.ts b/src/m365/spo/commands/web/web-alert-remove.ts index 96befe54ffe..f713cb91015 100644 --- a/src/m365/spo/commands/web/web-alert-remove.ts +++ b/src/m365/spo/commands/web/web-alert-remove.ts @@ -1,27 +1,22 @@ -import commands from '../../commands.js'; +import { z } from 'zod'; +import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; -import SpoCommand from '../../../base/SpoCommand.js'; import { globalOptionsZod } from '../../../../Command.js'; -import { z } from 'zod'; -import { zod } from '../../../../utils/zod.js'; -import { validation } from '../../../../utils/validation.js'; -import { formatting } from '../../../../utils/formatting.js'; import request, { CliRequestOptions } from '../../../../request.js'; -import { cli } from '../../../../cli/cli.js'; +import { formatting } from '../../../../utils/formatting.js'; +import { validation } from '../../../../utils/validation.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import commands from '../../commands.js'; -const options = globalOptionsZod - .extend({ - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint URL.` - }))), - id: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })), - force: zod.alias('f', z.boolean().optional()) - }) - .strict(); +const options = z.strictObject({ + ...globalOptionsZod.shape, + webUrl: z.string().alias('u') + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint URL.` + }), + id: z.uuid(), + force: z.boolean().optional().alias('f') +}); declare type Options = z.infer; diff --git a/src/m365/spp/commands/model/model-apply.spec.ts b/src/m365/spp/commands/model/model-apply.spec.ts index ecc3d3ad544..3cdd71e6bfe 100644 --- a/src/m365/spp/commands/model/model-apply.spec.ts +++ b/src/m365/spp/commands/model/model-apply.spec.ts @@ -4,23 +4,22 @@ import auth from '../../../../Auth.js'; import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.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 commands from '../../commands.js'; -import command from './model-apply.js'; import { spp } from '../../../../utils/spp.js'; -import { CommandError } from '../../../../Command.js'; -import { z } from 'zod'; +import commands from '../../commands.js'; +import command, { options } from './model-apply.js'; describe(commands.MODEL_APPLY, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; const publicationsResult = { Details: [ @@ -51,7 +50,7 @@ describe(commands.MODEL_APPLY, () => { sinon.stub(spp, 'assertSiteIsContentCenter').resolves(); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/spp/commands/model/model-apply.ts b/src/m365/spp/commands/model/model-apply.ts index 06459e75c9a..f490cc29903 100644 --- a/src/m365/spp/commands/model/model-apply.ts +++ b/src/m365/spp/commands/model/model-apply.ts @@ -9,34 +9,26 @@ import { ListInstance } from '../../../spo/commands/list/ListInstance.js'; import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; - -const options = globalOptionsZod - .extend({ - contentCenterUrl: zod.alias('c', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - webUrl: zod.alias('u', z.string() - .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `'${url}' is not a valid SharePoint Online site URL.` - })) - ), - id: zod.alias('i', z.string() - .refine(id => validation.isValidGuid(id) === true, id => ({ - message: `${id} is not a valid GUID.` - })).optional()), - title: zod.alias('t', z.string()).optional(), - listTitle: z.string().optional(), - listId: z.string() - .refine(listId => validation.isValidGuid(listId) === true, listId => ({ - message: `${listId} is not a valid GUID.` - })).optional(), - listUrl: z.string().optional(), - viewOption: z.enum(['NewViewAsDefault', 'DoNotChangeDefault', 'TileViewAsDefault']).optional() - }) - .strict(); + +export const options = z.strictObject({ + ...globalOptionsZod.shape, + contentCenterUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('c'), + webUrl: z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, { + error: e => `'${e.input}' is not a valid SharePoint Online site URL.` + }) + .alias('u'), + id: z.uuid().optional().alias('i'), + title: z.string().optional().alias('t'), + listTitle: z.string().optional(), + listId: z.uuid().optional(), + listUrl: z.string().optional(), + viewOption: z.enum(['NewViewAsDefault', 'DoNotChangeDefault', 'TileViewAsDefault']).optional() +}); declare type Options = z.infer; @@ -53,17 +45,17 @@ class SppModelApplyCommand extends SpoCommand { return 'Applies (or syncs) a trained document understanding model to a document library'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.id, options.title].filter(x => x !== undefined).length === 1, { - message: `Specify exactly one of the following options: 'id' or 'title'.` + error: `Specify exactly one of the following options: 'id' or 'title'.` }) .refine(options => [options.listTitle, options.listId, options.listUrl].filter(x => x !== undefined).length === 1, { - message: `Specify exactly one of the following options: 'listTitle', 'listId' or 'listUrl'.` + error: `Specify exactly one of the following options: 'listTitle', 'listId' or 'listUrl'.` }); } diff --git a/src/m365/teams/commands/callrecord/callrecord-list.spec.ts b/src/m365/teams/commands/callrecord/callrecord-list.spec.ts index 5d2c3ea5225..edcb8fa182b 100644 --- a/src/m365/teams/commands/callrecord/callrecord-list.spec.ts +++ b/src/m365/teams/commands/callrecord/callrecord-list.spec.ts @@ -4,17 +4,16 @@ import auth from '../../../../Auth.js'; import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; import { telemetry } from '../../../../telemetry.js'; +import { accessToken } from '../../../../utils/accessToken.js'; +import { entraUser } from '../../../../utils/entraUser.js'; +import { odata } from '../../../../utils/odata.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import { z } from 'zod'; import commands from '../../commands.js'; -import command from './callrecord-list.js'; -import { odata } from '../../../../utils/odata.js'; -import { accessToken } from '../../../../utils/accessToken.js'; -import { entraUser } from '../../../../utils/entraUser.js'; -import { CommandError } from '../../../../Command.js'; +import command, { options } from './callrecord-list.js'; describe(commands.CALLRECORD_LIST, () => { const validStartDateTime = new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(); @@ -201,7 +200,7 @@ describe(commands.CALLRECORD_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; let assertAccessTokenTypeStub: sinon.SinonStub; before(() => { @@ -212,7 +211,7 @@ describe(commands.CALLRECORD_LIST, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/teams/commands/callrecord/callrecord-list.ts b/src/m365/teams/commands/callrecord/callrecord-list.ts index bda2ad6ceff..91b970726f4 100644 --- a/src/m365/teams/commands/callrecord/callrecord-list.ts +++ b/src/m365/teams/commands/callrecord/callrecord-list.ts @@ -7,46 +7,42 @@ import { validation } from '../../../../utils/validation.js'; import { entraUser } from '../../../../utils/entraUser.js'; import { odata } from '../../../../utils/odata.js'; -const options = globalOptionsZod - .extend({ - userId: z.string() - .refine((val) => validation.isValidGuid(val), { - message: 'Invalid GUID.' - }).optional(), - userName: z.string() - .refine((val) => validation.isValidUserPrincipalName(val), { - message: 'Invalid user principal name.' - }).optional(), - startDateTime: z.string() - .refine((val) => { - if (!validation.isValidISODateTime(val)) { - return false; - } - const date = new Date(val); - const maxDate = new Date(); - const minDate = new Date(); - minDate.setDate(maxDate.getDate() - 30); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + userId: z.uuid().optional(), + userName: z.string() + .refine((val) => validation.isValidUserPrincipalName(val), { + message: 'Invalid user principal name.' + }).optional(), + startDateTime: z.string() + .refine((val) => { + if (!validation.isValidISODateTime(val)) { + return false; + } + const date = new Date(val); + const maxDate = new Date(); + const minDate = new Date(); + minDate.setDate(maxDate.getDate() - 30); - return date >= minDate && date <= maxDate; - }, { - message: 'Date must be a valid ISO date within the last 30 days and not in the future.' - }).optional(), - endDateTime: z.string() - .refine((val) => { - if (!validation.isValidISODateTime(val)) { - return false; - } - const date = new Date(val); - const maxDate = new Date(); - const minDate = new Date(); - minDate.setDate(maxDate.getDate() - 30); + return date >= minDate && date <= maxDate; + }, { + message: 'Date must be a valid ISO date within the last 30 days and not in the future.' + }).optional(), + endDateTime: z.string() + .refine((val) => { + if (!validation.isValidISODateTime(val)) { + return false; + } + const date = new Date(val); + const maxDate = new Date(); + const minDate = new Date(); + minDate.setDate(maxDate.getDate() - 30); - return date >= minDate && date <= maxDate; - }, { - message: 'Date must be a valid ISO date within the last 30 days and not in the future.' - }).optional() - }) - .strict(); + return date >= minDate && date <= maxDate; + }, { + message: 'Date must be a valid ISO date within the last 30 days and not in the future.' + }).optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -66,14 +62,14 @@ class TeamsCallRecordListCommand extends GraphApplicationCommand { return ['id', 'type', 'startDateTime', 'endDateTime']; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine((options: Options) => [options.userId, options.userName].filter(o => o !== undefined).length <= 1, { - message: 'Use one of the following options: userId or userName but not both.' + error: 'Use one of the following options: userId or userName but not both.' }) .refine((options: Options) => [options.startDateTime, options.endDateTime].filter(o => o !== undefined).length <= 1 || new Date(options.startDateTime!) < new Date(options.endDateTime!), { message: 'Value of startDateTime, must be before endDateTime.' diff --git a/src/m365/tenant/commands/people/people-pronouns-set.spec.ts b/src/m365/tenant/commands/people/people-pronouns-set.spec.ts index edcc09f11b1..94f10277577 100644 --- a/src/m365/tenant/commands/people/people-pronouns-set.spec.ts +++ b/src/m365/tenant/commands/people/people-pronouns-set.spec.ts @@ -1,6 +1,8 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; @@ -9,17 +11,14 @@ import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './people-pronouns-set.js'; -import { z } from 'zod'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { cli } from '../../../../cli/cli.js'; +import command, { options } from './people-pronouns-set.js'; describe(commands.PEOPLE_PRONOUNS_SET, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -28,7 +27,7 @@ describe(commands.PEOPLE_PRONOUNS_SET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -84,7 +83,7 @@ describe(commands.PEOPLE_PRONOUNS_SET, () => { const parsedSchema = commandOptionsSchema.safeParse({ verbose: true, enabled: true }); await command.action(logger, { - options: parsedSchema.data + options: parsedSchema.data! as any }); assert( diff --git a/src/m365/tenant/commands/people/people-pronouns-set.ts b/src/m365/tenant/commands/people/people-pronouns-set.ts index deae2711165..a51a8f9d3cd 100644 --- a/src/m365/tenant/commands/people/people-pronouns-set.ts +++ b/src/m365/tenant/commands/people/people-pronouns-set.ts @@ -2,17 +2,15 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; import { Logger } from '../../../../cli/Logger.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({ - enabled: zod.alias('e', z.boolean()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + enabled: z.boolean().alias('e') +}); -declare type Options = z.infer; +export declare type Options = z.infer; interface CommandArgs { options: Options; @@ -27,7 +25,7 @@ class TenantPeoplePronounsSetCommand extends GraphCommand { return 'Manage pronouns settings for an organization'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/tenant/commands/report/report-settings-set.spec.ts b/src/m365/tenant/commands/report/report-settings-set.spec.ts index 16f6aeacf06..12a0a8013c2 100644 --- a/src/m365/tenant/commands/report/report-settings-set.spec.ts +++ b/src/m365/tenant/commands/report/report-settings-set.spec.ts @@ -3,22 +3,21 @@ import sinon from 'sinon'; import auth from '../../../../Auth.js'; import { cli } from '../../../../cli/cli.js'; import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import request from '../../../../request.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 commands from '../../commands.js'; -import command from './report-settings-set.js'; -import { z } from 'zod'; +import command, { options } from './report-settings-set.js'; describe(commands.REPORT_SETTINGS_SET, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -27,7 +26,7 @@ describe(commands.REPORT_SETTINGS_SET, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { @@ -72,7 +71,7 @@ describe(commands.REPORT_SETTINGS_SET, () => { assert.strictEqual(result.success, false); if (!result.success) { - assert.strictEqual(result.error.issues[0].message, "Expected boolean, received string"); + assert.strictEqual(result.error.issues[0].message, "Invalid input: expected boolean, received string"); } }); diff --git a/src/m365/tenant/commands/report/report-settings-set.ts b/src/m365/tenant/commands/report/report-settings-set.ts index 591909ed6f7..90ce52b67f8 100644 --- a/src/m365/tenant/commands/report/report-settings-set.ts +++ b/src/m365/tenant/commands/report/report-settings-set.ts @@ -4,13 +4,11 @@ import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; -const options = globalOptionsZod - .extend({ - displayConcealedNames: zod.alias('d', z.boolean()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + displayConcealedNames: z.boolean().alias('d') +}); declare type Options = z.infer; @@ -27,7 +25,7 @@ class TenantReportSettingsSetCommand extends GraphCommand { return 'Update tenant-level settings for Microsoft 365 reports'; } - public get schema(): z.ZodTypeAny | undefined { + public get schema(): z.ZodType | undefined { return options; } diff --git a/src/m365/viva/commands/engage/engage-community-user-add.spec.ts b/src/m365/viva/commands/engage/engage-community-user-add.spec.ts index 183c6298629..7faefbdc9d8 100644 --- a/src/m365/viva/commands/engage/engage-community-user-add.spec.ts +++ b/src/m365/viva/commands/engage/engage-community-user-add.spec.ts @@ -2,20 +2,19 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { entraUser } from '../../../../utils/entraUser.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import commands from '../../commands.js'; -import command from './engage-community-user-add.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; import { vivaEngage } from '../../../../utils/vivaEngage.js'; -import { entraUser } from '../../../../utils/entraUser.js'; +import commands from '../../commands.js'; +import command, { options } from './engage-community-user-add.js'; describe(commands.ENGAGE_COMMUNITY_USER_ADD, () => { const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIzNjAyMDAxMTAwOSJ9'; @@ -28,7 +27,7 @@ describe(commands.ENGAGE_COMMUNITY_USER_ADD, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -37,7 +36,7 @@ describe(commands.ENGAGE_COMMUNITY_USER_ADD, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; sinon.stub(entraUser, 'getUserIdsByUpns').resolves(userIds); sinon.stub(vivaEngage, 'getCommunityByDisplayName').resolves({ groupId: entraGroupId }); sinon.stub(vivaEngage, 'getCommunityById').resolves({ groupId: entraGroupId }); diff --git a/src/m365/viva/commands/engage/engage-community-user-add.ts b/src/m365/viva/commands/engage/engage-community-user-add.ts index 23118585d4b..4e48636f214 100644 --- a/src/m365/viva/commands/engage/engage-community-user-add.ts +++ b/src/m365/viva/commands/engage/engage-community-user-add.ts @@ -1,7 +1,6 @@ import { z } from 'zod'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { validation } from '../../../../utils/validation.js'; @@ -10,25 +9,21 @@ import request, { CliRequestOptions } from '../../../../request.js'; import { entraUser } from '../../../../utils/entraUser.js'; import { formatting } from '../../../../utils/formatting.js'; -const options = globalOptionsZod - .extend({ - communityId: z.string().optional(), - communityDisplayName: zod.alias('n', z.string().optional()), - entraGroupId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - ids: z.string() - .refine(ids => validation.isValidGuidArray(ids) === true, invalidIds => ({ - message: `The following GUIDs are invalid: ${invalidIds}.` - })).optional(), - userNames: z.string() - .refine(userNames => validation.isValidUserPrincipalNameArray(userNames) === true, invalidUserNames => ({ - message: `The following user principal names are invalid: ${invalidUserNames}.` - })).optional(), - role: zod.alias('r', z.enum(['Admin', 'Member'])) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + communityId: z.string().optional(), + communityDisplayName: z.string().optional().alias('n'), + entraGroupId: z.uuid().optional(), + ids: z.string() + .refine(ids => validation.isValidGuidArray(ids) === true, { + error: e => `The following GUIDs are invalid: ${e.input}.` + }).optional(), + userNames: z.string() + .refine(userNames => validation.isValidUserPrincipalNameArray(userNames) === true, { + error: e => `The following user principal names are invalid: ${e.input}.` + }).optional(), + role: z.enum(['Admin', 'Member']).alias('r') +}); declare type Options = z.infer; interface CommandArgs { @@ -45,23 +40,23 @@ class VivaEngageCommunityUserAddCommand extends GraphCommand { return 'Adds a user to a specific Microsoft 365 Viva Engage community'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, { - message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.' + error: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.' }) .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, { - message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.' + error: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.' }) .refine(options => options.ids || options.userNames, { - message: 'Specify either of ids or userNames.' + error: 'Specify either of ids or userNames.' }) - .refine(options => options.userNames !== undefined || options.ids !== undefined, { - message: 'Specify either ids or userNames, but not both.' + .refine(options => typeof options.userNames !== 'undefined' || typeof options.ids !== 'undefined', { + error: 'Specify either ids or userNames, but not both.' }); } diff --git a/src/m365/viva/commands/engage/engage-community-user-list.spec.ts b/src/m365/viva/commands/engage/engage-community-user-list.spec.ts index 9d7671194f8..aeee17ea433 100644 --- a/src/m365/viva/commands/engage/engage-community-user-list.spec.ts +++ b/src/m365/viva/commands/engage/engage-community-user-list.spec.ts @@ -2,6 +2,8 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; @@ -9,12 +11,9 @@ import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import commands from '../../commands.js'; -import command from './engage-community-user-list.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; import { vivaEngage } from '../../../../utils/vivaEngage.js'; +import commands from '../../commands.js'; +import command, { options } from './engage-community-user-list.js'; describe(commands.ENGAGE_COMMUNITY_USER_LIST, () => { const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIzNjAyMDAxMTAwOSJ9'; @@ -67,7 +66,7 @@ describe(commands.ENGAGE_COMMUNITY_USER_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -76,7 +75,7 @@ describe(commands.ENGAGE_COMMUNITY_USER_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/viva/commands/engage/engage-community-user-list.ts b/src/m365/viva/commands/engage/engage-community-user-list.ts index 4c7e3dfb8a0..752d3261011 100644 --- a/src/m365/viva/commands/engage/engage-community-user-list.ts +++ b/src/m365/viva/commands/engage/engage-community-user-list.ts @@ -1,26 +1,20 @@ import { z } from 'zod'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -import { validation } from '../../../../utils/validation.js'; import { vivaEngage } from '../../../../utils/vivaEngage.js'; import { CliRequestOptions } from '../../../../request.js'; import { User } from '@microsoft/microsoft-graph-types'; import { odata } from '../../../../utils/odata.js'; -const options = globalOptionsZod - .extend({ - communityId: z.string().optional(), - communityDisplayName: zod.alias('n', z.string().optional()), - entraGroupId: z.string() - .refine(name => validation.isValidGuid(name), name => ({ - message: `'${name}' is not a valid GUID.` - })).optional(), - role: zod.alias('r', z.enum(['Admin', 'Member']).optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + communityId: z.string().optional(), + communityDisplayName: z.string().optional().alias('n'), + entraGroupId: z.uuid().optional(), + role: z.enum(['Admin', 'Member']).optional().alias('r') +}); declare type Options = z.infer; interface CommandArgs { @@ -41,17 +35,17 @@ class VivaEngageCommunityUserListCommand extends GraphCommand { return 'Lists all users within a specified Microsoft 365 Viva Engage community'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, { - message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.' + error: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.' }) .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, { - message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.' + error: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.' }); } diff --git a/src/m365/viva/commands/engage/engage-community-user-remove.spec.ts b/src/m365/viva/commands/engage/engage-community-user-remove.spec.ts index ab2c60a16bd..e6e8ea7f238 100644 --- a/src/m365/viva/commands/engage/engage-community-user-remove.spec.ts +++ b/src/m365/viva/commands/engage/engage-community-user-remove.spec.ts @@ -2,20 +2,19 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; import { telemetry } from '../../../../telemetry.js'; +import { entraUser } from '../../../../utils/entraUser.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import commands from '../../commands.js'; -import command from './engage-community-user-remove.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; import { vivaEngage } from '../../../../utils/vivaEngage.js'; -import { entraUser } from '../../../../utils/entraUser.js'; +import commands from '../../commands.js'; +import command, { options } from './engage-community-user-remove.js'; describe(commands.ENGAGE_COMMUNITY_USER_REMOVE, () => { const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIzNjAyMDAxMTAwOSJ9'; @@ -27,7 +26,7 @@ describe(commands.ENGAGE_COMMUNITY_USER_REMOVE, () => { let log: string[]; let logger: Logger; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -36,7 +35,7 @@ describe(commands.ENGAGE_COMMUNITY_USER_REMOVE, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; sinon.stub(entraUser, 'getUserIdByUpn').resolves(userId); sinon.stub(vivaEngage, 'getCommunityByDisplayName').resolves({ groupId: entraGroupId }); sinon.stub(vivaEngage, 'getCommunityById').resolves({ groupId: entraGroupId }); diff --git a/src/m365/viva/commands/engage/engage-community-user-remove.ts b/src/m365/viva/commands/engage/engage-community-user-remove.ts index fa6f9e5c87e..3695270a7eb 100644 --- a/src/m365/viva/commands/engage/engage-community-user-remove.ts +++ b/src/m365/viva/commands/engage/engage-community-user-remove.ts @@ -1,7 +1,6 @@ import { z } from 'zod'; import { Logger } from '../../../../cli/Logger.js'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import { validation } from '../../../../utils/validation.js'; @@ -10,25 +9,18 @@ import request, { CliRequestOptions } from '../../../../request.js'; import { entraUser } from '../../../../utils/entraUser.js'; import { cli } from '../../../../cli/cli.js'; -const options = globalOptionsZod - .extend({ - communityId: z.string().optional(), - communityDisplayName: zod.alias('n', z.string().optional()), - entraGroupId: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - id: z.string() - .refine(id => validation.isValidGuid(id), id => ({ - message: `'${id}' is not a valid GUID.` - })).optional(), - userName: z.string() - .refine(userName => validation.isValidUserPrincipalName(userName), userName => ({ - message: `'${userName}' is not a valid user principal name.` - })).optional(), - force: z.boolean().optional() - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + communityId: z.string().optional(), + communityDisplayName: z.string().optional().alias('n'), + entraGroupId: z.uuid().optional(), + id: z.uuid().optional(), + userName: z.string() + .refine(userName => validation.isValidUserPrincipalName(userName), { + error: e => `'${e.input}' is not a valid user principal name.` + }).optional(), + force: z.boolean().optional() +}); declare type Options = z.infer; interface CommandArgs { @@ -45,23 +37,23 @@ class VivaEngageCommunityUserRemoveCommand extends GraphCommand { return 'Removes a specified user from a Microsoft 365 Viva Engage community'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, { - message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.' + error: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.' }) .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, { - message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.' + error: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.' }) .refine(options => options.id || options.userName, { - message: 'Specify either of id or userName.' + error: 'Specify either of id or userName.' }) - .refine(options => options.userName !== undefined || options.id !== undefined, { - message: 'Specify either id or userName, but not both.' + .refine(options => typeof options.userName !== 'undefined' || typeof options.id !== 'undefined', { + error: 'Specify either id or userName, but not both.' }); } diff --git a/src/m365/viva/commands/engage/engage-role-member-list.spec.ts b/src/m365/viva/commands/engage/engage-role-member-list.spec.ts index b79b16e97b8..44a26e1e7f2 100644 --- a/src/m365/viva/commands/engage/engage-role-member-list.spec.ts +++ b/src/m365/viva/commands/engage/engage-role-member-list.spec.ts @@ -1,6 +1,8 @@ import assert from 'assert'; import sinon from 'sinon'; import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; import { Logger } from '../../../../cli/Logger.js'; import { CommandError } from '../../../../Command.js'; import request from '../../../../request.js'; @@ -8,12 +10,9 @@ import { telemetry } from '../../../../telemetry.js'; import { pid } from '../../../../utils/pid.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; -import commands from '../../commands.js'; -import command from './engage-role-member-list.js'; -import { CommandInfo } from '../../../../cli/CommandInfo.js'; -import { z } from 'zod'; -import { cli } from '../../../../cli/cli.js'; import { vivaEngage } from '../../../../utils/vivaEngage.js'; +import commands from '../../commands.js'; +import command, { options } from './engage-role-member-list.js'; describe(commands.ENGAGE_ROLE_MEMBER_LIST, () => { const roleId = 'ec759127-089f-4f91-8dfc-03a30b51cb38'; @@ -23,7 +22,7 @@ describe(commands.ENGAGE_ROLE_MEMBER_LIST, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; - let commandOptionsSchema: z.ZodTypeAny; + let commandOptionsSchema: typeof options; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -32,7 +31,7 @@ describe(commands.ENGAGE_ROLE_MEMBER_LIST, () => { sinon.stub(session, 'getId').returns(''); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); - commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; }); beforeEach(() => { diff --git a/src/m365/viva/commands/engage/engage-role-member-list.ts b/src/m365/viva/commands/engage/engage-role-member-list.ts index 01a8fa32940..c05272ff00b 100644 --- a/src/m365/viva/commands/engage/engage-role-member-list.ts +++ b/src/m365/viva/commands/engage/engage-role-member-list.ts @@ -1,21 +1,16 @@ import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; -import { zod } from '../../../../utils/zod.js'; import { Logger } from '../../../../cli/Logger.js'; import { odata } from '../../../../utils/odata.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; -import { validation } from '../../../../utils/validation.js'; import { vivaEngage } from '../../../../utils/vivaEngage.js'; -const options = globalOptionsZod - .extend({ - roleId: zod.alias('i', z.string().refine(name => validation.isValidGuid(name), name => ({ - message: `'${name}' is not a valid GUID.` - })).optional()), - roleName: zod.alias('n', z.string().optional()) - }) - .strict(); +export const options = z.strictObject({ + ...globalOptionsZod.shape, + roleId: z.uuid().optional().alias('i'), + roleName: z.string().optional().alias('n') +}); declare type Options = z.infer; interface CommandArgs { @@ -31,14 +26,14 @@ class VivaEngageRoleMemberListCommand extends GraphCommand { return 'Lists all users assigned to a Viva Engage role'; } - public get schema(): z.ZodTypeAny { + public get schema(): z.ZodType { return options; } - public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { return schema .refine(options => [options.roleId, options.roleName].filter(x => x !== undefined).length === 1, { - message: 'Specify either roleId, or roleName, but not both.' + error: 'Specify either roleId, or roleName, but not both.' }); } diff --git a/src/utils/zod.spec.ts b/src/utils/zod.spec.ts index c9b23c98362..50613976943 100644 --- a/src/utils/zod.spec.ts +++ b/src/utils/zod.spec.ts @@ -4,17 +4,17 @@ import { zod } from '../utils/zod.js'; describe('utils/zod', () => { it('parses string option', () => { - const schema = z.object({ + const schema = z.strictObject({ stringOption: z.string() - }).strict(); + }); const options = zod.schemaToOptionInfo(schema); assert.strictEqual(options[0].type, 'string'); }); it('parses enum option', () => { - const schema = z.object({ + const schema = z.strictObject({ enumOption: z.enum(['a', 'b', 'c']) - }).strict(); + }); const options = zod.schemaToOptionInfo(schema); assert.deepStrictEqual(options[0].autocomplete, ['a', 'b', 'c']); }); @@ -25,58 +25,57 @@ describe('utils/zod', () => { B = 'B', C = 'C' } - const schema = z.object({ - enumOption: z.nativeEnum(TestEnum) - }).strict(); + const schema = z.strictObject({ + enumOption: z.enum(TestEnum) + }); const options = zod.schemaToOptionInfo(schema); assert.deepStrictEqual(options[0].autocomplete, ['A', 'B', 'C']); }); it('parses boolean option', () => { - const schema = z.object({ + const schema = z.strictObject({ booleanOption: z.boolean() - }).strict(); + }); const options = zod.schemaToOptionInfo(schema); assert.strictEqual(options[0].type, 'boolean'); }); it('parses number option', () => { - const schema = z.object({ + const schema = z.strictObject({ numberOption: z.number() - }).strict(); + }); const options = zod.schemaToOptionInfo(schema); assert.strictEqual(options[0].type, 'number'); }); it('parses required string option', () => { - const schema = z.object({ + const schema = z.strictObject({ stringOption: z.string() - }).strict(); + }); const options = zod.schemaToOptionInfo(schema); assert.strictEqual(options[0].required, true); }); it('parses optional string option', () => { - const schema = z.object({ + const schema = z.strictObject({ stringOption: z.string().optional() - }).strict(); + }); const options = zod.schemaToOptionInfo(schema); assert.strictEqual(options[0].required, false); }); it('parses optional boolean option with a default value', () => { - const schema = z.object({ + const schema = z.strictObject({ boolOption: z.boolean().default(false) - }).strict(); + }); const options = zod.schemaToOptionInfo(schema); assert.strictEqual(options[0].required, false); }); it('parses refined schema', () => { - const schema = z.object({ + const schema = z.strictObject({ boolOption: z.boolean().default(false) }) - .strict() .refine(data => data.boolOption === true, { message: 'boolOption must be true' }); diff --git a/src/utils/zod.ts b/src/utils/zod.ts index ca6954c4e38..f03185dbb0b 100644 --- a/src/utils/zod.ts +++ b/src/utils/zod.ts @@ -1,52 +1,53 @@ -import { ZodTypeAny, z } from 'zod'; +import { z } from 'zod'; +import { JSONSchema } from 'zod/v4/core'; +import { EnumLike } from 'zod/v4/core/util.cjs'; import { CommandOptionInfo } from '../cli/CommandOptionInfo'; import { CommandOption } from '../Command'; -function parseEffect(def: z.ZodEffectsDef, _options: CommandOptionInfo[], _currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { - return def.schema._def; -} - -function parseIntersection(def: z.ZodIntersectionDef, _options: CommandOptionInfo[], _currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { - if (def.left._def.typeName !== z.ZodFirstPartyTypeKind.ZodAny) { - return def.left._def; - } - - if (def.right._def.typeName !== z.ZodFirstPartyTypeKind.ZodAny) { - return def.right._def; +declare module 'zod' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface ZodType = z.core.$ZodTypeInternals> { + alias(name: string): this & { alias?: string }; } - - return; } -function parseObject(def: z.ZodObjectDef, options: CommandOptionInfo[], _currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { - const properties = def.shape(); - for (const key in properties) { - const property = properties[key]; +z.ZodType.prototype.alias = function (name: string) { + (this.def as any).alias = name; + return this; +}; + +function parseObject(schema: JSONSchema.JSONSchema, options: CommandOptionInfo[], _currentOption?: CommandOptionInfo): JSONSchema.JSONSchema | undefined { + for (const key in schema.properties) { + const property = schema.properties[key]; const option: CommandOptionInfo = { name: key, long: key, - short: property._def.alias, - required: true, + short: (property as any)['x-alias'], + required: schema.required?.includes(key) && (property as any).default === undefined || false, type: 'string' }; - parseDef(property._def, options, option); + parseJSONSchema(property as JSONSchema.JSONSchema, options, option); options.push(option); } return; } -function parseString(_def: z.ZodStringDef, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { +function parseString(schema: JSONSchema.JSONSchema, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): JSONSchema.JSONSchema | undefined { if (currentOption) { currentOption.type = 'string'; + + if (schema.enum) { + currentOption.autocomplete = schema.enum.map(e => String(e)); + } } return; } -function parseNumber(_def: z.ZodNumberDef, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { +function parseNumber(schema: JSONSchema.JSONSchema, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): JSONSchema.JSONSchema | undefined { if (currentOption) { currentOption.type = 'number'; } @@ -54,7 +55,7 @@ function parseNumber(_def: z.ZodNumberDef, _options: CommandOptionInfo[], curren return; } -function parseBoolean(_def: z.ZodBooleanDef, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { +function parseBoolean(schema: JSONSchema.JSONSchema, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): JSONSchema.JSONSchema | undefined { if (currentOption) { currentOption.type = 'boolean'; } @@ -62,82 +63,41 @@ function parseBoolean(_def: z.ZodBooleanDef, _options: CommandOptionInfo[], curr return; } -function parseOptional(def: z.ZodOptionalDef, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { - if (currentOption) { - currentOption.required = false; - } - - return def.innerType._def; -} - -function parseDefault(def: z.ZodDefaultDef, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { - if (currentOption) { - currentOption.required = false; - } - - return def.innerType._def; -} - -function parseEnum(def: z.ZodEnumDef, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { - if (currentOption) { - currentOption.type = 'string'; - currentOption.autocomplete = [...def.values]; - } - - return; -} - -function parseNativeEnum(def: z.ZodNativeEnumDef, _options: CommandOptionInfo[], currentOption?: CommandOptionInfo): z.ZodTypeDef | undefined { - if (currentOption) { - currentOption.type = 'string'; - currentOption.autocomplete = Object.values(def.values).map(v => String(v)); - } - - return; -} - -function getParseFn(typeName: z.ZodFirstPartyTypeKind): undefined | ((def: any, options: CommandOptionInfo[], currentOption?: CommandOptionInfo) => z.ZodTypeDef | undefined) { +function getParseFn(typeName?: "object" | "array" | "string" | "number" | "boolean" | "null" | "integer"): undefined | ((schema: JSONSchema.JSONSchema, options: CommandOptionInfo[], currentOption?: CommandOptionInfo) => JSONSchema.JSONSchema | undefined) { switch (typeName) { - case z.ZodFirstPartyTypeKind.ZodEffects: - return parseEffect; - case z.ZodFirstPartyTypeKind.ZodObject: + case 'object': return parseObject; - case z.ZodFirstPartyTypeKind.ZodOptional: - return parseOptional; - case z.ZodFirstPartyTypeKind.ZodString: + case 'string': return parseString; - case z.ZodFirstPartyTypeKind.ZodNumber: + case 'number': + case 'integer': return parseNumber; - case z.ZodFirstPartyTypeKind.ZodBoolean: + case 'boolean': return parseBoolean; - case z.ZodFirstPartyTypeKind.ZodEnum: - return parseEnum; - case z.ZodFirstPartyTypeKind.ZodNativeEnum: - return parseNativeEnum; - case z.ZodFirstPartyTypeKind.ZodDefault: - return parseDefault; - case z.ZodFirstPartyTypeKind.ZodIntersection: - return parseIntersection; default: return; } } -function parseDef(def: z.ZodTypeDef, options: CommandOptionInfo[], currentOption?: CommandOptionInfo): void { - let parsedDef: z.ZodTypeDef | undefined = def; +function parseJSONSchema(jsonSchema: JSONSchema.JSONSchema, options: CommandOptionInfo[], currentOption?: CommandOptionInfo): void { + let parsedSchema: JSONSchema.JSONSchema | undefined = jsonSchema; do { - const parse = getParseFn((parsedDef as any).typeName); + if (parsedSchema.allOf) { + parsedSchema.allOf.forEach(s => parseJSONSchema(s, options, currentOption)); + } + + const parse = getParseFn(parsedSchema.type); if (!parse) { break; } - parsedDef = parse(parsedDef as any, options, currentOption); - if (!parsedDef) { + parsedSchema = parse(parsedSchema, options, currentOption); + if (!parsedSchema) { break; } - } while (parsedDef); + } while (parsedSchema); } function optionToString(optionInfo: CommandOptionInfo): string { @@ -159,20 +119,20 @@ function optionToString(optionInfo: CommandOptionInfo): string { return s; }; -type EnumLike = { - [k: string]: string | number - [nu: number]: string -}; - export const zod = { - alias(alias: string, type: T): T { - type._def.alias = alias; - return type; - }, - schemaToOptionInfo(schema: z.ZodSchema): CommandOptionInfo[] { + const jsonSchema = z.toJSONSchema(schema, { + override: s => { + const alias = ((s.zodSchema as unknown as z.core.$ZodTypeInternals).def as any).alias; + if (alias) { + s.jsonSchema['x-alias'] = alias; + } + }, + unrepresentable: 'any' + }); + const options: CommandOptionInfo[] = []; - parseDef(schema._def, options); + parseJSONSchema(jsonSchema, options); return options; }, @@ -187,7 +147,7 @@ export const zod = { return options; }, - coercedEnum: (e: T): z.ZodEffects, T[keyof T], unknown> => + coercedEnum: (e: T): z.ZodPipe, z.ZodEnum> => z.preprocess(val => { const target = String(val)?.toLowerCase(); for (const k of Object.values(e)) { @@ -197,5 +157,5 @@ export const zod = { } return null; - }, z.nativeEnum(e)) + }, z.enum(e)) }; \ No newline at end of file