diff --git a/.changeset/brave-toes-switch.md b/.changeset/brave-toes-switch.md new file mode 100644 index 0000000000..27b39cccd3 --- /dev/null +++ b/.changeset/brave-toes-switch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': minor +--- + +Add new `userGroupMember.path`, `user.path` and, `group.path` option to each query type to allow more complex msgraph queries diff --git a/docs/assets/integrations/azure/org.svg b/docs/assets/integrations/azure/org.svg new file mode 100644 index 0000000000..d30fc6e1d8 --- /dev/null +++ b/docs/assets/integrations/azure/org.svg @@ -0,0 +1 @@ +someRootGroupalphabetasharksgoatsdolphinsJohnAnnaPatriciaEmilLovisaChandra \ No newline at end of file diff --git a/docs/integrations/azure/org.md b/docs/integrations/azure/org.md index 068aa70101..3765275e1e 100644 --- a/docs/integrations/azure/org.md +++ b/docs/integrations/azure/org.md @@ -125,6 +125,8 @@ microsoftGraphOrg: In addition to these groups, one additional group will be created for your organization. All imported groups will be a child of this group. +By default the provider will get groups using the msgraph `/group` endpoint, but it is possible to use different endpoints by setting the `path` configuration. All the endpoint containing `/microsoft.graph.group` will return the right type of group object. [See usage](#Using-path-parameter) for more details. + ### Users There are two modes for importing users - You can import all user objects matching a `filter`. @@ -148,6 +150,36 @@ microsoftGraphOrg: search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")' ``` +By default the provider will get user using the msgraph `/user` endpoint, but it is possible to use different endpoints by setting the `path` configuration. All the endpoint containing `/microsoft.graph.user` will return the right type of user object. [See usage](#Using-path-parameter) for more details. + +### Using `path` parameter + +By default the provider will get groups and users using the msgraph `/group` and `/user` endpoints, but it is possible to use different endpoints by setting the `path` configuration. +All the endpoint containing `/microsoft.graph.user` will return the right type of user object and all the endpoint containing `/microsoft.graph.group` will return the right type of group object. + +#### Example + +Given the following org structure it is possible to use the `path` parameter to get all the users and groups that are members of the group `someRootGroup` on all levels. + +
+ +![email](../../assets/integrations/azure/org.svg) + +
+ +The configuration would look like this: + +```yaml +microsoftGraphOrg: + providerId: + group: + path: /groups/{someRootGroup id}/transitiveMembers/microsoft.graph.group + user: + path: /groups/{someRootGroup id}/transitiveMembers/microsoft.graph.user +``` + +Using the transitive members endpoint will return all the users and groups that are members of the group `someRootGroup` on all levels. + ### User photos By default, the photos of users will be fetched and added to each user entity. For huge organizations this may be unfeasible, as it will take a _very_ long time, and can be disabled by setting `loadPhotos` to `false`: diff --git a/plugins/catalog-backend-module-msgraph/README.md b/plugins/catalog-backend-module-msgraph/README.md index 7ee416ecd4..8e8c84de37 100644 --- a/plugins/catalog-backend-module-msgraph/README.md +++ b/plugins/catalog-backend-module-msgraph/README.md @@ -58,6 +58,8 @@ catalog: loadPhotos: true # See https://docs.microsoft.com/en-us/graph/api/resources/schemaextension?view=graph-rest-1.0 select: ['id', 'displayName', 'description'] + # Optional /users by default but allow to query users from different msgraph endpoints + path: /users # Optional configuration block userGroupMember: # Optional filter for users, use group membership to get users. @@ -69,6 +71,8 @@ catalog: # (Search for groups and fetch their members.) # This and userFilter are mutually exclusive, only one can be specified search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")' + # Optional /groups by default but allow to query groups from different msgraph endpoints + path: /groups # Optional configuration block group: # Optional parameter to include the expanded resource or collection referenced @@ -87,6 +91,8 @@ catalog: # in order to add extra information to your groups that can be used on your custom groupTransformers # See https://docs.microsoft.com/en-us/graph/api/resources/schemaextension?view=graph-rest-1.0 select: ['id', 'displayName', 'description'] + # Optional /groups by default but allow to query groups from different msgraph endpoints + path: /groups schedule: # optional; same options as in TaskScheduleDefinition # supports cron, ISO duration, "human duration" as used in code frequency: { hours: 1 } diff --git a/plugins/catalog-backend-module-msgraph/config.d.ts b/plugins/catalog-backend-module-msgraph/config.d.ts index 8928da6f53..dfcad06593 100644 --- a/plugins/catalog-backend-module-msgraph/config.d.ts +++ b/plugins/catalog-backend-module-msgraph/config.d.ts @@ -148,6 +148,12 @@ export interface Config { */ queryMode?: string; user?: { + /** + * The url path to fetch groups, defaults to `/users`. + * + * E.g. "groups/{id}/transitiveMembers/microsoft.graph.user/". + */ + path?: string; /** * The "expand" argument to apply to users. * @@ -174,6 +180,12 @@ export interface Config { }; group?: { + /** + * The url path to fetch groups, defaults to `/groups`. + * + * E.g. "groups/{id}/transitiveMembers/microsoft.graph.group/". + */ + path?: string; /** * The "expand" argument to apply to groups. * @@ -206,6 +218,12 @@ export interface Config { }; userGroupMember?: { + /** + * The url path to fetch groups, defaults to `/groups`. + * + * E.g. "groups/{id}/transitiveMembers/microsoft.graph.group/". + */ + path?: string; /** * The filter to apply to extract users by groups memberships. * @@ -263,6 +281,12 @@ export interface Config { */ queryMode?: string; user?: { + /** + * The url path to fetch groups, defaults to `/groups`. + * + * E.g. "groups/{id}/transitiveMembers/microsoft.graph.group/". + */ + path?: string; /** * The "expand" argument to apply to users. * @@ -289,6 +313,12 @@ export interface Config { }; group?: { + /** + * The url path to fetch groups, defaults to `/groups`. + * + * E.g. "groups/{id}/transitiveMembers/microsoft.graph.group/". + */ + path?: string; /** * The "expand" argument to apply to groups. * @@ -321,6 +351,12 @@ export interface Config { }; userGroupMember?: { + /** + * The url path to fetch groups, defaults to `/groups`. + * + * E.g. "groups/{id}/transitiveMembers/microsoft.graph.group/". + */ + path?: string; /** * The filter to apply to extract users by groups memberships. * diff --git a/plugins/catalog-backend-module-msgraph/report.api.md b/plugins/catalog-backend-module-msgraph/report.api.md index 912ca16e85..02d3949d04 100644 --- a/plugins/catalog-backend-module-msgraph/report.api.md +++ b/plugins/catalog-backend-module-msgraph/report.api.md @@ -92,6 +92,7 @@ export class MicrosoftGraphClient { getGroups( query?: ODataQuery, queryMode?: 'basic' | 'advanced', + path?: string, ): AsyncIterable; getGroupUserMembers( groupId: string, @@ -108,6 +109,7 @@ export class MicrosoftGraphClient { getUsers( query?: ODataQuery, queryMode?: 'basic' | 'advanced', + path?: string, ): AsyncIterable; requestApi( path: string, @@ -239,12 +241,15 @@ export type MicrosoftGraphProviderConfig = { userFilter?: string; userSelect?: string[]; userExpand?: string; + userPath?: string; userGroupMemberFilter?: string; userGroupMemberSearch?: string; + userGroupMemberPath?: string; groupExpand?: string; groupFilter?: string; groupSearch?: string; groupSelect?: string[]; + groupPath?: string; groupIncludeSubGroups?: boolean; queryMode?: 'basic' | 'advanced'; loadUserPhotos?: boolean; @@ -287,13 +292,16 @@ export function readMicrosoftGraphOrg( userExpand?: string; userFilter?: string; userSelect?: string[]; + userPath?: string; loadUserPhotos?: boolean; userGroupMemberSearch?: string; userGroupMemberFilter?: string; + userGroupMemberPath?: string; groupExpand?: string; groupSearch?: string; groupFilter?: string; groupSelect?: string[]; + groupPath?: string; groupIncludeSubGroups?: boolean; queryMode?: 'basic' | 'advanced'; userTransformer?: UserTransformer; diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts index aa2d735a07..81b57273a8 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts @@ -272,16 +272,14 @@ export class MicrosoftGraphClient { * @public * @param query - OData Query {@link ODataQuery} * @param queryMode - Mode to use while querying. Some features are only available at "advanced". + * @param path - Resource endpoint in Microsoft Graph */ async *getUsers( query?: ODataQuery, queryMode?: 'basic' | 'advanced', + path: string = 'users', ): AsyncIterable { - yield* this.requestCollection( - `users`, - query, - queryMode, - ); + yield* this.requestCollection(path, query, queryMode); } /** @@ -314,16 +312,14 @@ export class MicrosoftGraphClient { * @public * @param query - OData Query {@link ODataQuery} * @param queryMode - Mode to use while querying. Some features are only available at "advanced". + * @param path - Resource endpoint in Microsoft Graph */ async *getGroups( query?: ODataQuery, queryMode?: 'basic' | 'advanced', + path: string = 'groups', ): AsyncIterable { - yield* this.requestCollection( - `groups`, - query, - queryMode, - ); + yield* this.requestCollection(path, query, queryMode); } /** diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts index 0ec56f134d..182c4842dd 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts @@ -148,6 +148,8 @@ describe('readProviderConfigs', () => { id: 'customProviderId', target: 'https://graph.microsoft.com/v1.0', tenantId: 'tenantId', + userPath: 'users', + groupPath: 'groups', }, ]; expect(actual).toEqual(expected); @@ -169,12 +171,14 @@ describe('readProviderConfigs', () => { expand: 'manager', filter: 'accountEnabled eq true', select: ['id', 'displayName', 'department'], + path: '/groups/{groupId}/members', }, group: { expand: 'member', filter: 'securityEnabled eq false', select: ['id', 'displayName', 'description'], includeSubGroups: true, + path: '/groups/{groupId}/members', }, schedule: { frequency: 'PT30M', @@ -200,9 +204,11 @@ describe('readProviderConfigs', () => { userExpand: 'manager', userFilter: 'accountEnabled eq true', userSelect: ['id', 'displayName', 'department'], + userPath: '/groups/{groupId}/members', groupExpand: 'member', groupSelect: ['id', 'displayName', 'description'], groupFilter: 'securityEnabled eq false', + groupPath: '/groups/{groupId}/members', groupIncludeSubGroups: true, schedule: { frequency: { minutes: 30 }, diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts index e47181a1c4..5feabcb9b2 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts @@ -78,6 +78,13 @@ export type MicrosoftGraphProviderConfig = { * E.g. "manager". */ userExpand?: string; + + /** + * The path to the users endpoint. Defaults to "/users". + * + * E.g. "/users" + */ + userPath?: string; /** * The filter to apply to extract users by groups memberships. * @@ -90,6 +97,14 @@ export type MicrosoftGraphProviderConfig = { * E.g. "\"displayName:-team\"" would only match groups which contain '-team' */ userGroupMemberSearch?: string; + + /** + * The path to the groups endpoint. Defaults to "/groups". + * + * E.g. "/groups" + */ + userGroupMemberPath?: string; + /** * The "expand" argument to apply to groups. * @@ -116,6 +131,13 @@ export type MicrosoftGraphProviderConfig = { */ groupSelect?: string[]; + /** + * The path to the groups endpoint. Defaults to "/groups". + * + * E.g. "/groups" + */ + groupPath?: string; + /** * Whether to ingest groups that are members of the found/filtered/searched groups. * Default value is `false`. @@ -292,12 +314,14 @@ export function readProviderConfig( const userExpand = config.getOptionalString('user.expand'); const userFilter = config.getOptionalString('user.filter'); const userSelect = config.getOptionalStringArray('user.select'); + const userPath = config.getOptionalString('user.path') ?? 'users'; const loadUserPhotos = config.getOptionalBoolean('user.loadPhotos'); const groupExpand = config.getOptionalString('group.expand'); const groupFilter = config.getOptionalString('group.filter'); const groupSearch = config.getOptionalString('group.search'); const groupSelect = config.getOptionalStringArray('group.select'); + const groupPath = config.getOptionalString('group.path') ?? 'groups'; const groupIncludeSubGroups = config.getOptionalBoolean( 'group.includeSubGroups', ); @@ -317,6 +341,7 @@ export function readProviderConfig( const userGroupMemberSearch = config.getOptionalString( 'userGroupMember.search', ); + const userGroupMemberPath = config.getOptionalString('userGroupMember.path'); if (userFilter && userGroupMemberFilter) { throw new Error( @@ -353,15 +378,18 @@ export function readProviderConfig( userExpand, userFilter, userSelect, + userPath, loadUserPhotos, groupExpand, groupFilter, groupSearch, groupSelect, + groupPath, groupIncludeSubGroups, queryMode, userGroupMemberFilter, userGroupMemberSearch, + userGroupMemberPath, schedule, }; } diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts index b83597653f..33ae04bd76 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts @@ -139,6 +139,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledWith( @@ -186,6 +187,7 @@ describe('read microsoft graph', () => { top: 999, }, 'advanced', + undefined, ); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledWith( @@ -229,6 +231,55 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, + ); + expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); + expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledWith( + 'userid', + 120, + ); + }); + + it('should read users from different endpoints', async () => { + client.getUsers.mockImplementation(getExampleUsers); + client.getUserPhotoWithSizeLimit.mockResolvedValue( + 'data:image/jpeg;base64,...', + ); + + const { users } = await readMicrosoftGraphUsers(client, { + userFilter: 'accountEnabled eq true', + userPath: '/users/x/y', + logger: mockServices.logger.mock(), + }); + + expect(users).toEqual([ + user({ + metadata: { + annotations: { + 'graph.microsoft.com/user-id': 'userid', + 'microsoft.com/email': 'user.name@example.com', + }, + name: 'user.name_example.com', + }, + spec: { + profile: { + displayName: 'User Name', + email: 'user.name@example.com', + picture: 'data:image/jpeg;base64,...', + }, + memberOf: [], + }, + }), + ]); + + expect(client.getUsers).toHaveBeenCalledTimes(1); + expect(client.getUsers).toHaveBeenCalledWith( + { + filter: 'accountEnabled eq true', + top: 999, + }, + undefined, + '/users/x/y', ); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledWith( @@ -280,6 +331,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroupUserMembers).toHaveBeenCalledTimes(1); @@ -338,6 +390,7 @@ describe('read microsoft graph', () => { top: 999, }, 'advanced', + undefined, ); expect(client.getGroupUserMembers).toHaveBeenCalledTimes(1); @@ -393,6 +446,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroupUserMembers).toHaveBeenCalledTimes(1); @@ -411,6 +465,52 @@ describe('read microsoft graph', () => { 120, ); }); + + it('should read users from different group endpoints', async () => { + client.getGroups.mockImplementation(getExampleGroups); + client.getGroupUserMembers.mockImplementation(getExampleUsers); + + client.getUserPhotoWithSizeLimit.mockResolvedValue( + 'data:image/jpeg;base64,...', + ); + + const { users } = await readMicrosoftGraphUsersInGroups(client, { + userGroupMemberFilter: 'securityEnabled eq true', + userGroupMemberPath: '/groups/x/y', + logger: mockServices.logger.mock(), + }); + + expect(users).toEqual([ + user({ + metadata: { + annotations: { + 'graph.microsoft.com/user-id': 'userid', + 'microsoft.com/email': 'user.name@example.com', + }, + name: 'user.name_example.com', + }, + spec: { + profile: { + displayName: 'User Name', + email: 'user.name@example.com', + picture: 'data:image/jpeg;base64,...', + }, + memberOf: [], + }, + }), + ]); + + expect(client.getGroups).toHaveBeenCalledTimes(1); + expect(client.getGroups).toHaveBeenCalledWith( + { + filter: 'securityEnabled eq true', + select: ['id', 'displayName'], + top: 999, + }, + undefined, + '/groups/x/y', + ); + }); }); describe('readMicrosoftGraphOrganization', () => { @@ -535,6 +635,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { @@ -614,6 +715,7 @@ describe('read microsoft graph', () => { top: 999, }, 'advanced', + undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { @@ -694,6 +796,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { @@ -767,6 +870,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { @@ -871,6 +975,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(2); expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { @@ -884,6 +989,79 @@ describe('read microsoft graph', () => { // expect(client.getGroupPhotoWithSizeLimit).toBeCalledTimes(1); // expect(client.getGroupPhotoWithSizeLimit).toBeCalledWith('groupid', 120); }); + + it('should read groups from different endpoints paths', async () => { + 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', { + groupFilter: 'securityEnabled eq false', + groupPath: '/groups/x/y', + }); + + 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', + }, + 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).toHaveBeenCalledTimes(1); + expect(client.getGroups).toHaveBeenCalledWith( + { + filter: 'securityEnabled eq false', + top: 999, + }, + undefined, + '/groups/x/y', + ); + expect(client.getGroupMembers).toHaveBeenCalledTimes(1); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); + }); }); describe('resolveRelations', () => { @@ -1018,6 +1196,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroups).toHaveBeenCalledTimes(1); expect(client.getGroups).toHaveBeenCalledWith( @@ -1026,6 +1205,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); }); @@ -1058,6 +1238,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroups).toHaveBeenCalledTimes(1); expect(client.getGroups).toHaveBeenCalledWith( @@ -1066,6 +1247,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); }); @@ -1096,6 +1278,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroups).toHaveBeenCalledTimes(1); expect(client.getGroups).toHaveBeenCalledWith( @@ -1103,6 +1286,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); }); @@ -1132,6 +1316,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); }); @@ -1161,20 +1346,24 @@ describe('read microsoft graph', () => { expect(client.getUsers).toHaveBeenCalledTimes(0); expect(client.getGroups).toHaveBeenCalledTimes(2); - expect(client.getGroups).toHaveBeenCalledWith( + expect(client.getGroups).toHaveBeenNthCalledWith( + 1, { filter: 'name eq backstage-group', select: ['id', 'displayName'], top: 999, }, undefined, + undefined, ); - expect(client.getGroups).toHaveBeenCalledWith( + expect(client.getGroups).toHaveBeenNthCalledWith( + 2, { filter: 'securityEnabled eq false', top: 999, }, undefined, + undefined, ); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); }); @@ -1215,6 +1404,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); expect(client.getGroups).toHaveBeenCalledTimes(1); expect(client.getGroups).toHaveBeenCalledWith( @@ -1222,6 +1412,7 @@ describe('read microsoft graph', () => { top: 999, }, undefined, + undefined, ); }); }); diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index 4f1c9efbe2..4761c45ac7 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -49,6 +49,7 @@ export async function readMicrosoftGraphUsers( userExpand?: string; userFilter?: string; userSelect?: string[]; + userPath?: string; loadUserPhotos?: boolean; transformer?: UserTransformer; logger: LoggerService; @@ -64,6 +65,7 @@ export async function readMicrosoftGraphUsers( top: PAGE_SIZE, }, options.queryMode, + options.userPath, ); return { @@ -87,6 +89,7 @@ export async function readMicrosoftGraphUsersInGroups( loadUserPhotos?: boolean; userGroupMemberSearch?: string; userGroupMemberFilter?: string; + userGroupMemberPath?: string; groupExpand?: string; transformer?: UserTransformer; logger: LoggerService; @@ -108,6 +111,7 @@ export async function readMicrosoftGraphUsersInGroups( top: PAGE_SIZE, }, options.queryMode, + options.userGroupMemberPath, )) { // Process all groups in parallel, otherwise it can take quite some time userGroupMemberPromises.push( @@ -178,6 +182,7 @@ export async function readMicrosoftGraphGroups( groupFilter?: string; groupSearch?: string; groupSelect?: string[]; + groupPath?: string; groupIncludeSubGroups?: boolean; groupTransformer?: GroupTransformer; organizationTransformer?: OrganizationTransformer; @@ -213,6 +218,7 @@ export async function readMicrosoftGraphGroups( top: PAGE_SIZE, }, options?.queryMode, + options?.groupPath, )) { // Process all groups in parallel, otherwise it can take quite some time promises.push( @@ -396,13 +402,16 @@ export async function readMicrosoftGraphOrg( userExpand?: string; userFilter?: string; userSelect?: string[]; + userPath?: string; loadUserPhotos?: boolean; userGroupMemberSearch?: string; userGroupMemberFilter?: string; + userGroupMemberPath?: string; groupExpand?: string; groupSearch?: string; groupFilter?: string; groupSelect?: string[]; + groupPath?: string; groupIncludeSubGroups?: boolean; queryMode?: 'basic' | 'advanced'; userTransformer?: UserTransformer; @@ -413,7 +422,11 @@ export async function readMicrosoftGraphOrg( ): Promise<{ users: UserEntity[]; groups: GroupEntity[] }> { let users: UserEntity[] = []; - if (options.userGroupMemberFilter || options.userGroupMemberSearch) { + if ( + options.userGroupMemberFilter || + options.userGroupMemberSearch || + options.userGroupMemberPath + ) { const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups( client, { @@ -423,6 +436,7 @@ export async function readMicrosoftGraphOrg( userSelect: options.userSelect, userGroupMemberFilter: options.userGroupMemberFilter, userGroupMemberSearch: options.userGroupMemberSearch, + userGroupMemberPath: options.userGroupMemberPath, loadUserPhotos: options.loadUserPhotos, transformer: options.userTransformer, logger: options.logger, @@ -435,6 +449,7 @@ export async function readMicrosoftGraphOrg( userExpand: options.userExpand, userFilter: options.userFilter, userSelect: options.userSelect, + userPath: options.userPath, loadUserPhotos: options.loadUserPhotos, transformer: options.userTransformer, logger: options.logger, @@ -448,6 +463,7 @@ export async function readMicrosoftGraphOrg( groupFilter: options.groupFilter, groupSearch: options.groupSearch, groupSelect: options.groupSelect, + groupPath: options.groupPath, groupIncludeSubGroups: options.groupIncludeSubGroups, groupTransformer: options.groupTransformer, organizationTransformer: options.organizationTransformer, diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts index c490c0473d..423b7eb466 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts @@ -327,6 +327,7 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { : this.options.provider; const { markReadComplete } = trackProgress(logger); const client = MicrosoftGraphClient.create(this.options.provider); + const { users, groups } = await readMicrosoftGraphOrg( client, provider.tenantId, @@ -334,13 +335,16 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { userExpand: provider.userExpand, userFilter: provider.userFilter, userSelect: provider.userSelect, + userPath: provider.userPath, loadUserPhotos: provider.loadUserPhotos, userGroupMemberFilter: provider.userGroupMemberFilter, userGroupMemberSearch: provider.userGroupMemberSearch, + userGroupMemberPath: provider.userGroupMemberPath, groupExpand: provider.groupExpand, groupFilter: provider.groupFilter, groupSearch: provider.groupSearch, groupSelect: provider.groupSelect, + groupPath: provider.groupPath, groupIncludeSubGroups: provider.groupIncludeSubGroups, queryMode: provider.queryMode, groupTransformer: this.options.groupTransformer,