Skip to content

Commit ae546e2

Browse files
Added the ability to add filters of users, people or group properties (#1572)
Co-authored-by: Sébastien Levert <[email protected]>
1 parent e9920ef commit ae546e2

File tree

8 files changed

+227
-51
lines changed

8 files changed

+227
-51
lines changed

packages/mgt-components/src/components/mgt-people-picker/mgt-people-picker.ts

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { classMap } from 'lit-html/directives/class-map';
1111
import { repeat } from 'lit-html/directives/repeat';
1212
import { findGroups, getGroupsForGroupIds, GroupType, getGroup } from '../../graph/graph.groups';
1313
import { findPeople, getPeople, PersonType, UserType } from '../../graph/graph.people';
14-
import { findUsers, findGroupMembers, getUser, getUsersForUserIds } from '../../graph/graph.user';
14+
import { findUsers, findGroupMembers, getUser, getUsersForUserIds, getUsers } from '../../graph/graph.user';
1515
import { IDynamicPerson, ViewType } from '../../graph/types';
1616
import { Providers, ProviderState, MgtTemplatedComponent, arraysAreEqual } from '@microsoft/mgt-element';
1717
import '../../styles/style-helper';
@@ -376,6 +376,45 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
376376
this.requestStateUpdate(true);
377377
}
378378

