relax github transform types
Signed-off-by: Jack Grigg <jrgrigg@gmail.com>
This commit is contained in:
@@ -50,6 +50,14 @@ export function isDomainEntity(entity: Entity): entity is DomainEntity {
|
||||
export function isGroupEntity(entity: Entity): entity is GroupEntity {
|
||||
return entity.kind.toLocaleUpperCase('en-US') === 'GROUP';
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function areGroupEntities(
|
||||
entities: Entity[],
|
||||
): entities is GroupEntity[] {
|
||||
return entities.every(e => isGroupEntity(e));
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -74,3 +82,9 @@ export function isSystemEntity(entity: Entity): entity is SystemEntity {
|
||||
export function isUserEntity(entity: Entity): entity is UserEntity {
|
||||
return entity.kind.toLocaleUpperCase('en-US') === 'USER';
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function areUserEntities(entities: Entity[]): entities is UserEntity[] {
|
||||
return entities.every(e => isUserEntity(e));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import { graphql } from '@octokit/graphql';
|
||||
import {
|
||||
ANNOTATION_GITHUB_TEAM_SLUG,
|
||||
@@ -34,24 +34,24 @@ export interface TransformerContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformer for GitHub users to UserEntity
|
||||
* Transformer for GitHub users to an Entity
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type UserTransformer = (
|
||||
item: GithubUser,
|
||||
ctx: TransformerContext,
|
||||
) => Promise<UserEntity | undefined>;
|
||||
) => Promise<Entity | undefined>;
|
||||
|
||||
/**
|
||||
* Transformer for GitHub Team to GroupEntity
|
||||
* Transformer for GitHub Team to an Entity
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TeamTransformer = (
|
||||
item: GithubTeam,
|
||||
ctx: TransformerContext,
|
||||
) => Promise<GroupEntity | undefined>;
|
||||
) => Promise<Entity | undefined>;
|
||||
|
||||
/**
|
||||
* Default transformer for GitHub users to UserEntity
|
||||
|
||||
@@ -444,7 +444,7 @@ describe('github', () => {
|
||||
customTeamTransformer,
|
||||
);
|
||||
|
||||
expect(teams.groups).toHaveLength(1);
|
||||
expect(teams.teams).toHaveLength(1);
|
||||
expect(teams).toEqual(output);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { GithubCredentialType } from '@backstage/integration';
|
||||
import { graphql } from '@octokit/graphql';
|
||||
import {
|
||||
@@ -139,7 +139,7 @@ export async function getOrganizationUsers(
|
||||
org: string,
|
||||
tokenType: GithubCredentialType,
|
||||
userTransformer: UserTransformer = defaultUserTransformer,
|
||||
): Promise<{ users: UserEntity[] }> {
|
||||
): Promise<{ users: Entity[] }> {
|
||||
const query = `
|
||||
query users($org: String!, $email: Boolean!, $cursor: String) {
|
||||
organization(login: $org) {
|
||||
@@ -188,7 +188,7 @@ export async function getOrganizationTeams(
|
||||
org: string,
|
||||
teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,
|
||||
): Promise<{
|
||||
groups: GroupEntity[];
|
||||
teams: Entity[];
|
||||
}> {
|
||||
const query = `
|
||||
query teams($org: String!, $cursor: String) {
|
||||
@@ -222,7 +222,7 @@ export async function getOrganizationTeams(
|
||||
const materialisedTeams = async (
|
||||
item: GithubTeamResponse,
|
||||
ctx: TransformerContext,
|
||||
): Promise<GroupEntity | undefined> => {
|
||||
): Promise<Entity | undefined> => {
|
||||
const memberNames: GithubUser[] = [];
|
||||
|
||||
if (!item.members.pageInfo.hasNextPage) {
|
||||
@@ -247,7 +247,7 @@ export async function getOrganizationTeams(
|
||||
return await teamTransformer(team, ctx);
|
||||
};
|
||||
|
||||
const groups = await queryWithPaging(
|
||||
const teams = await queryWithPaging(
|
||||
client,
|
||||
query,
|
||||
org,
|
||||
@@ -256,7 +256,7 @@ export async function getOrganizationTeams(
|
||||
{ org },
|
||||
);
|
||||
|
||||
return { groups };
|
||||
return { teams };
|
||||
}
|
||||
|
||||
export async function getOrganizationTeamsFromUsers(
|
||||
@@ -265,7 +265,7 @@ export async function getOrganizationTeamsFromUsers(
|
||||
userLogins: string[],
|
||||
teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,
|
||||
): Promise<{
|
||||
groups: GroupEntity[];
|
||||
teams: Entity[];
|
||||
}> {
|
||||
const query = `
|
||||
query teams($org: String!, $cursor: String, $userLogins: [String!] = "") {
|
||||
@@ -306,7 +306,7 @@ export async function getOrganizationTeamsFromUsers(
|
||||
const materialisedTeams = async (
|
||||
item: GithubTeamResponse,
|
||||
ctx: TransformerContext,
|
||||
): Promise<GroupEntity | undefined> => {
|
||||
): Promise<Entity | undefined> => {
|
||||
const memberNames: GithubUser[] = [];
|
||||
|
||||
if (!item.members.pageInfo.hasNextPage) {
|
||||
@@ -331,7 +331,7 @@ export async function getOrganizationTeamsFromUsers(
|
||||
return await teamTransformer(team, ctx);
|
||||
};
|
||||
|
||||
const groups = await queryWithPaging(
|
||||
const teams = await queryWithPaging(
|
||||
client,
|
||||
query,
|
||||
org,
|
||||
@@ -340,7 +340,7 @@ export async function getOrganizationTeamsFromUsers(
|
||||
{ org, userLogins },
|
||||
);
|
||||
|
||||
return { groups };
|
||||
return { teams };
|
||||
}
|
||||
|
||||
export async function getOrganizationsFromUser(
|
||||
@@ -377,7 +377,7 @@ export async function getOrganizationTeam(
|
||||
teamSlug: string,
|
||||
teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,
|
||||
): Promise<{
|
||||
group: GroupEntity;
|
||||
team: Entity;
|
||||
}> {
|
||||
const query = `
|
||||
query teams($org: String!, $teamSlug: String!) {
|
||||
@@ -401,7 +401,7 @@ export async function getOrganizationTeam(
|
||||
const materialisedTeam = async (
|
||||
item: GithubTeamResponse,
|
||||
ctx: TransformerContext,
|
||||
): Promise<GroupEntity | undefined> => {
|
||||
): Promise<Entity | undefined> => {
|
||||
const memberNames: GithubUser[] = [];
|
||||
|
||||
if (!item.members.pageInfo.hasNextPage) {
|
||||
@@ -432,17 +432,17 @@ export async function getOrganizationTeam(
|
||||
});
|
||||
|
||||
if (!response.organization?.team)
|
||||
throw new Error(`Found no match for group ${teamSlug}`);
|
||||
throw new Error(`Found no match for team ${teamSlug}`);
|
||||
|
||||
const group = await materialisedTeam(response.organization?.team, {
|
||||
const team = await materialisedTeam(response.organization?.team, {
|
||||
query,
|
||||
client,
|
||||
org,
|
||||
});
|
||||
|
||||
if (!group) throw new Error(`Can't transform for group ${teamSlug}`);
|
||||
if (!team) throw new Error(`Can't transform for team ${teamSlug}`);
|
||||
|
||||
return { group };
|
||||
return { team };
|
||||
}
|
||||
|
||||
export async function getOrganizationRepositories(
|
||||
|
||||
+17
-11
@@ -16,9 +16,11 @@
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
GroupEntity,
|
||||
Entity,
|
||||
areGroupEntities,
|
||||
areUserEntities,
|
||||
isGroupEntity,
|
||||
stringifyEntityRef,
|
||||
UserEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
@@ -147,7 +149,7 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
client,
|
||||
orgConfig.name,
|
||||
tokenType,
|
||||
async (githubUser, ctx): Promise<UserEntity | undefined> => {
|
||||
async (githubUser, ctx): Promise<Entity | undefined> => {
|
||||
const result = this.options.userTransformer
|
||||
? await this.options.userTransformer(githubUser, ctx)
|
||||
: await defaultUserTransformer(githubUser, ctx);
|
||||
@@ -160,15 +162,15 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
},
|
||||
);
|
||||
|
||||
const { groups } = await getOrganizationTeams(
|
||||
const { teams } = await getOrganizationTeams(
|
||||
client,
|
||||
orgConfig.name,
|
||||
async (team, ctx): Promise<GroupEntity | undefined> => {
|
||||
async (team, ctx): Promise<Entity | undefined> => {
|
||||
const result = this.options.teamTransformer
|
||||
? await this.options.teamTransformer(team, ctx)
|
||||
: await defaultOrganizationTeamTransformer(team, ctx);
|
||||
|
||||
if (result) {
|
||||
if (result && isGroupEntity(result)) {
|
||||
result.metadata.namespace = orgConfig.groupNamespace;
|
||||
// Group `spec.members` inherits the namespace of it's group so need to explicitly specify refs here
|
||||
result.spec.members = team.members.map(
|
||||
@@ -185,7 +187,7 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
|
||||
const duration = ((Date.now() - startTimestamp) / 1000).toFixed(1);
|
||||
this.logger.debug(
|
||||
`Read ${users.length} GitHub users and ${groups.length} GitHub teams from ${orgConfig.name} in ${duration} seconds`,
|
||||
`Read ${users.length} GitHub users and ${teams.length} GitHub teams from ${orgConfig.name} in ${duration} seconds`,
|
||||
);
|
||||
|
||||
// Grab current users from `allUsersMap` if they already exist in our
|
||||
@@ -199,11 +201,15 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
return allUsersMap.get(userRef);
|
||||
});
|
||||
|
||||
assignGroupsToUsers(pendingUsers, groups);
|
||||
buildOrgHierarchy(groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
buildOrgHierarchy(teams);
|
||||
if (areUserEntities(pendingUsers)) {
|
||||
assignGroupsToUsers(pendingUsers, teams);
|
||||
}
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
emit(processingResult.entity(location, group));
|
||||
for (const team of teams) {
|
||||
emit(processingResult.entity(location, team));
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
getOrganizationUsers,
|
||||
parseGithubOrgUrl,
|
||||
} from '../lib';
|
||||
import { areGroupEntities, areUserEntities } from '@backstage/catalog-model';
|
||||
|
||||
type GraphQL = typeof graphql;
|
||||
|
||||
@@ -101,19 +102,23 @@ export class GithubOrgReaderProcessor implements CatalogProcessor {
|
||||
this.logger.info('Reading GitHub users and groups');
|
||||
|
||||
const { users } = await getOrganizationUsers(client, org, tokenType);
|
||||
const { groups } = await getOrganizationTeams(client, org);
|
||||
const { teams } = await getOrganizationTeams(client, org);
|
||||
|
||||
const duration = ((Date.now() - startTimestamp) / 1000).toFixed(1);
|
||||
this.logger.debug(
|
||||
`Read ${users.length} GitHub users and ${groups.length} GitHub groups in ${duration} seconds`,
|
||||
`Read ${users.length} GitHub users and ${teams.length} GitHub teams in ${duration} seconds`,
|
||||
);
|
||||
|
||||
assignGroupsToUsers(users, groups);
|
||||
buildOrgHierarchy(groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
buildOrgHierarchy(teams);
|
||||
if (areUserEntities(users)) {
|
||||
assignGroupsToUsers(users, teams);
|
||||
}
|
||||
}
|
||||
|
||||
// Done!
|
||||
for (const group of groups) {
|
||||
emit(processingResult.entity(location, group));
|
||||
for (const team of teams) {
|
||||
emit(processingResult.entity(location, team));
|
||||
}
|
||||
for (const user of users) {
|
||||
emit(processingResult.entity(location, user));
|
||||
|
||||
+45
-30
@@ -18,9 +18,11 @@ import { TaskRunner } from '@backstage/backend-tasks';
|
||||
import {
|
||||
ANNOTATION_LOCATION,
|
||||
ANNOTATION_ORIGIN_LOCATION,
|
||||
areGroupEntities,
|
||||
areUserEntities,
|
||||
DEFAULT_NAMESPACE,
|
||||
Entity,
|
||||
GroupEntity,
|
||||
isGroupEntity,
|
||||
parseEntityRef,
|
||||
stringifyEntityRef,
|
||||
UserEntity,
|
||||
@@ -240,7 +242,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
const { markReadComplete } = trackProgress(logger);
|
||||
|
||||
const allUsersMap = new Map();
|
||||
const allGroups: Entity[] = [];
|
||||
const allTeams: Entity[] = [];
|
||||
|
||||
const orgsToProcess = this.options.orgs?.length
|
||||
? this.options.orgs
|
||||
@@ -266,7 +268,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
this.options.userTransformer,
|
||||
);
|
||||
|
||||
const { groups } = await getOrganizationTeams(
|
||||
const { teams } = await getOrganizationTeams(
|
||||
client,
|
||||
org,
|
||||
this.defaultMultiOrgTeamTransformer.bind(this),
|
||||
@@ -283,10 +285,14 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
return allUsersMap.get(userRef);
|
||||
});
|
||||
|
||||
assignGroupsToUsers(pendingUsers, groups);
|
||||
buildOrgHierarchy(groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
buildOrgHierarchy(teams);
|
||||
if (areUserEntities(pendingUsers)) {
|
||||
assignGroupsToUsers(pendingUsers, teams);
|
||||
}
|
||||
}
|
||||
|
||||
allGroups.push(...groups);
|
||||
allTeams.push(...teams);
|
||||
} catch (e) {
|
||||
logger.error(`Failed to read GitHub org data for ${org}: ${e}`);
|
||||
}
|
||||
@@ -294,11 +300,11 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
|
||||
const allUsers = Array.from(allUsersMap.values());
|
||||
|
||||
const { markCommitComplete } = markReadComplete({ allUsers, allGroups });
|
||||
const { markCommitComplete } = markReadComplete({ allUsers, allTeams });
|
||||
|
||||
await this.connection.applyMutation({
|
||||
type: 'full',
|
||||
entities: [...allUsers, ...allGroups].map(entity => ({
|
||||
entities: [...allUsers, ...allTeams].map(entity => ({
|
||||
locationKey: `github-multi-org-provider:${this.options.id}`,
|
||||
entity: withLocations(
|
||||
`https://${this.options.gitHubConfig.host}`,
|
||||
@@ -422,7 +428,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
this.options.userTransformer,
|
||||
);
|
||||
|
||||
const { groups } = await getOrganizationTeams(
|
||||
const { teams } = await getOrganizationTeams(
|
||||
client,
|
||||
org,
|
||||
this.defaultMultiOrgTeamTransformer.bind(this),
|
||||
@@ -440,7 +446,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
headers: orgHeaders,
|
||||
});
|
||||
|
||||
const { groups: userGroups } = await getOrganizationTeamsFromUsers(
|
||||
const { teams: userTeams } = await getOrganizationTeamsFromUsers(
|
||||
orgClient,
|
||||
userOrg,
|
||||
users.map(
|
||||
@@ -451,12 +457,14 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
this.defaultMultiOrgTeamTransformer.bind(this),
|
||||
);
|
||||
|
||||
assignGroupsToUsers(users, userGroups);
|
||||
if (areGroupEntities(userTeams) && areUserEntities(users)) {
|
||||
assignGroupsToUsers(users, userTeams);
|
||||
}
|
||||
}
|
||||
|
||||
const { added, removed } = this.createAddEntitiesOperation([
|
||||
...users,
|
||||
...groups,
|
||||
...teams,
|
||||
]);
|
||||
await this.connection.applyMutation({
|
||||
type: 'delta',
|
||||
@@ -536,14 +544,16 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
headers: orgHeaders,
|
||||
});
|
||||
|
||||
const { groups } = await getOrganizationTeamsFromUsers(
|
||||
const { teams } = await getOrganizationTeamsFromUsers(
|
||||
orgClient,
|
||||
userOrg,
|
||||
[login],
|
||||
this.defaultMultiOrgTeamTransformer.bind(this),
|
||||
);
|
||||
|
||||
assignGroupsToUsers([user], groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
assignGroupsToUsers([user], teams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,7 +633,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
});
|
||||
|
||||
const teamSlug = event.team.slug;
|
||||
const { group } = await getOrganizationTeam(
|
||||
const { team } = await getOrganizationTeam(
|
||||
client,
|
||||
org,
|
||||
teamSlug,
|
||||
@@ -637,10 +647,11 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
this.options.userTransformer,
|
||||
);
|
||||
|
||||
const usersFromChangedGroup =
|
||||
group.spec.members?.map(m =>
|
||||
stringifyEntityRef(parseEntityRef(m, { defaultKind: 'user' })),
|
||||
) || [];
|
||||
const usersFromChangedGroup = isGroupEntity(team)
|
||||
? team.spec.members?.map(m =>
|
||||
stringifyEntityRef(parseEntityRef(m, { defaultKind: 'user' })),
|
||||
) || []
|
||||
: [];
|
||||
const usersToRebuild = users.filter(u =>
|
||||
usersFromChangedGroup.includes(stringifyEntityRef(u)),
|
||||
);
|
||||
@@ -656,7 +667,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
headers: orgHeaders,
|
||||
});
|
||||
|
||||
const { groups } = await getOrganizationTeamsFromUsers(
|
||||
const { teams } = await getOrganizationTeamsFromUsers(
|
||||
orgClient,
|
||||
userOrg,
|
||||
usersToRebuild.map(
|
||||
@@ -667,7 +678,9 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
this.defaultMultiOrgTeamTransformer.bind(this),
|
||||
);
|
||||
|
||||
assignGroupsToUsers(usersToRebuild, groups);
|
||||
if (areGroupEntities(teams) && areUserEntities(usersToRebuild)) {
|
||||
assignGroupsToUsers(usersToRebuild, teams);
|
||||
}
|
||||
}
|
||||
|
||||
const oldName = event.changes.name?.from || '';
|
||||
@@ -693,7 +706,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
const { removed } = this.createRemoveEntitiesOperation([oldGroup]);
|
||||
const { added } = this.createAddEntitiesOperation([
|
||||
...usersToRebuild,
|
||||
group,
|
||||
team,
|
||||
]);
|
||||
await this.connection.applyMutation({
|
||||
type: 'delta',
|
||||
@@ -729,7 +742,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
});
|
||||
|
||||
const teamSlug = event.team.slug;
|
||||
const { group } = await getOrganizationTeam(
|
||||
const { team } = await getOrganizationTeam(
|
||||
client,
|
||||
org,
|
||||
teamSlug,
|
||||
@@ -765,17 +778,19 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
headers: orgHeaders,
|
||||
});
|
||||
|
||||
const { groups } = await getOrganizationTeamsFromUsers(
|
||||
const { teams } = await getOrganizationTeamsFromUsers(
|
||||
orgClient,
|
||||
userOrg,
|
||||
[login],
|
||||
this.defaultMultiOrgTeamTransformer.bind(this),
|
||||
);
|
||||
|
||||
assignGroupsToUsers([user], groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
assignGroupsToUsers([user], teams);
|
||||
}
|
||||
}
|
||||
|
||||
const { added, removed } = this.createAddEntitiesOperation([user, group]);
|
||||
const { added, removed } = this.createAddEntitiesOperation([user, team]);
|
||||
await this.connection.applyMutation({
|
||||
type: 'delta',
|
||||
removed,
|
||||
@@ -812,14 +827,14 @@ export class GithubMultiOrgEntityProvider implements EntityProvider {
|
||||
private async defaultMultiOrgTeamTransformer(
|
||||
team: GithubTeam,
|
||||
ctx: TransformerContext,
|
||||
): Promise<GroupEntity | undefined> {
|
||||
): Promise<Entity | undefined> {
|
||||
if (this.options.teamTransformer) {
|
||||
return await this.options.teamTransformer(team, ctx);
|
||||
}
|
||||
|
||||
const result = await defaultOrganizationTeamTransformer(team, ctx);
|
||||
|
||||
if (result) {
|
||||
if (result && result.spec) {
|
||||
result.metadata.namespace = ctx.org.toLocaleLowerCase('en-US');
|
||||
// Group `spec.members` inherits the namespace of it's group so need to explicitly specify refs here
|
||||
result.spec.members = team.members.map(
|
||||
@@ -885,9 +900,9 @@ function trackProgress(logger: Logger) {
|
||||
|
||||
function markReadComplete(read: {
|
||||
allUsers: unknown[];
|
||||
allGroups: unknown[];
|
||||
allTeams: unknown[];
|
||||
}) {
|
||||
summary = `${read.allUsers.length} GitHub users and ${read.allGroups.length} GitHub groups`;
|
||||
summary = `${read.allUsers.length} GitHub users and ${read.allTeams.length} GitHub groups`;
|
||||
const readDuration = ((Date.now() - timestamp) / 1000).toFixed(1);
|
||||
timestamp = Date.now();
|
||||
logger.info(`Read ${summary} in ${readDuration} seconds. Committing...`);
|
||||
|
||||
@@ -19,6 +19,9 @@ import {
|
||||
ANNOTATION_LOCATION,
|
||||
ANNOTATION_ORIGIN_LOCATION,
|
||||
Entity,
|
||||
areGroupEntities,
|
||||
areUserEntities,
|
||||
isGroupEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
@@ -223,20 +226,24 @@ export class GithubOrgEntityProvider
|
||||
tokenType,
|
||||
this.options.userTransformer,
|
||||
);
|
||||
const { groups } = await getOrganizationTeams(
|
||||
const { teams } = await getOrganizationTeams(
|
||||
client,
|
||||
org,
|
||||
this.options.teamTransformer,
|
||||
);
|
||||
|
||||
assignGroupsToUsers(users, groups);
|
||||
buildOrgHierarchy(groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
buildOrgHierarchy(teams);
|
||||
if (areUserEntities(users)) {
|
||||
assignGroupsToUsers(users, teams);
|
||||
}
|
||||
}
|
||||
|
||||
const { markCommitComplete } = markReadComplete({ users, groups });
|
||||
const { markCommitComplete } = markReadComplete({ users, teams });
|
||||
|
||||
await this.connection.applyMutation({
|
||||
type: 'full',
|
||||
entities: [...users, ...groups].map(entity => ({
|
||||
entities: [...users, ...teams].map(entity => ({
|
||||
locationKey: `github-org-provider:${this.options.id}`,
|
||||
entity: withLocations(
|
||||
`https://${this.options.gitHubConfig.host}`,
|
||||
@@ -340,7 +347,7 @@ export class GithubOrgEntityProvider
|
||||
});
|
||||
|
||||
const { org } = parseGithubOrgUrl(this.options.orgUrl);
|
||||
const { group } = await getOrganizationTeam(
|
||||
const { team } = await getOrganizationTeam(
|
||||
client,
|
||||
org,
|
||||
teamSlug,
|
||||
@@ -354,20 +361,28 @@ export class GithubOrgEntityProvider
|
||||
this.options.userTransformer,
|
||||
);
|
||||
|
||||
const usersFromChangedGroup = group.spec.members || [];
|
||||
if (!isGroupEntity(team)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const usersFromChangedGroup = team.spec.members || [];
|
||||
const usersToRebuild = users.filter(u =>
|
||||
usersFromChangedGroup.includes(u.metadata.name),
|
||||
);
|
||||
|
||||
const { groups } = await getOrganizationTeamsFromUsers(
|
||||
const { teams } = await getOrganizationTeamsFromUsers(
|
||||
client,
|
||||
org,
|
||||
usersToRebuild.map(u => u.metadata.name),
|
||||
this.options.teamTransformer,
|
||||
);
|
||||
|
||||
assignGroupsToUsers(usersToRebuild, groups);
|
||||
buildOrgHierarchy(groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
buildOrgHierarchy(teams);
|
||||
if (areUserEntities(usersToRebuild)) {
|
||||
assignGroupsToUsers(usersToRebuild, teams);
|
||||
}
|
||||
}
|
||||
|
||||
const oldName = event.changes.name?.from || event.team.name;
|
||||
const oldSlug = oldName.toLowerCase().replaceAll(/\s/gi, '-');
|
||||
@@ -380,14 +395,14 @@ export class GithubOrgEntityProvider
|
||||
|
||||
const { removed } = createDeltaOperation(org, [
|
||||
{
|
||||
...group,
|
||||
...team,
|
||||
metadata: {
|
||||
name: oldSlug,
|
||||
description: oldDescriptionSlug,
|
||||
},
|
||||
},
|
||||
]);
|
||||
const { added } = createDeltaOperation(org, [...usersToRebuild, ...groups]);
|
||||
const { added } = createDeltaOperation(org, [...usersToRebuild, ...teams]);
|
||||
await this.connection.applyMutation({
|
||||
type: 'delta',
|
||||
removed,
|
||||
@@ -423,7 +438,7 @@ export class GithubOrgEntityProvider
|
||||
});
|
||||
|
||||
const { org } = parseGithubOrgUrl(this.options.orgUrl);
|
||||
const { group } = await getOrganizationTeam(
|
||||
const { team } = await getOrganizationTeam(
|
||||
client,
|
||||
org,
|
||||
teamSlug,
|
||||
@@ -439,7 +454,7 @@ export class GithubOrgEntityProvider
|
||||
|
||||
const usersToRebuild = users.filter(u => u.metadata.name === userLogin);
|
||||
|
||||
const { groups } = await getOrganizationTeamsFromUsers(
|
||||
const { teams } = await getOrganizationTeamsFromUsers(
|
||||
client,
|
||||
org,
|
||||
[userLogin],
|
||||
@@ -447,16 +462,20 @@ export class GithubOrgEntityProvider
|
||||
);
|
||||
|
||||
// we include group because the removed event need to update the old group too
|
||||
if (!groups.some(g => g.metadata.name === group.metadata.name)) {
|
||||
groups.push(group);
|
||||
if (!teams.some(t => t.metadata.name === team.metadata.name)) {
|
||||
teams.push(team);
|
||||
}
|
||||
|
||||
assignGroupsToUsers(usersToRebuild, groups);
|
||||
buildOrgHierarchy(groups);
|
||||
if (areGroupEntities(teams)) {
|
||||
buildOrgHierarchy(teams);
|
||||
if (areUserEntities(usersToRebuild)) {
|
||||
assignGroupsToUsers(usersToRebuild, teams);
|
||||
}
|
||||
}
|
||||
|
||||
const { added, removed } = createDeltaOperation(org, [
|
||||
...usersToRebuild,
|
||||
...groups,
|
||||
...teams,
|
||||
]);
|
||||
await this.connection.applyMutation({
|
||||
type: 'delta',
|
||||
@@ -588,10 +607,10 @@ function trackProgress(logger: Logger) {
|
||||
let timestamp = Date.now();
|
||||
let summary: string;
|
||||
|
||||
logger.info('Reading GitHub users and groups');
|
||||
logger.info('Reading GitHub users and teams');
|
||||
|
||||
function markReadComplete(read: { users: unknown[]; groups: unknown[] }) {
|
||||
summary = `${read.users.length} GitHub users and ${read.groups.length} GitHub groups`;
|
||||
function markReadComplete(read: { users: unknown[]; teams: unknown[] }) {
|
||||
summary = `${read.users.length} GitHub users and ${read.teams.length} GitHub teams`;
|
||||
const readDuration = ((Date.now() - timestamp) / 1000).toFixed(1);
|
||||
timestamp = Date.now();
|
||||
logger.info(`Read ${summary} in ${readDuration} seconds. Committing...`);
|
||||
|
||||
Reference in New Issue
Block a user