Skip to content

Commit

Permalink
Adds 'tenant people profilecardproperty get' command. Closes #5623
Browse files Browse the repository at this point in the history
  • Loading branch information
milanholemans committed Nov 3, 2023
1 parent d9292ff commit 4b4eab4
Show file tree
Hide file tree
Showing 7 changed files with 388 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const dictionary = [
'audit',
'bin',
'builder',
'card',
'catalog',
'checklist',
'client',
Expand Down Expand Up @@ -68,6 +69,7 @@ const dictionary = [
'permission',
'place',
'policy',
'profile',
'property',
'records',
'recycle',
Expand Down
97 changes: 97 additions & 0 deletions docs/docs/cmd/tenant/people/people-profilecardproperty-get.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import Global from '/docs/cmd/_global.mdx';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# tenant people profilecardproperty get

Retrieves information about a specific profile card property

## Usage

```sh
m365 tenant people profilecardproperty get [options]
```

## Options

```md definition-list
`-n, --name <name>`
: The name of the property to retrieve. Allowed values: `userPrincipalName`, `faxNumber`, `streetAddress`, `postalCode`, `state`, `mailNickname`, `customAttribute1`, `customAttribute2`, `customAttribute3`, `customAttribute4`, `customAttribute5`, `customAttribute6`, `customAttribute7`, `customAttribute8`, `customAttribute9`, `customAttribute10`, `customAttribute11`, `customAttribute12`, `customAttribute13`, `customAttribute14`, `customAttribute15`.
```

<Global />

## Remarks

:::info

To use this command you must be either **Tenant Administrator** or **Global Administrator**.

:::

## Examples

Retrieve information about a specific profile card property

```sh
m365 tenant people profilecardproperty get --name customAttribute1
```

## Response

<Tabs>
<TabItem value="JSON">

```json
{
"directoryPropertyName": "customAttribute1",
"annotations": [
{
"displayName": "Cost center",
"localizations": [
{
"languageTag": "nl-NL",
"displayName": "Kostencentrum"
}
]
}
]
}
```

</TabItem>
<TabItem value="Text">

```text
directoryPropertyName: customAttribute1
displayName : Cost center
displayName nl-NL : Kostencentrum
```

</TabItem>
<TabItem value="CSV">

```csv
directoryPropertyName,displayName,displayName nl-NL
customAttribute1,Cost center,Kostencentrum
```

</TabItem>
<TabItem value="Markdown">

```md
# tenant people profilecardproperty get --name "customAttribute1"

Date: 3/11/2023

## Cost center

Property | Value
---------|-------
directoryPropertyName | customAttribute1
displayName | Cost center
displayName nl-NL | Kostencentrum
```

</TabItem>
</Tabs>
9 changes: 9 additions & 0 deletions docs/src/config/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,15 @@ const sidebars = {
}
]
},
{
people: [
{
type: 'doc',
label: 'people profilecardproperty get',
id: 'cmd/tenant/people/people-profilecardproperty-get'
}
]
},
{
report: [
{
Expand Down
1 change: 1 addition & 0 deletions src/m365/tenant/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const prefix: string = 'tenant';
export default {
ID_GET: `${prefix} id get`,
INFO_GET: `${prefix} info get`,
PEOPLE_PROFILECARDPROPERTY_GET: `${prefix} people profilecardproperty get`,
REPORT_ACTIVEUSERCOUNTS: `${prefix} report activeusercounts`,
REPORT_ACTIVEUSERDETAIL: `${prefix} report activeuserdetail`,
REPORT_OFFICE365ACTIVATIONCOUNTS: `${prefix} report office365activationcounts`,
Expand Down
154 changes: 154 additions & 0 deletions src/m365/tenant/commands/people/people-profilecardproperty-get.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
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 commands from '../../commands.js';
import command from './people-profilecardproperty-get.js';

describe(commands.PEOPLE_PROFILECARDPROPERTY_GET, () => {
const profileCardPropertyName = 'customAttribute1';

//#region Mocked responses
const response = {
directoryPropertyName: profileCardPropertyName,
annotations: [
{
displayName: 'Cost center',
localizations: [
{
languageTag: 'nl-NL',
displayName: 'Kostencentrum'
}
]
}
]
};
//#endregion

let log: any[];
let loggerLogSpy: sinon.SinonSpy;
let logger: Logger;
let commandInfo: CommandInfo;

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
sinon.stub(telemetry, 'trackEvent').returns();
sinon.stub(pid, 'getProcessName').returns('');
sinon.stub(session, 'getId').returns('');
auth.service.connected = true;
commandInfo = Cli.getCommandInfo(command);
});

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');
});

afterEach(() => {
sinonUtil.restore([
request.get
]);
});

after(() => {
sinon.restore();
auth.service.connected = false;
});

it('has correct name', () => {
assert.strictEqual(command.name, commands.PEOPLE_PROFILECARDPROPERTY_GET);
});

it('has a description', () => {
assert.notStrictEqual(command.description, null);
});

it('fails validation if the name is invalid', async () => {
const actual = await command.validate({ options: { name: 'invalid' } }, commandInfo);
assert.notStrictEqual(actual, true);
});

it('passes validation when the name is valid', async () => {
const actual = await command.validate({ options: { name: profileCardPropertyName } }, commandInfo);
assert.strictEqual(actual, true);
});

it('passes validation when the name is valid with other capitalization', async () => {
const actual = await command.validate({ options: { name: 'cUstoMATTriBUte1' } }, commandInfo);
assert.strictEqual(actual, true);
});

it('gets profile card property information', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/admin/people/profileCardProperties/${profileCardPropertyName}`) {
return response;
}

throw 'Invalid Request';
});

await command.action(logger, { options: { name: profileCardPropertyName, verbose: true } });
assert(loggerLogSpy.calledOnceWith(response));
});

it('gets profile card property information for text output', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/admin/people/profileCardProperties/${profileCardPropertyName}`) {
return response;
}

throw 'Invalid Request';
});