379+
/**
380+
* Filters that can be set on the user properties query.
381+
*/
382+
@property({ attribute: 'user-filters' })
383+
public get userFilters(): string {
384+
return this._userFilters;
385+
}
386+
387+
public set userFilters(value: string) {
388+
this._userFilters = value;
389+
this.requestStateUpdate(true);
390+
}
391+
392+
/**
393+
* Filters that can be set on the people query properties.
394+
*/
395+
@property({ attribute: 'people-filters' })
396+
public get peopleFilters(): string {
397+
return this._peopleFilters;
398+
}
399+
400+
public set peopleFilters(value: string) {
401+
this._peopleFilters = value;
402+
this.requestStateUpdate(true);
403+
}
404+
405+
/**
406+
* Filters that can be set on the group query properties.
407+
*/
408+
@property({ attribute: 'group-filters' })
409+
public get groupFilters(): string {
410+
return this._groupFilters;
411+
}
412+
413+
public set groupFilters(value: string) {
414+
this._groupFilters = value;
415+
this.requestStateUpdate(true);
416+
}
417+
379418
/**
380419
* Get the scopes required for people picker
381420
*
@@ -406,6 +445,9 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
406445
private _groupType: GroupType = GroupType.any;
407446
private _userType: UserType = UserType.any;
408447
private _currentSelectedUser: IDynamicPerson;
448+
private _userFilters: string;
449+
private _groupFilters: string;
450+
private _peopleFilters: string;
409451

410452
private defaultPeople: IDynamicPerson[];
411453

@@ -915,7 +957,8 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
915957
this.groupId,
916958
this.showMax,
917959
this.type,
918-
this.transitiveSearch
960+
this.transitiveSearch,
961+
this._groupFilters
919962
);
920963
} catch (_) {
921964
this._groupPeople = [];
@@ -924,13 +967,18 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
924967
people = this._groupPeople || [];
925968
} else if (this.type === PersonType.person || this.type === PersonType.any) {
926969
if (this.userIds) {
927-
people = await getUsersForUserIds(graph, this.userIds);
970+
people = await getUsersForUserIds(graph, this.userIds, '', this.userFilters);
928971
} else {
929-
people = await getPeople(graph, this.userType);
972+
const isUserOrContactType = this.userType === UserType.user || this.userType === UserType.contact;
973+
if (this._userFilters && isUserOrContactType) {
974+
people = await getUsers(graph, this._userFilters, this.showMax);
975+
} else {
976+
people = await getPeople(graph, this.userType, this._peopleFilters);
977+
}
930978
}
931979
} else if (this.type === PersonType.group) {
932-
let groups = (await findGroups(graph, '', this.showMax, this.groupType)) || [];
933-
if (groups[0]['value']) {
980+
let groups = (await findGroups(graph, '', this.showMax, this.groupType, this._groupFilters)) || [];
981+
if (groups.length > 0 && groups[0]['value']) {
934982
groups = groups[0]['value'];
935983
}
936984
people = groups;
@@ -945,8 +993,12 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
945993
!this.selectedPeople.length &&
946994
!this.defaultSelectedUsers
947995
) {
948-
this.defaultSelectedUsers = await getUsersForUserIds(graph, this.defaultSelectedUserIds);
949-
this.defaultSelectedGroups = await getGroupsForGroupIds(graph, this.defaultSelectedGroupIds);
996+
this.defaultSelectedUsers = await getUsersForUserIds(graph, this.defaultSelectedUserIds, '', this._userFilters);
997+
this.defaultSelectedGroups = await getGroupsForGroupIds(
998+
graph,
999+
this.defaultSelectedGroupIds,
1000+
this._groupFilters
1001+
);
9501002

9511003
this.defaultSelectedGroups = this.defaultSelectedGroups.filter(group => {
9521004
return group !== null;
@@ -966,22 +1018,39 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
9661018

9671019
if (this.groupId) {
9681020
people =
969-
(await findGroupMembers(graph, input, this.groupId, this.showMax, this.type, this.transitiveSearch)) || [];
1021+
(await findGroupMembers(
1022+
graph,
1023+
input,
1024+
this.groupId,
1025+
this.showMax,
1026+
this.type,
1027+
this.transitiveSearch,
1028+
this._groupFilters
1029+
)) || [];
9701030
} else {
9711031
if (this.type === PersonType.person || this.type === PersonType.any) {
9721032
try {
973-
if (this.userIds && this.userIds.length) {
974-
people = await getUsersForUserIds(graph, this.userIds, input);
1033+
// Default UserType === any
1034+
if (this.userType === UserType.contact || this.userType === UserType.user) {
1035+
// we might have a user-filters property set, search for users with it.
1036+
if (this.userIds && this.userIds.length) {
1037+
// has the user-ids proerty set
1038+
people = await getUsersForUserIds(graph, this.userIds, input, this._userFilters);
1039+
} else {
1040+
people = await findUsers(graph, input, this.showMax, this._userFilters);
1041+
}
9751042
} else {
976-
people = (await findPeople(graph, input, this.showMax, this.userType)) || [];
1043+
people = (await findPeople(graph, input, this.showMax, this.userType, this._peopleFilters)) || [];
9771044
}
9781045
} catch (e) {
9791046
// nop
9801047
}
9811048

982-
if (people.length < this.showMax && this.userType !== UserType.contact) {
1049+
// Don't follow this path if a people-filters attribute is set on the component as the
1050+
// default type === PersonType.person
1051+
if (people.length < this.showMax && this.userType !== UserType.contact && this.type !== PersonType.person) {
9831052
try {
984-
const users = (await findUsers(graph, input, this.showMax)) || [];
1053+
const users = (await findUsers(graph, input, this.showMax, this._userFilters)) || [];
9851054

9861055
// make sure only unique people
9871056
const peopleIds = new Set(people.map(p => p.id));
@@ -995,10 +1064,11 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
9951064
}
9961065
}
9971066
}
1067+
9981068
if ((this.type === PersonType.group || this.type === PersonType.any) && people.length < this.showMax) {
9991069
let groups = [];
10001070
try {
1001-
groups = (await findGroups(graph, input, this.showMax, this.groupType)) || [];
1071+
groups = (await findGroups(graph, input, this.showMax, this.groupType, this._groupFilters)) || [];
10021072
people = people.concat(groups);
10031073
} catch (e) {
10041074
// nop
@@ -1007,7 +1077,6 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
10071077
}
10081078
}
10091079
}
1010-
10111080
//people = this.getUniquePeople(people);
10121081
this._foundPeople = this.filterPeople(people);
10131082
}
@@ -1606,7 +1675,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
16061675
// check if people need to be updated
16071676
// ensuring people list is displayed
16081677
// find ids from selected people
1609-
if (people) {
1678+
if (people && people.length > 0) {
16101679
const idFilter = this.selectedPeople.map(el => {
16111680
return el.id ? el.id : el.displayName;
16121681
});

packages/mgt-components/src/components/mgt-people/mgt-people.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ export class MgtPeople extends MgtTemplatedComponent {
325325
* @memberof MgtPeople
326326
*/
327327
protected renderPeople(): TemplateResult {
328-
const maxPeople = this.people.slice(0, this.showMax);
328+
const maxPeople = this.people.slice(0, this.showMax).filter(pple => pple);
329329
return html`
330330
<ul class="people-list" aria-label="people">
331331
${repeat(

packages/mgt-components/src/graph/cacheStores.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ export const schemas = {
2020
name: 'users',
2121
stores: {
2222
users: 'users',
23-
usersQuery: 'usersQuery'
23+
usersQuery: 'usersQuery',
24+
userFilters: 'userFilters'
2425
},
25-
version: 1
26+
version: 2
2627
},
2728
photos: {
2829
name: 'photos',
@@ -40,15 +41,15 @@ export const schemas = {
4041
groupPeople: 'groupPeople',
4142
peopleQuery: 'peopleQuery'
4243
},
43-
version: 1
44+
version: 2
4445
},
4546
groups: {
4647
name: 'groups',
4748
stores: {
4849
groups: 'groups',
4950
groupsQuery: 'groupsQuery'
5051
},
51-
version: 2
52+
version: 3
5253
},
5354
get: {
5455
name: 'responses',

packages/mgt-components/src/graph/graph.groups.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,13 @@ export async function findGroups(
9595
graph: IGraph,
9696
query: string,
9797
top: number = 10,
98-
groupTypes: GroupType = GroupType.any
98+
groupTypes: GroupType = GroupType.any,
99+
groupFilters: string = ''
99100
): Promise<Group[]> {
100101
const scopes = 'Group.Read.All';
101102

102103
let cache: CacheStore<CacheGroupQuery>;
103-
const key = query || '*' + groupTypes;
104+
const key = `${query ? query : '*'}*${groupTypes}*${groupFilters}`;
104105

105106
if (getIsGroupsCacheEnabled()) {
106107
cache = CacheService.getCache(schemas.groups, schemas.groups.stores.groupsQuery);
@@ -122,6 +123,10 @@ export async function findGroups(
122123
filterQuery = `(startswith(displayName,'${query}') or startswith(mailNickname,'${query}') or startswith(mail,'${query}'))`;
123124
}
124125

126+
if (groupFilters) {
127+
filterQuery += `${query ? ' and ' : ''}${groupFilters}`;
128+
}
129+
125130
if (groupTypes !== GroupType.any) {
126131
const batch = graph.createBatch();
127132

@@ -147,10 +152,7 @@ export async function findGroups(
147152
filterGroups.push('(mailEnabled eq true and securityEnabled eq false)');
148153
}
149154

150-
if (query !== '') {
151-
filterQuery = filterQuery + ' and ';
152-
}
153-
155+
filterQuery += `${filterQuery} and `;
154156
for (let filter of filterGroups) {
155157
batch.get(filter, `/groups?$filter=${filterQuery + filter}`, ['Group.Read.All']);
156158
}
@@ -176,8 +178,10 @@ export async function findGroups(
176178
queries.push(
177179
await graph
178180
.api('groups')
179-
.filter(filterQuery + filter)
181+
.filter(`${filterQuery} and ${filter}`)
180182
.top(top)
183+
.count(true)
184+
.header('ConsistencyLevel', 'eventual')
181185
.middlewareOptions(prepScopes(scopes))
182186
.get()
183187
);
@@ -189,8 +193,14 @@ export async function findGroups(
189193
}
190194
} else {
191195
if (batchedResult.length === 0) {
192-
const result = await graph.api('groups').filter(filterQuery).top(top).middlewareOptions(prepScopes(scopes)).get();
193-
196+
const result = await graph
197+
.api('groups')
198+
.filter(filterQuery)
199+
.top(top)
200+
.count(true)
201+
.header('ConsistencyLevel', 'eventual')
202+
.middlewareOptions(prepScopes(scopes))
203+
.get();
194204
if (getIsGroupsCacheEnabled() && result) {
195205
cache.putValue(key, { groups: result.value.map(x => JSON.stringify(x)), top: top });
196206
}
@@ -336,7 +346,11 @@ export async function getGroup(graph: IGraph, id: string, requestedProps?: strin
336346
* @param {string[]} groupIds, an array of string ids
337347
* @returns {Promise<Group[]>}
338348
*/
339-
export async function getGroupsForGroupIds(graph: IGraph, groupIds: string[]): Promise<Group[]> {
349+
export async function getGroupsForGroupIds(
350+
graph: IGraph,
351+
groupIds: string[],
352+
groupFilters: string = ''
353+
): Promise<Group[]> {
340354
if (!groupIds || groupIds.length === 0) {
341355
return [];
342356
}
@@ -358,7 +372,11 @@ export async function getGroupsForGroupIds(graph: IGraph, groupIds: string[]): P
358372
if (group && getGroupsInvalidationTime() > Date.now() - group.timeCached) {
359373
groupDict[id] = group.group ? JSON.parse(group.group) : null;
360374
} else if (id !== '') {
361-
batch.get(id, `/groups/${id}`, ['Group.Read.All']);
375+
let apiUrl: string = `/groups/${id}`;
376+
if (groupFilters) {
377+
apiUrl += `${apiUrl}?$filters=${groupFilters}`;
378+
}
379+
batch.get(id, apiUrl, ['Group.Read.All']);
362380
notInCache.push(id);
363381
}
364382
}

packages/mgt-components/src/graph/graph.people.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ export async function findPeople(
105105
graph: IGraph,
106106
query: string,
107107
top: number = 10,
108-
userType: UserType = UserType.any
108+
userType: UserType = UserType.any,
109+
peopleFilters: string = ''
109110
): Promise<Person[]> {
110111
const scopes = 'people.read';
111112

@@ -130,6 +131,11 @@ export async function findPeople(
130131
}
131132
}
132133

134+
if (peopleFilters !== '') {
135+
// Adding the default people filters to the search filters
136+
filter = `${filter} and ${peopleFilters}`;
137+
}
138+
133139
let graphResult;
134140
try {
135141
graphResult = await graph
@@ -155,11 +161,15 @@ export async function findPeople(
155161
* @returns {(Promise<Person[]>)}
156162
* @memberof Graph
157163
*/
158-
export async function getPeople(graph: IGraph, userType: UserType = UserType.any): Promise<Person[]> {
164+
export async function getPeople(
165+
graph: IGraph,
166+
userType: UserType = UserType.any,
167+
peopleFilters: string = ''
168+
): Promise<Person[]> {
159169
const scopes = 'people.read';
160170

161171
let cache: CacheStore<CachePeopleQuery>;
162-
let cacheKey = `*:${userType}`;
172+
let cacheKey = peopleFilters ? peopleFilters : `*:${userType}`;
163173

164174
if (getIsPeopleCacheEnabled()) {
165175
cache = CacheService.getCache<CachePeopleQuery>(schemas.people, schemas.people.stores.peopleQuery);
@@ -172,7 +182,6 @@ export async function getPeople(graph: IGraph, userType: UserType = UserType.any
172182

173183
const uri = '/me/people';
174184
let filter = "personType/class eq 'Person'";
175-
176185
if (userType !== UserType.any) {
177186
if (userType === UserType.user) {
178187
filter += "and personType/subclass eq 'OrganizationUser'";
@@ -181,13 +190,17 @@ export async function getPeople(graph: IGraph, userType: UserType = UserType.any
181190
}
182191
}
183192

193+
if (peopleFilters) {
194+
filter += ` and ${peopleFilters}`;
195+
}
196+
184197
let people;
185198
try {
186199
people = await graph.api(uri).middlewareOptions(prepScopes(scopes)).filter(filter).get();
187200
if (getIsPeopleCacheEnabled() && people) {
188201
cache.putValue(cacheKey, { maxResults: 10, results: people.value.map(ppl => JSON.stringify(ppl)) });
189202
}
190-
} catch (error) {}
203+
} catch (_) {}
191204
return people ? people.value : null;
192205
}
193206

0 commit comments

Comments
 (0)