From fee11aacd65738d1b59b30e5ce66cb149e4ddd9c Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Sat, 18 Jan 2025 11:44:37 +0100 Subject: [PATCH 1/4] New command: outlook mailbox settings get --- .../outlook/mailbox/mailbox-settings-get.mdx | 131 +++++++++ docs/src/config/sidebars.ts | 5 + src/m365/outlook/commands.ts | 1 + .../mailbox/mailbox-settings-get.spec.ts | 251 ++++++++++++++++++ .../commands/mailbox/mailbox-settings-get.ts | 89 +++++++ .../mailbox/mailbox-settings-set.spec.ts | 9 + .../commands/mailbox/mailbox-settings-set.ts | 4 + 7 files changed, 490 insertions(+) create mode 100644 docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx create mode 100644 src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts create mode 100644 src/m365/outlook/commands/mailbox/mailbox-settings-get.ts diff --git a/docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx b/docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx new file mode 100644 index 0000000000..7093784ea6 --- /dev/null +++ b/docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx @@ -0,0 +1,131 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# outlook mailbox settings get + +Get the user's mailbox settings + +## Usage + +```sh +m365 outlook mailbox settings get [options] +``` + +## Options + +```md definition-list +`-i, --userId [userId]` +: The ID of the Microsoft Entra user for which you want to get mailbox settings. Specify either `userId` or `userName`, but not both. This option is required when using application permissions. + +`-n, --userName [userName]` +: The UPN of the Microsoft Entra user for which you want to get mailbox settings. Specify either `userId` or `userName`, but not both. This option is required when using application permissions. +``` + + + +## Examples + +Get mailbox settings of signed-in user + +```sh +m365 outlook mailbox settings get +``` + +Get mailbox settings of a user specified by id + +```sh +m365 outlook mailbox settings get --userId 1caf7dcd-7e83-4c3a-94f7-932a1299c844 +``` + +## Response + + + + + ```json + { + "archiveFolder": "AQMkAGRlM2Y5YTkzLWI2NzAtNDczOS05YWMyLTJhZGY2MGExMGU0MgAuAAADSG3wPE27kUeySjmT5eRT8QEAfJKVL07sbkmIfHqjbDnRgQAAAgEMAAAA", + "timeZone": "Central Europe Standard Time", + "delegateMeetingMessageDeliveryOptions": "sendToDelegateOnly", + "dateFormat": "dd.MM.yyyy", + "timeFormat": "H:mm", + "userPurpose": "user", + "automaticRepliesSetting": { + "status": "disabled", + "externalAudience": "all", + "internalReplyMessage": "On vacation. Will be back.", + "externalReplyMessage": "Vacation", + "scheduledStartDateTime": { + "dateTime": "2025-01-20T08:00:00.0000000", + "timeZone": "UTC" + }, + "scheduledEndDateTime": { + "dateTime": "2025-01-25T18:00:00.0000000", + "timeZone": "UTC" + } + }, + "language": { + "locale": "en-US", + "displayName": "English (United States)" + }, + "workingHours": { + "daysOfWeek": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday" + ], + "startTime": "08:00:00.0000000", + "endTime": "16:30:00.0000000", + "timeZone": { + "name": "Central Europe Standard Time" + } + } + } + ``` + + + + + ```text + archiveFolder : AQMkAGRlM2Y5YTkzLWI2NzAtNDczOS05YWMyLTJhZGY2MGExMGU0MgAuAAADSG3wPE27kUeySjmT5eRT8QEAfJKVL07sbkmIfHqjbDnRgQAAAgEMAAAA + automaticRepliesSetting : {"status":"disabled","externalAudience":"all","internalReplyMessage":"","externalReplyMessage":"","scheduledStartDateTime":{"dateTime":"2025-01-20T08:00:00.0000000","timeZone":"UTC"},"scheduledEndDateTime":{"dateTime":"2025-01-25T18:00:00.0000000","timeZone":"UTC"}} + dateFormat : dd.MM.yyyy + delegateMeetingMessageDeliveryOptions: sendToDelegateOnly + language : {"locale":"en-US","displayName":"English (United States)"} + timeFormat : H:mm + timeZone : Central Europe Standard Time + userPurpose : user + workingHours : {"daysOfWeek":["monday","tuesday","wednesday","thursday","friday"],"startTime":"08:00:00.0000000","endTime":"16:30:00.0000000","timeZone":{"name":"Central Europe Standard Time"}} + ``` + + + + + ```csv + archiveFolder,timeZone,delegateMeetingMessageDeliveryOptions,dateFormat,timeFormat,userPurpose + AQMkAGRlM2Y5YTkzLWI2NzAtNDczOS05YWMyLTJhZGY2MGExMGU0MgAuAAADSG3wPE27kUeySjmT5eRT8QEAfJKVL07sbkmIfHqjbDnRgQAAAgEMAAAA,Central Europe Standard Time,sendToDelegateOnly,dd.MM.yyyy,H:mm,user + ``` + + + + + ```md + # outlook mailbox settings get + + Date: 1/17/2025 + + Property | Value + ---------|------- + archiveFolder | AQMkAGRlM2Y5YTkzLWI2NzAtNDczOS05YWMyLTJhZGY2MGExMGU0MgAuAAADSG3wPE27kUeySjmT5eRT8QEAfJKVL07sbkmIfHqjbDnRgQAAAgEMAAAA + timeZone | Central Europe Standard Time + delegateMeetingMessageDeliveryOptions | sendToDelegateOnly + dateFormat | dd.MM.yyyy + timeFormat | H:mm + userPurpose | user + ``` + + + \ No newline at end of file diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 8302d3b8d0..560915ae81 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -1184,6 +1184,11 @@ const sidebars: SidebarsConfig = { }, { mailbox: [ + { + type: 'doc', + label: 'mailbox settings get', + id: 'cmd/outlook/mailbox/mailbox-settings-get' + }, { type: 'doc', label: 'mailbox settings set', diff --git a/src/m365/outlook/commands.ts b/src/m365/outlook/commands.ts index a9ae8fc3f2..1600259124 100644 --- a/src/m365/outlook/commands.ts +++ b/src/m365/outlook/commands.ts @@ -2,6 +2,7 @@ const prefix: string = 'outlook'; export default { MAIL_SEND: `${prefix} mail send`, + MAILBOX_SETTINGS_GET: `${prefix} mailbox settings get`, MAILBOX_SETTINGS_SET: `${prefix} mailbox settings set`, MESSAGE_GET: `${prefix} message get`, MESSAGE_LIST: `${prefix} message list`, diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts new file mode 100644 index 0000000000..a24b380bc7 --- /dev/null +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts @@ -0,0 +1,251 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import commands from '../../commands.js'; +import request from '../../../../request.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 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'; + +describe(commands.MAILBOX_SETTINGS_GET, () => { + const userId = 'abcd1234-de71-4623-b4af-96380a352509'; + const userName = 'john.doe@contoso.com'; + + const mailboxSettingsResponse = { + "timeZone": "Central Europe Standard Time", + "delegateMeetingMessageDeliveryOptions": "sendToDelegateAndInformationToPrincipal", + "dateFormat": "dd.MM.yyyy", + "timeFormat": "HH:mm", + "userPurpose": "user", + "automaticRepliesSetting": { + "status": "disabled", + "externalAudience": "none", + "internalReplyMessage": "I'm out of office. Contact my manager in case of any troubles.", + "externalReplyMessage": "I'm out of office", + "scheduledStartDateTime": { + "dateTime": "2025-01-03T19:00:00.0000000", + "timeZone": "UTC" + }, + "scheduledEndDateTime": { + "dateTime": "2025-01-04T19:00:00.0000000", + "timeZone": "UTC" + } + }, + "language": { + "locale": "cs-CZ", + "displayName": "Czech (Czech Republic)" + }, + "workingHours": { + "daysOfWeek": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday" + ], + "startTime": "07:00:00.0000000", + "endTime": "16:00:00.0000000", + "timeZone": { + "name": "Central Europe Standard Time" + } + } + }; + + let log: string[]; + let logger: Logger; + let commandInfo: CommandInfo; + let loggerLogSpy: sinon.SinonSpy; + let commandOptionsSchema: z.ZodTypeAny; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + if (!auth.connection.accessTokens[auth.defaultResource]) { + auth.connection.accessTokens[auth.defaultResource] = { + expiresOn: 'abc', + accessToken: 'abc' + }; + } + commandInfo = cli.getCommandInfo(command); + commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(false); + }); + + afterEach(() => { + sinonUtil.restore([ + accessToken.isAppOnlyAccessToken, + request.get + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.MAILBOX_SETTINGS_GET); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('fails validation if userId is not a valid GUID', () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + const actual = commandOptionsSchema.safeParse({ + userId: 'foo' + }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if userName is not a valid UPN', () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + const actual = commandOptionsSchema.safeParse({ + userName: 'foo' + }); + assert.notStrictEqual(actual.success, true); + }); + + it('retrieves mailbox settings of the signed-in user', async () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(false); + + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === 'https://graph.microsoft.com/v1.0/me/mailboxSettings') { + return mailboxSettingsResponse; + } + + throw 'Invalid request'; + }); + + const result = commandOptionsSchema.safeParse({ + verbose: true + }); + + await command.action(logger, { + options: result.data + }); + assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); + }); + + it('retrieves mailbox settings of a user specified by id', async () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users('${ userId }')/mailboxSettings`) { + return mailboxSettingsResponse; + } + + throw 'Invalid request'; + }); + + const result = commandOptionsSchema.safeParse({ + userId: userId, + verbose: true + }); + + await command.action(logger, { + options: result.data + }); + assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); + }); + + it('retrieves mailbox settings of a user specified by user principal name', async () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users('${userName}')/mailboxSettings`) { + return mailboxSettingsResponse; + } + + throw 'Invalid request'; + }); + + const result = commandOptionsSchema.safeParse({ + userName: userName, + verbose: true + }); + + await command.action(logger, { + options: result.data + }); + assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); + }); + + it('fails retrieve mailbox settings if both userId and userName is specified in app-only mode', async () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + const result = commandOptionsSchema.safeParse({ userId: userId, userName: userName, verbose: true }); + + await assert.rejects(command.action(logger, { options: result.data }), new CommandError('When running with application permissions either userId or userName is required, but not both')); + }); + + it('fails retrieve mailbox settings if neither userId nor userName is specified in app-only mode', async () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + 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')); + }); + + 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')); + }); + + 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')); + }); + + it('correctly handles API OData error', async () => { + sinon.stub(request, 'get').rejects({ + error: { + 'odata.error': { + code: '-1, InvalidOperationException', + message: { + value: 'Invalid request' + } + } + } + }); + const result = commandOptionsSchema.safeParse({ verbose: true }); + 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 new file mode 100644 index 0000000000..cc56a44bfd --- /dev/null +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts @@ -0,0 +1,89 @@ +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'; +import { validation } from '../../../../utils/validation.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +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(); + +declare type Options = z.infer; + +interface CommandArgs { + options: Options; +} + +class OutlookMailboxSettingsGetCommand extends GraphCommand { + public get name(): string { + return commands.MAILBOX_SETTINGS_GET; + } + + public get description(): string { + return `Get the user's mailbox settings`; + } + + public get schema(): z.ZodTypeAny | undefined { + return options; + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + const isAppOnlyAccessToken = accessToken.isAppOnlyAccessToken(auth.connection.accessTokens[auth.defaultResource].accessToken); + + let requestUrl = `${this.resource}/v1.0/me/mailboxSettings`; + + if (isAppOnlyAccessToken) { + if (args.options.userId && args.options.userName) { + throw 'When running with application permissions either userId or userName is required, but not both'; + } + + if (!(args.options.userId || args.options.userName)) { + throw 'When running with application permissions either userId or userName is required'; + } + + const userIdentifier = args.options.userId ?? args.options.userName; + + if (this.verbose) { + await logger.logToStderr(`Retrieving mailbox settings for user ${userIdentifier}...`); + } + + requestUrl = `${this.resource}/v1.0/users('${userIdentifier}')/mailboxSettings`; + } + else { + if (args.options.userId || args.options.userName) { + throw 'You can retrieve mailbox settings of other users only if CLI is authenticated in app-only mode'; + } + } + + const requestOptions: CliRequestOptions = { + url: requestUrl, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json' + }; + + try { + const result = await request.get(requestOptions); + await logger.log(result); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new OutlookMailboxSettingsGetCommand(); \ No newline at end of file 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 3c08d6eeff..8f0ad0587d 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts @@ -608,6 +608,15 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { await assert.rejects(command.action(logger, { options: result.data }), new CommandError('When running with application permissions either userId or userName is required, but not both')); }); + it('fails updating mailbox settings if neither userId nor userName is specified in app-only mode', async () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + 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')); + }); + 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')); diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts index 948633d370..1898864ebc 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts @@ -77,6 +77,10 @@ class OutlookMailboxSettingsSetCommand extends GraphCommand { throw 'When running with application permissions either userId or userName is required, but not both'; } + if (!(args.options.userId || args.options.userName)) { + throw 'When running with application permissions either userId or userName is required'; + } + const userIdentifier = args.options.userId ?? args.options.userName; if (this.verbose) { From 0f8636f12ab89fdf24e28db0e4a6eb500319361c Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Fri, 31 Jan 2025 12:05:57 +0100 Subject: [PATCH 2/4] New command: outlook mailbox settings get --- docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx b/docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx index 7093784ea6..537594586f 100644 --- a/docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx +++ b/docs/docs/cmd/outlook/mailbox/mailbox-settings-get.mdx @@ -26,7 +26,7 @@ m365 outlook mailbox settings get [options] ## Examples -Get mailbox settings of signed-in user +Get mailbox settings of the signed-in user ```sh m365 outlook mailbox settings get From bc1119652c282cdf395df4e24d7772d8d0376155 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Sat, 1 Feb 2025 14:41:54 +0100 Subject: [PATCH 3/4] New command: outlook mailbox settings get --- .../mailbox/mailbox-settings-get.spec.ts | 20 ++++++++++--------- .../commands/mailbox/mailbox-settings-get.ts | 11 ++++++---- .../mailbox/mailbox-settings-set.spec.ts | 11 +--------- .../commands/mailbox/mailbox-settings-set.ts | 7 +++---- 4 files changed, 22 insertions(+), 27 deletions(-) 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 a24b380bc7..26ab90c6d5 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts @@ -118,6 +118,17 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { assert.notStrictEqual(command.description, null); }); + it('fails validation if both userId and userName are specified', () => { + sinonUtil.restore(accessToken.isAppOnlyAccessToken); + sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); + + const actual = commandOptionsSchema.safeParse({ + userId: userId, + userName: userName + }); + assert.notStrictEqual(actual.success, true); + }); + it('fails validation if userId is not a valid GUID', () => { sinonUtil.restore(accessToken.isAppOnlyAccessToken); sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); @@ -206,15 +217,6 @@ describe(commands.MAILBOX_SETTINGS_GET, () => { assert(loggerLogSpy.calledOnceWith(mailboxSettingsResponse)); }); - it('fails retrieve mailbox settings if both userId and userName is specified in app-only mode', async () => { - sinonUtil.restore(accessToken.isAppOnlyAccessToken); - sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); - - const result = commandOptionsSchema.safeParse({ userId: userId, userName: userName, verbose: true }); - - await assert.rejects(command.action(logger, { options: result.data }), new CommandError('When running with application permissions either userId or userName is required, but not both')); - }); - it('fails retrieve mailbox settings if neither userId nor userName is specified in app-only mode', async () => { sinonUtil.restore(accessToken.isAppOnlyAccessToken); sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts index cc56a44bfd..66708d38e9 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts @@ -40,16 +40,19 @@ class OutlookMailboxSettingsGetCommand extends GraphCommand { return options; } + public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + return schema + .refine(options => !(options.userId && options.userName), { + message: 'Specify either userId or userName, but not both' + }); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { const isAppOnlyAccessToken = accessToken.isAppOnlyAccessToken(auth.connection.accessTokens[auth.defaultResource].accessToken); let requestUrl = `${this.resource}/v1.0/me/mailboxSettings`; if (isAppOnlyAccessToken) { - if (args.options.userId && args.options.userName) { - throw 'When running with application permissions either userId or userName is required, but not both'; - } - if (!(args.options.userId || args.options.userName)) { throw 'When running with application permissions either userId or userName is required'; } 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 8f0ad0587d..1901cbce82 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-set.spec.ts @@ -118,7 +118,7 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { assert.notStrictEqual(command.description, null); }); - it('fails validation if both userId and userName are provided in app-only mode', () => { + it('fails validation if both userId and userName are specified', () => { sinonUtil.restore(accessToken.isAppOnlyAccessToken); sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); @@ -599,15 +599,6 @@ describe(commands.MAILBOX_SETTINGS_SET, () => { }); }); - it('fails updating mailbox settings if both userId and userName is specified in app-only mode', async () => { - sinonUtil.restore(accessToken.isAppOnlyAccessToken); - sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); - - const result = commandOptionsSchema.safeParse({ userId: userId, userName: userName, 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, but not both')); - }); - it('fails updating mailbox settings if neither userId nor userName is specified in app-only mode', async () => { sinonUtil.restore(accessToken.isAppOnlyAccessToken); sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true); diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts index 1898864ebc..c36ecffea8 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts @@ -59,6 +59,9 @@ class OutlookMailboxSettingsSetCommand extends GraphCommand { public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { return schema + .refine(options => !(options.userId && options.userName), { + message: 'Specify either userId or userName, but not both' + }) .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, @@ -73,10 +76,6 @@ class OutlookMailboxSettingsSetCommand extends GraphCommand { let requestUrl = `${this.resource}/v1.0/me/mailboxSettings`; if (isAppOnlyAccessToken) { - if (args.options.userId && args.options.userName) { - throw 'When running with application permissions either userId or userName is required, but not both'; - } - if (!(args.options.userId || args.options.userName)) { throw 'When running with application permissions either userId or userName is required'; } From 69a90e68d83e3036997b8411b2f6c5d515debea4 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Mon, 3 Feb 2025 07:27:30 +0100 Subject: [PATCH 4/4] New command: outlook mailbox settings get --- src/m365/outlook/commands/mailbox/mailbox-settings-get.ts | 2 +- src/m365/outlook/commands/mailbox/mailbox-settings-set.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts index 66708d38e9..03e119ea59 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts @@ -53,7 +53,7 @@ class OutlookMailboxSettingsGetCommand extends GraphCommand { let requestUrl = `${this.resource}/v1.0/me/mailboxSettings`; if (isAppOnlyAccessToken) { - if (!(args.options.userId || args.options.userName)) { + if (!args.options.userId && !args.options.userName) { throw 'When running with application permissions either userId or userName is required'; } diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts index c36ecffea8..8c941d4ef2 100644 --- a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts +++ b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts @@ -76,7 +76,7 @@ class OutlookMailboxSettingsSetCommand extends GraphCommand { let requestUrl = `${this.resource}/v1.0/me/mailboxSettings`; if (isAppOnlyAccessToken) { - if (!(args.options.userId || args.options.userName)) { + if (!args.options.userId && !args.options.userName) { throw 'When running with application permissions either userId or userName is required'; }