const textOutput = {
directoryPropertyName: profileCardPropertyName,
displayName: response.annotations[0].displayName,
['displayName ' + response.annotations[0].localizations[0].languageTag]: response.annotations[0].localizations[0].displayName
};

await command.action(logger, { options: { name: profileCardPropertyName, output: 'text' } });
assert(loggerLogSpy.calledOnceWith(textOutput));
});

it('handles error when profile card property does not exist', async () => {
sinon.stub(request, 'get').rejects({
response: {
status: 404
}
});

await assert.rejects(command.action(logger, { options: { name: profileCardPropertyName } } as any),
new CommandError(`Profile card property '${profileCardPropertyName}' does not exist.`));
});

it('handles unexpected API error', async () => {
const errorMessage = 'Something went wrong';
sinon.stub(request, 'get').rejects({
error: {
message: errorMessage
}
});

await assert.rejects(command.action(logger, { options: { name: profileCardPropertyName } } as any),
new CommandError(errorMessage));
});
});
92 changes: 92 additions & 0 deletions src/m365/tenant/commands/people/people-profilecardproperty-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Logger } from '../../../../cli/Logger.js';
import GlobalOptions from '../../../../GlobalOptions.js';
import GraphCommand from '../../../base/GraphCommand.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { profileCardPropertyNames, ProfileCardProperty } from './profileCardProperties.js';
import commands from '../../commands.js';

interface CommandArgs {
options: Options;
}

interface Options extends GlobalOptions {
name: string;
}

class TenantPeopleProfileCardPropertyGetCommand extends GraphCommand {
public get name(): string {
return commands.PEOPLE_PROFILECARDPROPERTY_GET;
}

public get description(): string {
return 'Retrieves information about a specific profile card property';
}

constructor() {
super();

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

#initOptions(): void {
this.options.unshift(
{
option: '-n, --name <name>',
autocomplete: profileCardPropertyNames
}
);
}

#initValidators(): void {
this.validators.push(
async (args: CommandArgs) => {
if (!profileCardPropertyNames.some(p => p.toLowerCase() === args.options.name.toLowerCase())) {
return `'${args.options.name}' is not a valid value for option name. Allowed values are: ${profileCardPropertyNames.join(', ')}.`;
}

return true;
}
);
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
try {
if (this.verbose) {
await logger.logToStderr(`Retrieving information about profile card property '${args.options.name}'...`);
}

const requestOptions: CliRequestOptions = {
url: `${this.resource}/v1.0/admin/people/profileCardProperties/${args.options.name}`,
headers: {
accept: 'application/json;odata.metadata=none'
},
responseType: 'json'
};

const result = await request.get<ProfileCardProperty>(requestOptions);
let output: any = result;

// Transform the output to make it more readable
if (args.options.output && args.options.output !== 'json' && result.annotations.length > 0) {
output = result.annotations[0].localizations.reduce((acc, curr) => ({
...acc,
['displayName ' + curr.languageTag]: curr.displayName
}), {
directoryPropertyName: result.directoryPropertyName, displayName: result.annotations[0].displayName
});
}

await logger.log(output);
}
catch (err: any) {
if (err.response?.status === 404) {
this.handleError(`Profile card property '${args.options.name}' does not exist.`);
}

this.handleRejectedODataJsonPromise(err);
}
}
}

export default new TenantPeopleProfileCardPropertyGetCommand();
Loading

0 comments on commit 4b4eab4

Please sign in to comment.