Skip to content

Commit

Permalink
fix(contacts): handle optional scim fields (webex#3827)
Browse files Browse the repository at this point in the history
  • Loading branch information
sreenara authored and parv_gour committed Sep 27, 2024
1 parent adc1ecd commit d3e7e79
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 32 deletions.
38 changes: 38 additions & 0 deletions packages/calling/src/Contacts/ContactsClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import {
mockContactGroupListOne,
mockContactGroupListTwo,
mockAvatarURL,
mockSCIMMinListResponse,
mockContactMinimum,
} from './contactFixtures';

describe('ContactClient Tests', () => {
Expand Down Expand Up @@ -724,4 +726,40 @@ describe('ContactClient Tests', () => {

expect(contactClient['contacts']).toEqual(mockContactListOne);
});

it('test resolveContacts function for a minimal contact with few details', () => {
const contact = contactClient['resolveCloudContacts'](
{userId: mockContactMinimum},
mockSCIMMinListResponse.body
);

expect(contact).toEqual([
{
avatarURL: '',
avatarUrlDomain: undefined,
contactId: 'userId',
contactType: 'CLOUD',
department: undefined,
displayName: undefined,
emails: undefined,
encryptionKeyUrl: 'kms://cisco.com/keys/dcf18f9d-155e-44ff-ad61-c8a69b7103ab',
firstName: undefined,
groups: ['1561977e-3443-4ccf-a591-69686275d7d2'],
lastName: undefined,
manager: undefined,
ownerId: 'ownerId',
phoneNumbers: undefined,
sipAddresses: undefined,
},
]);
});

it('test resolveContacts function encountering an error', () => {
const contact = contactClient['resolveCloudContacts'](
{userId: mockContactMinimum},
mockSCIMMinListResponse
);

expect(contact).toEqual(null);
});
});
26 changes: 13 additions & 13 deletions packages/calling/src/Contacts/ContactsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ import {
} from './types';

import {scimQuery, serviceErrorCodeHandler} from '../common/Utils';
import Logger from '../Logger';
import ExtendedError from '../Errors/catalog/ExtendedError';
import {ERROR_TYPE} from '../Errors/types';

/**
* `ContactsClient` module is designed to offer a set of APIs for retrieving and updating contacts and groups from the contacts-service.
Expand Down Expand Up @@ -264,6 +261,10 @@ export class ContactsClient implements IContacts {
contactsDataMap: ContactIdContactInfo,
inputList: SCIMListResponse
): Contact[] | null {
const loggerContext = {
file: CONTACTS_FILE,
method: 'resolveCloudContacts',
};
const finalContactList: Contact[] = [];

try {
Expand All @@ -272,16 +273,15 @@ export class ContactsClient implements IContacts {
const filteredContact = inputList.Resources.filter((item) => item.id === contactList[n])[0];

const {displayName, emails, phoneNumbers, photos} = filteredContact;
const {sipAddresses} = filteredContact[SCIM_WEBEXIDENTITY_USER];
const firstName = filteredContact.name.givenName;
const lastName = filteredContact.name.familyName;
const manager = filteredContact[SCIM_ENTERPRISE_USER].manager.displayName;
const department = filteredContact[SCIM_ENTERPRISE_USER].department;

let avatarURL = '';
if (photos?.length) {
avatarURL = photos[0].value;
let sipAddresses;
if (filteredContact[SCIM_WEBEXIDENTITY_USER]) {
sipAddresses = filteredContact[SCIM_WEBEXIDENTITY_USER].sipAddresses;
}
const firstName = filteredContact.name?.givenName;
const lastName = filteredContact.name?.familyName;
const manager = filteredContact[SCIM_ENTERPRISE_USER]?.manager?.displayName;
const department = filteredContact[SCIM_ENTERPRISE_USER]?.department;
const avatarURL = photos?.length ? photos[0].value : '';

const {contactType, avatarUrlDomain, encryptionKeyUrl, ownerId, groups} =
contactsDataMap[contactList[n]];
Expand All @@ -307,7 +307,7 @@ export class ContactsClient implements IContacts {
finalContactList.push(cloudContact);
}
} catch (error: any) {
Logger.error(new ExtendedError(error.message, {}, ERROR_TYPE.DEFAULT), {});
log.warn('Error occurred while parsing resolved contacts', loggerContext);

return null;
}
Expand Down
30 changes: 28 additions & 2 deletions packages/calling/src/Contacts/contactFixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,21 @@ export const mockContactGroupListTwo = [
},
];

export const mockContactMinimum = {
contactId: 'userId',
contactType: 'CLOUD',
encryptionKeyUrl: 'kms://cisco.com/keys/dcf18f9d-155e-44ff-ad61-c8a69b7103ab',
groups: ['1561977e-3443-4ccf-a591-69686275d7d2'],
ownerId: 'ownerId',
};

export const scimUserMinimum = {
schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'],
id: 'userId',
userName: 'userName',
userType: 'user',
};

const scimUser1 = {
schemas: [
'urn:ietf:params:scim:schemas:core:2.0:User',
Expand Down Expand Up @@ -448,13 +463,24 @@ export const mockSCIMListResponse = {
statusCode: 200,
body: {
schemas: ['urn:ietf:params:scim:api:messages:2.0:ListResponse'],
totalResults: 11,
itemsPerPage: 11,
totalResults: 2,
itemsPerPage: 2,
startIndex: 1,
Resources: [scimUser1, scimUser2NoPhoto],
},
};

export const mockSCIMMinListResponse = {
statusCode: 200,
body: {
schemas: ['urn:ietf:params:scim:api:messages:2.0:ListResponse'],
totalResults: 1,
itemsPerPage: 1,
startIndex: 1,
Resources: [scimUserMinimum],
},
};

export const mockKmsKey = {
uri: 'kms://kms-cisco.wbx2.com/keys/16095024-612d-4424-ba51-57cad2402e14',
};
Expand Down
4 changes: 2 additions & 2 deletions packages/calling/src/Contacts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type Contact = {
/**
* Unique identifier of the contact.
*/
contactId?: string;
contactId: string;
/**
* Indicates the type of the contact, can be `CLOUD` or `CUSTOM`.
*/
Expand All @@ -41,7 +41,7 @@ export type Contact = {
/**
* This represents the display name of the contact.
*/
displayName: string;
displayName?: string;
/**
* This represents the array of different email addresses of the contact.
*/
Expand Down
24 changes: 24 additions & 0 deletions packages/calling/src/common/Utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getSamplePeopleListResponse,
getSampleRawAndParsedMediaStats,
getMobiusDiscoveryResponse,
getSampleMinimumScimResponse,
} from './testUtil';
import {
CallDirection,
Expand Down Expand Up @@ -1048,6 +1049,29 @@ describe('resolveContact tests', () => {
});
});

