Merge pull request #9921 from Bonial-International-GmbH/PJ_msgraph_advanced-queries

feat(msgraph): support advanced querying capabilities
This commit is contained in:
Fredrik Adelöw
2022-03-14 14:02:28 +01:00
committed by GitHub
10 changed files with 454 additions and 79 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-msgraph': patch
---
support advanced querying capabilities using the config option `queryMode`
@@ -35,6 +35,11 @@ catalog:
# the App registration in the Microsoft Azure Portal.
clientId: ${MICROSOFT_GRAPH_CLIENT_ID}
clientSecret: ${MICROSOFT_GRAPH_CLIENT_SECRET_TOKEN}
# Optional mode for querying which defaults to "basic".
# By default, the Microsoft Graph API only provides the basic feature set
# for querying. Certain features are limited to advanced querying capabilities.
# (See https://docs.microsoft.com/en-us/graph/aad-advanced-queries)
queryMode: basic # basic | advanced
# Optional parameter to include the expanded resource or collection referenced
# by a single relationship (navigation property) in your results.
# Only one relationship can be expanded in a single request.
@@ -70,7 +70,10 @@ export class MicrosoftGraphClient {
groupId: string,
maxSize: number,
): Promise<string | undefined>;
getGroups(query?: ODataQuery): AsyncIterable<MicrosoftGraph.Group>;
getGroups(
query?: ODataQuery,
queryMode?: 'basic' | 'advanced',
): AsyncIterable<MicrosoftGraph.Group>;
getOrganization(tenantId: string): Promise<MicrosoftGraph.Organization>;
// (undocumented)
getUserPhoto(userId: string, sizeId?: string): Promise<string | undefined>;
@@ -82,13 +85,20 @@ export class MicrosoftGraphClient {
userId: string,
query?: ODataQuery,
): Promise<MicrosoftGraph.User>;
getUsers(query?: ODataQuery): AsyncIterable<MicrosoftGraph.User>;
getUsers(
query?: ODataQuery,
queryMode?: 'basic' | 'advanced',
): AsyncIterable<MicrosoftGraph.User>;
requestApi(
path: string,
query?: ODataQuery,
headers?: Record<string, string>,
): Promise<Response_2>;
requestCollection<T>(path: string, query?: ODataQuery): AsyncIterable<T>;
requestCollection<T>(
path: string,
query?: ODataQuery,
queryMode?: 'basic' | 'advanced',
): AsyncIterable<T>;
requestRaw(
url: string,
headers?: Record<string, string>,
@@ -167,6 +177,7 @@ export type MicrosoftGraphProviderConfig = {
groupExpand?: string;
groupFilter?: string;
groupSearch?: string;
queryMode?: 'basic' | 'advanced';
};
// @public
@@ -178,6 +189,7 @@ export type ODataQuery = {
filter?: string;
expand?: string;
select?: string[];
count?: boolean;
};
// @public
@@ -202,6 +214,7 @@ export function readMicrosoftGraphOrg(
groupExpand?: string;
groupSearch?: string;
groupFilter?: string;
queryMode?: 'basic' | 'advanced';
userTransformer?: UserTransformer;
groupTransformer?: GroupTransformer;
organizationTransformer?: OrganizationTransformer;
@@ -24,6 +24,7 @@ import { MicrosoftGraphProviderConfig } from './config';
* OData (Open Data Protocol) Query
*
* {@link https://docs.microsoft.com/en-us/odata/concepts/queryoptions-overview}
* {@link https://docs.microsoft.com/en-us/graph/query-parameters}
* @public
*/
export type ODataQuery = {
@@ -43,6 +44,10 @@ export type ODataQuery = {
* request a specific set of properties for each entity or complex type
*/
select?: string[];
/**
* Retrieves the total count of matching resources.
*/
count?: boolean;
};
/**
@@ -100,19 +105,34 @@ export class MicrosoftGraphClient {
* @public
* @param path - Resource in Microsoft Graph
* @param query - OData Query {@link ODataQuery}
*
* @param queryMode - Mode to use while querying. Some features are only available at "advanced".
*/
async *requestCollection<T>(
path: string,
query?: ODataQuery,
queryMode?: 'basic' | 'advanced',
): AsyncIterable<T> {
const headers: Record<string, string> = query?.search
? {
// Eventual consistency is required to use $search.
// If a new user/group is not found, it'll eventually be imported on a subsequent read
ConsistencyLevel: 'eventual',
}
: {};
// upgrade to advanced query mode transparently when "search" is used
// to stay backwards compatible.
const appliedQueryMode = query?.search ? 'advanced' : queryMode ?? 'basic';
// not needed for "search"
// as of https://docs.microsoft.com/en-us/graph/aad-advanced-queries?tabs=http
// even though a few other places say the opposite
// - https://docs.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http#request-headers
// - https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
if (appliedQueryMode === 'advanced' && (query?.filter || query?.select)) {
query.count = true;
}
const headers: Record<string, string> =
appliedQueryMode === 'advanced'
? {
// Eventual consistency is required for advanced querying capabilities
// like "$search" or parts of "$filter".
// If a new user/group is not found, it'll eventually be imported on a subsequent read
ConsistencyLevel: 'eventual',
}
: {};
let response = await this.requestApi(path, query, headers);
@@ -156,6 +176,7 @@ export class MicrosoftGraphClient {
$filter: query?.filter,
$select: query?.select?.join(','),
$expand: query?.expand,
$count: query?.count,
},
{
addQueryPrefix: true,
@@ -248,10 +269,17 @@ export class MicrosoftGraphClient {
*
* @public
* @param query - OData Query {@link ODataQuery}
*
* @param queryMode - Mode to use while querying. Some features are only available at "advanced".
*/
async *getUsers(query?: ODataQuery): AsyncIterable<MicrosoftGraph.User> {
yield* this.requestCollection<MicrosoftGraph.User>(`users`, query);
async *getUsers(
query?: ODataQuery,
queryMode?: 'basic' | 'advanced',
): AsyncIterable<MicrosoftGraph.User> {
yield* this.requestCollection<MicrosoftGraph.User>(
`users`,
query,
queryMode,
);
}
/**
@@ -280,12 +308,20 @@ export class MicrosoftGraphClient {
* Get a collection of
* {@link https://docs.microsoft.com/en-us/graph/api/resources/group | Group}
* from Graph API and return as `AsyncIterable`
*
* @public
* @param query - OData Query {@link ODataQuery}
*
* @param queryMode - Mode to use while querying. Some features are only available at "advanced".
*/
async *getGroups(query?: ODataQuery): AsyncIterable<MicrosoftGraph.Group> {
yield* this.requestCollection<MicrosoftGraph.Group>(`groups`, query);
async *getGroups(
query?: ODataQuery,
queryMode?: 'basic' | 'advanced',
): AsyncIterable<MicrosoftGraph.Group> {
yield* this.requestCollection<MicrosoftGraph.Group>(
`groups`,
query,
queryMode,
);
}
/**
@@ -93,4 +93,21 @@ describe('readMicrosoftGraphConfig', () => {
};
expect(() => readMicrosoftGraphConfig(new ConfigReader(config))).toThrow();
});
it('should fail if both userFilter and userGroupMemberSearch are set', () => {
const config = {
providers: [
{
target: 'target',
tenantId: 'tenantId',
clientId: 'clientId',
clientSecret: 'clientSecret',
authority: 'https://login.example.com/',
userFilter: 'accountEnabled eq true',
userGroupMemberSearch: 'any',
},
],
};
expect(() => readMicrosoftGraphConfig(new ConfigReader(config))).toThrow();
});
});
@@ -53,7 +53,7 @@ export type MicrosoftGraphProviderConfig = {
*/
userFilter?: string;
/**
* The expand argument to apply to users.
* The "expand" argument to apply to users.
*
* E.g. "manager"
*/
@@ -88,6 +88,15 @@ export type MicrosoftGraphProviderConfig = {
* E.g. "\"displayName:-team\"" would only match groups which contain '-team'
*/
groupSearch?: string;
/**
* By default, the Microsoft Graph API only provides the basic feature set
* for querying. Certain features are limited to advanced query capabilities
* (see https://docs.microsoft.com/en-us/graph/aad-advanced-queries)
* and need to be enabled.
*
* Some features like `$expand` are not available for advanced queries, though.
*/
queryMode?: 'basic' | 'advanced';
};
/**
@@ -136,6 +145,15 @@ export function readMicrosoftGraphConfig(
);
}
const queryMode = providerConfig.getOptionalString('queryMode');
if (
queryMode !== undefined &&
queryMode !== 'basic' &&
queryMode !== 'advanced'
) {
throw new Error(`queryMode must be one of: basic, advanced`);
}
providers.push({
target,
authority,
@@ -149,6 +167,7 @@ export function readMicrosoftGraphConfig(
groupExpand,
groupFilter,
groupSearch,
queryMode,
});
}
@@ -111,9 +111,62 @@ describe('read microsoft graph', () => {
]);
expect(client.getUsers).toBeCalledTimes(1);
expect(client.getUsers).toBeCalledWith({
filter: 'accountEnabled eq true',
expect(client.getUsers).toBeCalledWith(
{
filter: 'accountEnabled eq true',
},
undefined,
);
expect(client.getUserPhotoWithSizeLimit).toBeCalledTimes(1);
expect(client.getUserPhotoWithSizeLimit).toBeCalledWith('userid', 120);
});
it('should read users with advanced query mode', async () => {
async function* getExampleUsers() {
yield {
id: 'userid',
displayName: 'User Name',
mail: 'user.name@example.com',
};
}
client.getUsers.mockImplementation(getExampleUsers);
client.getUserPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
const { users } = await readMicrosoftGraphUsers(client, {
queryMode: 'advanced',
userFilter: 'accountEnabled eq true',
logger: getVoidLogger(),
});
expect(users).toEqual([
user({
metadata: {
annotations: {
'graph.microsoft.com/user-id': 'userid',
},
name: 'user.name_example.com',
},
spec: {
profile: {
displayName: 'User Name',
email: 'user.name@example.com',
picture: 'data:image/jpeg;base64,...',
},
memberOf: [],
},
}),
]);
expect(client.getUsers).toBeCalledTimes(1);
expect(client.getUsers).toBeCalledWith(
{
filter: 'accountEnabled eq true',
},
'advanced',
);
expect(client.getUserPhotoWithSizeLimit).toBeCalledTimes(1);
expect(client.getUserPhotoWithSizeLimit).toBeCalledWith('userid', 120);
});
@@ -154,10 +207,13 @@ describe('read microsoft graph', () => {
]);
expect(client.getUsers).toBeCalledTimes(1);
expect(client.getUsers).toBeCalledWith({
expand: 'manager',
filter: 'accountEnabled eq true',
});
expect(client.getUsers).toBeCalledWith(
{
expand: 'manager',
filter: 'accountEnabled eq true',
},
undefined,
);
expect(client.getUserPhotoWithSizeLimit).toBeCalledTimes(1);
expect(client.getUserPhotoWithSizeLimit).toBeCalledWith('userid', 120);
});
@@ -222,9 +278,88 @@ describe('read microsoft graph', () => {
]);
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq true',
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq true',
},
undefined,
);
expect(client.getGroupMembers).toBeCalledTimes(1);
expect(client.getGroupMembers).toBeCalledWith('groupid');
expect(client.getUserProfile).toBeCalledTimes(1);
expect(client.getUserProfile).toBeCalledWith('userid', {
expand: undefined,
});
expect(client.getUserPhotoWithSizeLimit).toBeCalledTimes(1);
expect(client.getUserPhotoWithSizeLimit).toBeCalledWith('userid', 120);
});
it('should read users from Groups with advanced query mode', async () => {
async function* getExampleGroups() {
yield {
id: 'groupid',
displayName: 'Group Name',
description: 'Group Description',
mail: 'group@example.com',
};
}
async function* getExampleGroupMembers(): AsyncIterable<GroupMember> {
yield {
'@odata.type': '#microsoft.graph.group',
id: 'childgroupid',
};
yield {
'@odata.type': '#microsoft.graph.user',
id: 'userid',
};
}
client.getGroups.mockImplementation(getExampleGroups);
client.getGroupMembers.mockImplementation(getExampleGroupMembers);
client.getUserProfile.mockResolvedValue({
id: 'userid',
displayName: 'User Name',
mail: 'user.name@example.com',
});
client.getUserPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
const { users } = await readMicrosoftGraphUsersInGroups(client, {
queryMode: 'advanced',
userGroupMemberFilter: 'securityEnabled eq true',
logger: getVoidLogger(),
});
expect(users).toEqual([
user({
metadata: {
annotations: {
'graph.microsoft.com/user-id': 'userid',
},
name: 'user.name_example.com',
},
spec: {
profile: {
displayName: 'User Name',
email: 'user.name@example.com',
picture: 'data:image/jpeg;base64,...',
},
memberOf: [],
},
}),
]);
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq true',
},
'advanced',
);
expect(client.getGroupMembers).toBeCalledTimes(1);
expect(client.getGroupMembers).toBeCalledWith('groupid');
@@ -292,10 +427,13 @@ describe('read microsoft graph', () => {
]);
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
expand: 'member',
filter: 'securityEnabled eq true',
});
expect(client.getGroups).toBeCalledWith(
{
expand: 'member',
filter: 'securityEnabled eq true',
},
undefined,
);
expect(client.getGroupMembers).toBeCalledTimes(1);
expect(client.getGroupMembers).toBeCalledWith('groupid');
@@ -444,9 +582,108 @@ describe('read microsoft graph', () => {
expect(groupMember.get('organization_name')).toEqual(new Set());
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq false',
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq false',
},
undefined,
);
expect(client.getGroupMembers).toBeCalledTimes(1);
expect(client.getGroupMembers).toBeCalledWith('groupid');
// TODO: Loading groups photos doesn't work right now as Microsoft Graph
// doesn't allows this yet
// expect(client.getGroupPhotoWithSizeLimit).toBeCalledTimes(1);
// expect(client.getGroupPhotoWithSizeLimit).toBeCalledWith('groupid', 120);
});
it('should read groups with advanced query mode', async () => {
async function* getExampleGroups() {
yield {
id: 'groupid',
displayName: 'Group Name',
description: 'Group Description',
mail: 'group@example.com',
};
}
async function* getExampleGroupMembers(): AsyncIterable<GroupMember> {
yield {
'@odata.type': '#microsoft.graph.group',
id: 'childgroupid',
};
yield {
'@odata.type': '#microsoft.graph.user',
id: 'userid',
};
}
client.getGroups.mockImplementation(getExampleGroups);
client.getGroupMembers.mockImplementation(getExampleGroupMembers);
client.getOrganization.mockResolvedValue({
id: 'tenantid',
displayName: 'Organization Name',
});
client.getGroupPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
const { groups, groupMember, groupMemberOf, rootGroup } =
await readMicrosoftGraphGroups(client, 'tenantid', {
queryMode: 'advanced',
groupFilter: 'securityEnabled eq false',
});
const expectedRootGroup = group({
metadata: {
annotations: {
'graph.microsoft.com/tenant-id': 'tenantid',
},
name: 'organization_name',
description: 'Organization Name',
},
spec: {
type: 'root',
profile: {
displayName: 'Organization Name',
},
children: [],
},
});
expect(groups).toEqual([
expectedRootGroup,
group({
metadata: {
annotations: {
'graph.microsoft.com/group-id': 'groupid',
},
name: 'group_name',
description: 'Group Description',
},
spec: {
type: 'team',
profile: {
displayName: 'Group Name',
email: 'group@example.com',
// TODO: Loading groups photos doesn't work right now as Microsoft
// Graph doesn't allows this yet
/* picture: 'data:image/jpeg;base64,...',*/
},
children: [],
},
}),
]);
expect(rootGroup).toEqual(expectedRootGroup);
expect(groupMember.get('groupid')).toEqual(new Set(['childgroupid']));
expect(groupMemberOf.get('userid')).toEqual(new Set(['groupid']));
expect(groupMember.get('organization_name')).toEqual(new Set());
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq false',
},
'advanced',
);
expect(client.getGroupMembers).toBeCalledTimes(1);
expect(client.getGroupMembers).toBeCalledWith('groupid');
// TODO: Loading groups photos doesn't work right now as Microsoft Graph
@@ -537,10 +774,13 @@ describe('read microsoft graph', () => {
expect(groupMember.get('organization_name')).toEqual(new Set());
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
expand: 'member',
filter: 'securityEnabled eq false',
});
expect(client.getGroups).toBeCalledWith(
{
expand: 'member',
filter: 'securityEnabled eq false',
},
undefined,
);
expect(client.getGroupMembers).toBeCalledTimes(1);
expect(client.getGroupMembers).toBeCalledWith('groupid');
// TODO: Loading groups photos doesn't work right now as Microsoft Graph
@@ -628,9 +868,12 @@ describe('read microsoft graph', () => {
}),
]);
expect(rootGroup).toEqual(expectedRootGroup);
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq true',
});
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq true',
},
undefined,
);
expect(client.getGroupMembers).toBeCalledTimes(1);
expect(client.getGroupMembers).toBeCalledWith('groupid');
});
@@ -788,13 +1031,19 @@ describe('read microsoft graph', () => {
});
expect(client.getUsers).toBeCalledTimes(1);
expect(client.getUsers).toBeCalledWith({
filter: undefined,
});
expect(client.getUsers).toBeCalledWith(
{
filter: undefined,
},
undefined,
);
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq false',
});
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq false',
},
undefined,
);
});
it('should read users using userExpand and userFilter', async () => {
@@ -822,14 +1071,20 @@ describe('read microsoft graph', () => {
});
expect(client.getUsers).toBeCalledTimes(1);
expect(client.getUsers).toBeCalledWith({
expand: 'manager',
filter: 'accountEnabled eq true',
});
expect(client.getUsers).toBeCalledWith(
{
expand: 'manager',
filter: 'accountEnabled eq true',
},
undefined,
);
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq false',
});
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq false',
},
undefined,
);
});
it('should read users using userExpand and userGroupMemberFilter', async () => {
@@ -858,12 +1113,18 @@ describe('read microsoft graph', () => {
expect(client.getUsers).toBeCalledTimes(0);
expect(client.getGroups).toBeCalledTimes(2);
expect(client.getGroups).toBeCalledWith({
filter: 'name eq backstage-group',
});
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq false',
});
expect(client.getGroups).toBeCalledWith(
{
filter: 'name eq backstage-group',
},
undefined,
);
expect(client.getGroups).toBeCalledWith(
{
filter: 'securityEnabled eq false',
},
undefined,
);
expect(client.getUserProfile).toBeCalledTimes(1);
expect(client.getUserPhotoWithSizeLimit).toBeCalledTimes(1);
});
@@ -84,6 +84,7 @@ export async function defaultUserTransformer(
export async function readMicrosoftGraphUsers(
client: MicrosoftGraphClient,
options: {
queryMode?: 'basic' | 'advanced';
userFilter?: string;
userExpand?: string;
transformer?: UserTransformer;
@@ -95,13 +96,16 @@ export async function readMicrosoftGraphUsers(
const users: UserEntity[] = [];
const limiter = limiterFactory(10);
const transformer = options?.transformer ?? defaultUserTransformer;
const transformer = options.transformer ?? defaultUserTransformer;
const promises: Promise<void>[] = [];
for await (const user of client.getUsers({
filter: options.userFilter,
expand: options.userExpand,
})) {
for await (const user of client.getUsers(
{
filter: options.userFilter,
expand: options.userExpand,
},
options.queryMode,
)) {
// Process all users in parallel, otherwise it can take quite some time
promises.push(
limiter(async () => {
@@ -137,6 +141,7 @@ export async function readMicrosoftGraphUsers(
export async function readMicrosoftGraphUsersInGroups(
client: MicrosoftGraphClient,
options: {
queryMode?: 'basic' | 'advanced';
userExpand?: string;
userGroupMemberSearch?: string;
userGroupMemberFilter?: string;
@@ -157,11 +162,14 @@ export async function readMicrosoftGraphUsersInGroups(
const groupMemberUsers: Set<string> = new Set();
for await (const group of client.getGroups({
expand: options.groupExpand,
search: options.userGroupMemberSearch,
filter: options.userGroupMemberFilter,
})) {
for await (const group of client.getGroups(
{
expand: options.groupExpand,
search: options.userGroupMemberSearch,
filter: options.userGroupMemberFilter,
},
options.queryMode,
)) {
// Process all groups in parallel, otherwise it can take quite some time
userGroupMemberPromises.push(
limiter(async () => {
@@ -331,6 +339,7 @@ export async function readMicrosoftGraphGroups(
client: MicrosoftGraphClient,
tenantId: string,
options?: {
queryMode?: 'basic' | 'advanced';
groupExpand?: string;
groupFilter?: string;
groupSearch?: string;
@@ -359,11 +368,14 @@ export async function readMicrosoftGraphGroups(
const transformer = options?.groupTransformer ?? defaultGroupTransformer;
const promises: Promise<void>[] = [];
for await (const group of client.getGroups({
expand: options?.groupExpand,
search: options?.groupSearch,
filter: options?.groupFilter,
})) {
for await (const group of client.getGroups(
{
expand: options?.groupExpand,
search: options?.groupSearch,
filter: options?.groupFilter,
},
options?.queryMode,
)) {
// Process all groups in parallel, otherwise it can take quite some time
promises.push(
limiter(async () => {
@@ -520,6 +532,7 @@ export async function readMicrosoftGraphOrg(
groupExpand?: string;
groupSearch?: string;
groupFilter?: string;
queryMode?: 'basic' | 'advanced';
userTransformer?: UserTransformer;
groupTransformer?: GroupTransformer;
organizationTransformer?: OrganizationTransformer;
@@ -528,10 +541,11 @@ export async function readMicrosoftGraphOrg(
): Promise<{ users: UserEntity[]; groups: GroupEntity[] }> {
const users: UserEntity[] = [];
if (options.userGroupMemberFilter) {
if (options.userGroupMemberFilter || options.userGroupMemberSearch) {
const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(
client,
{
queryMode: options.queryMode,
userGroupMemberFilter: options.userGroupMemberFilter,
userGroupMemberSearch: options.userGroupMemberSearch,
transformer: options.userTransformer,
@@ -541,6 +555,7 @@ export async function readMicrosoftGraphOrg(
users.push(...usersInGroups);
} else {
const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {
queryMode: options.queryMode,
userFilter: options.userFilter,
userExpand: options.userExpand,
transformer: options.userTransformer,
@@ -550,10 +565,11 @@ export async function readMicrosoftGraphOrg(
}
const { groups, rootGroup, groupMember, groupMemberOf } =
await readMicrosoftGraphGroups(client, tenantId, {
groupSearch: options?.groupSearch,
groupFilter: options?.groupFilter,
groupTransformer: options?.groupTransformer,
organizationTransformer: options?.organizationTransformer,
queryMode: options.queryMode,
groupSearch: options.groupSearch,
groupFilter: options.groupFilter,
groupTransformer: options.groupTransformer,
organizationTransformer: options.organizationTransformer,
});
resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf);
@@ -126,6 +126,7 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider {
userGroupMemberSearch: provider.userGroupMemberSearch,
groupFilter: provider.groupFilter,
groupSearch: provider.groupSearch,
queryMode: provider.queryMode,
groupTransformer: this.options.groupTransformer,
userTransformer: this.options.userTransformer,
organizationTransformer: this.options.organizationTransformer,
@@ -73,6 +73,7 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
this.groupTransformer = options.groupTransformer;
this.organizationTransformer = options.organizationTransformer;
}
getProcessorName(): string {
return 'MicrosoftGraphOrgReaderProcessor';
}
@@ -95,7 +96,7 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
);
}
// Read out all of the raw data
// Read out all the raw data
const startTimestamp = Date.now();
this.logger.info('Reading Microsoft Graph users and groups');
@@ -112,6 +113,7 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
groupExpand: provider.groupExpand,
groupFilter: provider.groupFilter,
groupSearch: provider.groupSearch,
queryMode: provider.queryMode,
userTransformer: this.userTransformer,
groupTransformer: this.groupTransformer,
organizationTransformer: this.organizationTransformer,