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 @@
+someRootGroup alpha beta sharks goats dolphins John Anna Patricia Emil Lovisa Chandra
\ 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.
+
+
+
+
+
+
+
+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,