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..537594586f
--- /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 the 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..26ab90c6d5
--- /dev/null
+++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.spec.ts
@@ -0,0 +1,253 @@
+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 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);
+
+ 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 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..03e119ea59
--- /dev/null
+++ b/src/m365/outlook/commands/mailbox/mailbox-settings-get.ts
@@ -0,0 +1,92 @@
+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 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';
+ }
+
+ 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..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,13 +599,13 @@ describe(commands.MAILBOX_SETTINGS_SET, () => {
});
});
- it('fails updating mailbox settings if both userId and userName is specified in app-only mode', async () => {
+ 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({ userId: userId, userName: userName, timeFormat: 'HH:mm', verbose: 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, but not both'));
+ 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 () => {
diff --git a/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts b/src/m365/outlook/commands/mailbox/mailbox-settings-set.ts
index 948633d370..8c941d4ef2 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,8 +76,8 @@ 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';
}
const userIdentifier = args.options.userId ?? args.options.userName;