it('Resolve with minimal response from SCIM', () => {
const callingPartyInfo = {} as CallingPartyInfo;
const scimResponse = getSampleMinimumScimResponse();

// scimResponse.Resources[0].photos = [];
const webexSpy = jest.spyOn(webex, 'request').mockResolvedValue({
statusCode: 200,
body: scimResponse,
});

callingPartyInfo.userExternalId = {$: 'userExternalId'};
resolveContact(callingPartyInfo).then((displayInfo) => {
expect(displayInfo?.name).toBeUndefined();
expect(displayInfo?.num).toBeUndefined();
expect(displayInfo?.avatarSrc).toStrictEqual('unknown');
expect(displayInfo?.id).toStrictEqual(getSampleMinimumScimResponse().Resources[0].id);

const query = scimUrl + encodeURIComponent(`id eq "${callingPartyInfo.userExternalId?.$}"`);

expect(webexSpy).toBeCalledOnceWith(expect.objectContaining({uri: query}));
});
});

it('Resolve by name', () => {
const callingPartyInfo = {} as CallingPartyInfo;
const webexSpy = jest
Expand Down
6 changes: 3 additions & 3 deletions packages/calling/src/common/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1234,12 +1234,12 @@ export async function resolveCallerIdDisplay(filter: string) {

/* Pick only the primary number OR 2nd preference Work */
const numberObj =
scimResource.phoneNumbers.find((num) => num.primary) ||
scimResource.phoneNumbers.find((num) => num.type.toLowerCase() === 'work');
scimResource.phoneNumbers?.find((num) => num.primary) ||
scimResource.phoneNumbers?.find((num) => num.type.toLowerCase() === 'work');

if (numberObj) {
displayResult.num = <string>numberObj.value;
} else if (scimResource.phoneNumbers.length > 0) {
} else if (scimResource.phoneNumbers && scimResource.phoneNumbers.length > 0) {
/* When no primary number exists OR PA-ID/From failed to populate, we take the first number */
log.info('Failure to resolve caller information. Setting number as caller ID', {
file: UTILS_FILE,
Expand Down
26 changes: 26 additions & 0 deletions packages/calling/src/common/testUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,32 @@ export const getSampleScimResponse = () => {
};
};

export const getSampleMinimumScimResponse = () => {
return {
totalResults: '1',
itemsPerPage: '1',
startIndex: '1',
schemas: ['urn:scim:schemas:core:1.0'],
Resources: [
{
userName: 'atlas.test.wxcwebrtc+user8@gmail.com',
id: 'userExternalId',
meta: {
created: '2022-03-16T16:13:53.847Z',
lastModified: '2022-05-31T14:39:12.782Z',
lastLoginTime: '2022-05-31T14:39:12.780Z',
version: 'W/"66025591113"',
location:
'https://identitybts.webex.com/identity/scim/1704d30d-a131-4bc7-9449-948487643793/v1/Users/652fe0c7-05ce-4acd-8bda-9a080830187f',
organizationID: '1704d30d-a131-4bc7-9449-948487643793',
creator: '97fe25e3-d3e8-400e-856b-5b0cd5b0c790',
modifier: '8c7abf2f-0c8e-49cf-b8e4-693d4ec7daee',
},
},
],
};
};

/**
* Returns a sample people list response object.
*/
Expand Down
24 changes: 12 additions & 12 deletions packages/calling/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ interface WebexIdentityMeta {
organizationId: string;
}
interface WebexIdentityUser {
sipAddresses: URIAddress[];
meta: WebexIdentityMeta;
sipAddresses?: URIAddress[];
meta?: WebexIdentityMeta;
}

interface Manager {
Expand All @@ -231,24 +231,24 @@ interface Manager {
}

interface EnterpriseUser {
department: string;
manager: Manager;
department?: string;
manager?: Manager;
}

interface Resource {
schemas: string[];
id: string;
userName: string;
active: boolean;
name: Name;
displayName: string;
emails: URIAddress[];
active?: boolean;
name?: Name;
displayName?: string;
emails?: URIAddress[];
userType: string;
phoneNumbers: PhoneNumber[];
phoneNumbers?: PhoneNumber[];
photos?: ContactDetail[];
addresses: Address[];
[SCIM_WEBEXIDENTITY_USER]: WebexIdentityUser;
[SCIM_ENTERPRISE_USER]: EnterpriseUser;
addresses?: Address[];
[SCIM_WEBEXIDENTITY_USER]?: WebexIdentityUser;
[SCIM_ENTERPRISE_USER]?: EnterpriseUser;
}

export interface SCIMListResponse {
Expand Down

0 comments on commit d3e7e79

Please sign in to comment.