@backstage/plugin-catalog-backend-module-github 0.13.2-next.1 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/config.d.ts +16 -0
- package/dist/index.d.ts +41 -1
- package/dist/lib/github.cjs.js +137 -4
- package/dist/lib/github.cjs.js.map +1 -1
- package/dist/providers/GithubMultiOrgEntityProvider.cjs.js +93 -35
- package/dist/providers/GithubMultiOrgEntityProvider.cjs.js.map +1 -1
- package/dist/providers/GithubOrgEntityProvider.cjs.js +70 -20
- package/dist/providers/GithubOrgEntityProvider.cjs.js.map +1 -1
- package/package.json +17 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend-module-github
|
|
2
2
|
|
|
3
|
+
## 0.13.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d745f1c: Added experimental support for checking suspended users via the GitHub REST API instead of the GraphQL `suspendedAt` field. Enable by setting both `excludeSuspendedUsers: true` and `experimental_checkForSuspendedUsersWithRest: true` in the provider config. When enabled, responses are cached using conditional HTTP requests to minimize REST API rate limit usage.
|
|
8
|
+
- e9b78e9: Removed the `uuid` dependency and replaced usage with the built-in `crypto.randomUUID()`.
|
|
9
|
+
- aa313f0: The `GithubMultiOrgEntityProvider` now emits entities in a stable order during full mutations. Entities are sorted by entity ref, with the location annotation as a tiebreaker for entities that share the same ref. This prevents entity data from flickering between different GitHub orgs across refresh cycles when `alwaysUseDefaultNamespace` is enabled and teams with identical slugs exist in multiple orgs.
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
- @backstage/catalog-model@1.9.0
|
|
12
|
+
- @backstage/errors@1.3.1
|
|
13
|
+
- @backstage/backend-plugin-api@1.9.1
|
|
14
|
+
- @backstage/plugin-catalog-node@2.2.1
|
|
15
|
+
- @backstage/integration@2.0.2
|
|
16
|
+
- @backstage/config@1.3.8
|
|
17
|
+
- @backstage/plugin-catalog-common@1.1.10
|
|
18
|
+
- @backstage/plugin-events-node@0.4.22
|
|
19
|
+
|
|
3
20
|
## 0.13.2-next.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/config.d.ts
CHANGED
|
@@ -270,6 +270,14 @@ export interface Config {
|
|
|
270
270
|
*/
|
|
271
271
|
excludeSuspendedUsers?: boolean;
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* (Optional) When set to true alongside `excludeSuspendedUsers`, use the GitHub REST API
|
|
275
|
+
* to check for suspended users instead of the GraphQL `suspendedAt` field.
|
|
276
|
+
* REST responses are cached using conditional HTTP requests to minimize rate limit usage.
|
|
277
|
+
* Default: `false`.
|
|
278
|
+
*/
|
|
279
|
+
experimental_checkForSuspendedUsersWithRest?: boolean;
|
|
280
|
+
|
|
273
281
|
/**
|
|
274
282
|
* (Optional) Configuration for the default user transformer.
|
|
275
283
|
* These options only apply when using the built-in transformer;
|
|
@@ -345,6 +353,14 @@ export interface Config {
|
|
|
345
353
|
*/
|
|
346
354
|
excludeSuspendedUsers?: boolean;
|
|
347
355
|
|
|
356
|
+
/**
|
|
357
|
+
* (Optional) When set to true alongside `excludeSuspendedUsers`, use the GitHub REST API
|
|
358
|
+
* to check for suspended users instead of the GraphQL `suspendedAt` field.
|
|
359
|
+
* REST responses are cached using conditional HTTP requests to minimize rate limit usage.
|
|
360
|
+
* Default: `false`.
|
|
361
|
+
*/
|
|
362
|
+
experimental_checkForSuspendedUsersWithRest?: boolean;
|
|
363
|
+
|
|
348
364
|
/**
|
|
349
365
|
* (Optional) Configuration for the default user transformer.
|
|
350
366
|
* These options only apply when using the built-in transformer;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
|
-
import { AuthService, LoggerService, SchedulerServiceTaskRunner, SchedulerService } from '@backstage/backend-plugin-api';
|
|
2
|
+
import { AuthService, LoggerService, SchedulerServiceTaskRunner, SchedulerService, CacheService } from '@backstage/backend-plugin-api';
|
|
3
3
|
import * as _backstage_catalog_model from '@backstage/catalog-model';
|
|
4
4
|
import { Entity } from '@backstage/catalog-model';
|
|
5
5
|
import { GithubCredentialsProvider, ScmIntegrationRegistry, GithubIntegrationConfig } from '@backstage/integration';
|
|
@@ -451,6 +451,19 @@ interface GithubMultiOrgEntityProviderOptions {
|
|
|
451
451
|
* Only for GitHub Enterprise instances. Will error if used against GitHub.com API.
|
|
452
452
|
*/
|
|
453
453
|
excludeSuspendedUsers?: boolean;
|
|
454
|
+
/**
|
|
455
|
+
* Optional cache service used for conditional HTTP request caching when
|
|
456
|
+
* checking suspended users via the REST API.
|
|
457
|
+
*/
|
|
458
|
+
cache?: CacheService;
|
|
459
|
+
/**
|
|
460
|
+
* When set to true alongside `excludeSuspendedUsers`, use the GitHub REST API
|
|
461
|
+
* to check for suspended users instead of the GraphQL `suspendedAt` field.
|
|
462
|
+
* REST responses are cached using conditional HTTP requests to minimize rate
|
|
463
|
+
* limit usage.
|
|
464
|
+
* @defaultValue false
|
|
465
|
+
*/
|
|
466
|
+
experimental_checkForSuspendedUsersWithRest?: boolean;
|
|
454
467
|
}
|
|
455
468
|
/**
|
|
456
469
|
* Ingests org data (users and groups) from GitHub.
|
|
@@ -475,10 +488,17 @@ declare class GithubMultiOrgEntityProvider implements EntityProvider {
|
|
|
475
488
|
alwaysUseDefaultNamespace?: boolean;
|
|
476
489
|
pageSizes?: Partial<GithubPageSizes>;
|
|
477
490
|
excludeSuspendedUsers?: boolean;
|
|
491
|
+
cache?: CacheService;
|
|
492
|
+
experimental_checkForSuspendedUsersWithRest?: boolean;
|
|
478
493
|
});
|
|
479
494
|
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */
|
|
480
495
|
getProviderName(): string;
|
|
481
496
|
private getPageSizes;
|
|
497
|
+
private get useRestSuspendedCheck();
|
|
498
|
+
private readonly restClients;
|
|
499
|
+
private getRestClient;
|
|
500
|
+
private isGitHubEnterprise;
|
|
501
|
+
private shouldExclude;
|
|
482
502
|
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */
|
|
483
503
|
connect(connection: EntityProviderConnection): Promise<void>;
|
|
484
504
|
/**
|
|
@@ -565,6 +585,19 @@ interface GithubOrgEntityProviderOptions {
|
|
|
565
585
|
* Only for GitHub Enterprise instances. Will error if used against GitHub.com API.
|
|
566
586
|
*/
|
|
567
587
|
excludeSuspendedUsers?: boolean;
|
|
588
|
+
/**
|
|
589
|
+
* Optional cache service used for conditional HTTP request caching when
|
|
590
|
+
* checking suspended users via the REST API.
|
|
591
|
+
*/
|
|
592
|
+
cache?: CacheService;
|
|
593
|
+
/**
|
|
594
|
+
* When set to true alongside `excludeSuspendedUsers`, use the GitHub REST API
|
|
595
|
+
* to check for suspended users instead of the GraphQL `suspendedAt` field.
|
|
596
|
+
* REST responses are cached using conditional HTTP requests to minimize rate
|
|
597
|
+
* limit usage.
|
|
598
|
+
* @defaultValue false
|
|
599
|
+
*/
|
|
600
|
+
experimental_checkForSuspendedUsersWithRest?: boolean;
|
|
568
601
|
}
|
|
569
602
|
/**
|
|
570
603
|
* Ingests org data (users and groups) from GitHub.
|
|
@@ -574,6 +607,7 @@ interface GithubOrgEntityProviderOptions {
|
|
|
574
607
|
declare class GithubOrgEntityProvider implements EntityProvider {
|
|
575
608
|
private options;
|
|
576
609
|
private readonly credentialsProvider;
|
|
610
|
+
private cachedRestClient?;
|
|
577
611
|
private connection?;
|
|
578
612
|
private scheduleFn?;
|
|
579
613
|
static fromConfig(config: Config, options: GithubOrgEntityProviderOptions): GithubOrgEntityProvider;
|
|
@@ -588,10 +622,16 @@ declare class GithubOrgEntityProvider implements EntityProvider {
|
|
|
588
622
|
teamTransformer?: TeamTransformer;
|
|
589
623
|
pageSizes?: Partial<GithubPageSizes>;
|
|
590
624
|
excludeSuspendedUsers?: boolean;
|
|
625
|
+
cache?: CacheService;
|
|
626
|
+
experimental_checkForSuspendedUsersWithRest?: boolean;
|
|
591
627
|
});
|
|
592
628
|
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */
|
|
593
629
|
getProviderName(): string;
|
|
594
630
|
private getPageSizes;
|
|
631
|
+
private get useRestSuspendedCheck();
|
|
632
|
+
private isGitHubEnterprise;
|
|
633
|
+
private getRestClient;
|
|
634
|
+
private shouldExclude;
|
|
595
635
|
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */
|
|
596
636
|
connect(connection: EntityProviderConnection): Promise<void>;
|
|
597
637
|
/**
|
package/dist/lib/github.cjs.js
CHANGED
|
@@ -12,8 +12,9 @@ const DEFAULT_PAGE_SIZES = {
|
|
|
12
12
|
organizationMembers: 50,
|
|
13
13
|
repositories: 25
|
|
14
14
|
};
|
|
15
|
-
async function getOrganizationUsers(client, org, tokenType, userTransformer = defaultTransformers.defaultUserTransformer, pageSizes = DEFAULT_PAGE_SIZES, excludeSuspendedUsers = false) {
|
|
16
|
-
const
|
|
15
|
+
async function getOrganizationUsers(client, org, tokenType, userTransformer = defaultTransformers.defaultUserTransformer, pageSizes = DEFAULT_PAGE_SIZES, excludeSuspendedUsers = false, restClient) {
|
|
16
|
+
const useRestForSuspension = excludeSuspendedUsers && !!restClient;
|
|
17
|
+
const suspendedAtField = excludeSuspendedUsers && !useRestForSuspension ? "suspendedAt," : "";
|
|
17
18
|
const query = `
|
|
18
19
|
query users($org: String!, $email: Boolean!, $cursor: String, $organizationMembersPageSize: Int!) {
|
|
19
20
|
organization(login: $org) {
|
|
@@ -32,6 +33,13 @@ async function getOrganizationUsers(client, org, tokenType, userTransformer = de
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
}`;
|
|
36
|
+
let restFilter;
|
|
37
|
+
if (useRestForSuspension) {
|
|
38
|
+
const isEnterprise = await isGitHubEnterprise(restClient);
|
|
39
|
+
if (isEnterprise) {
|
|
40
|
+
restFilter = async (user) => !await isSuspended(user.login, restClient, { org });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
35
43
|
const users = await queryWithPaging({
|
|
36
44
|
client,
|
|
37
45
|
query,
|
|
@@ -43,7 +51,7 @@ async function getOrganizationUsers(client, org, tokenType, userTransformer = de
|
|
|
43
51
|
email: tokenType === "token",
|
|
44
52
|
organizationMembersPageSize: pageSizes.organizationMembers
|
|
45
53
|
},
|
|
46
|
-
filter: (u) => excludeSuspendedUsers ? !u.suspendedAt : true
|
|
54
|
+
filter: useRestForSuspension ? restFilter : (u) => excludeSuspendedUsers ? !u.suspendedAt : true
|
|
47
55
|
});
|
|
48
56
|
return { users };
|
|
49
57
|
}
|
|
@@ -451,7 +459,7 @@ async function queryWithPaging(params) {
|
|
|
451
459
|
throw new Error(`Found no match for ${JSON.stringify(variables)}`);
|
|
452
460
|
}
|
|
453
461
|
for (const node of conn.nodes) {
|
|
454
|
-
if (filter && !filter(node)) {
|
|
462
|
+
if (filter && !await filter(node)) {
|
|
455
463
|
continue;
|
|
456
464
|
}
|
|
457
465
|
const transformedNode = await transformer(node, {
|
|
@@ -523,11 +531,134 @@ const createGraphqlClient = (args) => {
|
|
|
523
531
|
});
|
|
524
532
|
return client;
|
|
525
533
|
};
|
|
534
|
+
function octokitThrottlingOptions(logger) {
|
|
535
|
+
return {
|
|
536
|
+
onRateLimit: (retryAfter, rateLimitData, _, retryCount) => {
|
|
537
|
+
logger.warn(
|
|
538
|
+
`Request quota exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`
|
|
539
|
+
);
|
|
540
|
+
if (retryCount < 2) {
|
|
541
|
+
logger.warn(
|
|
542
|
+
`Retrying after ${retryAfter} seconds for the ${retryCount} time due to Rate Limit!`
|
|
543
|
+
);
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
},
|
|
548
|
+
onSecondaryRateLimit: (retryAfter, rateLimitData, _, retryCount) => {
|
|
549
|
+
logger.warn(
|
|
550
|
+
`Secondary Rate Limit Exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`
|
|
551
|
+
);
|
|
552
|
+
if (retryCount < 2) {
|
|
553
|
+
logger.warn(
|
|
554
|
+
`Retrying after ${retryAfter} seconds for the ${retryCount} time due to Secondary Rate Limit!`
|
|
555
|
+
);
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function createRestClient(options) {
|
|
563
|
+
const getCredentials = () => options.credentialsProvider.getCredentials({
|
|
564
|
+
url: options.orgUrl
|
|
565
|
+
});
|
|
566
|
+
const authStrategy = () => {
|
|
567
|
+
const auth = () => getCredentials();
|
|
568
|
+
auth.hook = async (request, hookOptions) => {
|
|
569
|
+
const { headers } = await getCredentials();
|
|
570
|
+
return request({
|
|
571
|
+
...hookOptions,
|
|
572
|
+
headers: { ...hookOptions.headers, ...headers }
|
|
573
|
+
});
|
|
574
|
+
};
|
|
575
|
+
return auth;
|
|
576
|
+
};
|
|
577
|
+
const ThrottledOctokit = core.Octokit.plugin(pluginThrottling.throttling, pluginRetry.retry);
|
|
578
|
+
const octokit = new ThrottledOctokit({
|
|
579
|
+
baseUrl: options.baseUrl,
|
|
580
|
+
authStrategy,
|
|
581
|
+
throttle: octokitThrottlingOptions(options.logger)
|
|
582
|
+
});
|
|
583
|
+
if (options.cache) {
|
|
584
|
+
installConditionalRequestCache(octokit, options.cache);
|
|
585
|
+
}
|
|
586
|
+
return octokit;
|
|
587
|
+
}
|
|
588
|
+
function installConditionalRequestCache(octokit, cache) {
|
|
589
|
+
octokit.hook.wrap("request", async (request, wrappedOptions) => {
|
|
590
|
+
const resolvedUrl = (wrappedOptions.url || "").replace(
|
|
591
|
+
/\{([^}]+)\}/g,
|
|
592
|
+
(_, key) => encodeURIComponent(wrappedOptions[key])
|
|
593
|
+
);
|
|
594
|
+
const cacheKey = `catalog-backend-module-github:${wrappedOptions.method}:${wrappedOptions.baseUrl}${resolvedUrl}`;
|
|
595
|
+
const cached = await cache.get(cacheKey).catch(() => void 0);
|
|
596
|
+
if (cached?.lastModified) {
|
|
597
|
+
wrappedOptions.headers["if-modified-since"] = cached.lastModified;
|
|
598
|
+
} else if (cached?.etag) {
|
|
599
|
+
wrappedOptions.headers["if-none-match"] = cached.etag;
|
|
600
|
+
}
|
|
601
|
+
try {
|
|
602
|
+
const response = await request(wrappedOptions);
|
|
603
|
+
const lastModified = response.headers["last-modified"];
|
|
604
|
+
const etag = response.headers.etag;
|
|
605
|
+
if (lastModified || etag) {
|
|
606
|
+
cache.set(
|
|
607
|
+
cacheKey,
|
|
608
|
+
{
|
|
609
|
+
lastModified,
|
|
610
|
+
etag,
|
|
611
|
+
headers: JSON.parse(JSON.stringify(response.headers)),
|
|
612
|
+
data: JSON.parse(JSON.stringify(response.data))
|
|
613
|
+
},
|
|
614
|
+
// The TTL can be long here, since we only use the cache for
|
|
615
|
+
// conditional GitHub requests - it's never returned unless we get a
|
|
616
|
+
// 304 back from GitHub indicating that it's still correct.
|
|
617
|
+
{ ttl: { years: 1 } }
|
|
618
|
+
).catch(() => {
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
return response;
|
|
622
|
+
} catch (error) {
|
|
623
|
+
if (error?.status === 304 && cached) {
|
|
624
|
+
return {
|
|
625
|
+
...error.response,
|
|
626
|
+
headers: cached.headers,
|
|
627
|
+
data: cached.data
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
async function isSuspended(username, octokit, options) {
|
|
635
|
+
const [userResponse, membershipResponse] = await Promise.all([
|
|
636
|
+
octokit.request("GET /users/{username}", { username }),
|
|
637
|
+
octokit.request("GET /orgs/{org}/memberships/{username}", {
|
|
638
|
+
org: options.org,
|
|
639
|
+
username
|
|
640
|
+
})
|
|
641
|
+
]);
|
|
642
|
+
const userSuspendedAt = userResponse.data.suspended_at;
|
|
643
|
+
const membershipRole = membershipResponse.data.role;
|
|
644
|
+
const userSuspended = !!userSuspendedAt;
|
|
645
|
+
const orgMembershipSuspended = membershipRole === "suspended";
|
|
646
|
+
return userSuspended || orgMembershipSuspended;
|
|
647
|
+
}
|
|
648
|
+
async function isGitHubEnterprise(octokit) {
|
|
649
|
+
try {
|
|
650
|
+
const response = await octokit.request("GET /versions");
|
|
651
|
+
return !!response.headers["x-github-enterprise-version"];
|
|
652
|
+
} catch {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
526
656
|
|
|
527
657
|
exports.DEFAULT_PAGE_SIZES = DEFAULT_PAGE_SIZES;
|
|
528
658
|
exports.createAddEntitiesOperation = createAddEntitiesOperation;
|
|
529
659
|
exports.createGraphqlClient = createGraphqlClient;
|
|
530
660
|
exports.createRemoveEntitiesOperation = createRemoveEntitiesOperation;
|
|
661
|
+
exports.createRestClient = createRestClient;
|
|
531
662
|
exports.getOrganizationRepositories = getOrganizationRepositories;
|
|
532
663
|
exports.getOrganizationRepository = getOrganizationRepository;
|
|
533
664
|
exports.getOrganizationTeam = getOrganizationTeam;
|
|
@@ -537,5 +668,7 @@ exports.getOrganizationTeamsFromUsers = getOrganizationTeamsFromUsers;
|
|
|
537
668
|
exports.getOrganizationUsers = getOrganizationUsers;
|
|
538
669
|
exports.getOrganizationsFromUser = getOrganizationsFromUser;
|
|
539
670
|
exports.getTeamMembers = getTeamMembers;
|
|
671
|
+
exports.isGitHubEnterprise = isGitHubEnterprise;
|
|
672
|
+
exports.isSuspended = isSuspended;
|
|
540
673
|
exports.queryWithPaging = queryWithPaging;
|
|
541
674
|
//# sourceMappingURL=github.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.cjs.js","sources":["../../src/lib/github.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { GithubCredentialType } from '@backstage/integration';\nimport { graphql } from '@octokit/graphql';\nimport {\n defaultOrganizationTeamTransformer,\n defaultUserTransformer,\n TeamTransformer,\n TransformerContext,\n UserTransformer,\n} from './defaultTransformers';\nimport { withLocations } from './withLocations';\n\nimport { DeferredEntity } from '@backstage/plugin-catalog-node';\nimport { Octokit } from '@octokit/core';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { throttling } from '@octokit/plugin-throttling';\nimport { retry } from '@octokit/plugin-retry';\n\n/**\n * Configuration for GitHub GraphQL API page sizes.\n *\n * @public\n */\nexport type GithubPageSizes = {\n /**\n * Number of teams to fetch per page when querying organization teams.\n * Default: 25\n */\n teams: number;\n /**\n * Number of team members to fetch per page when querying team members.\n * Default: 50\n */\n teamMembers: number;\n /**\n * Number of organization members to fetch per page when querying org members.\n * Default: 50\n */\n organizationMembers: number;\n /**\n * Number of repositories to fetch per page when querying repositories.\n * Default: 25\n */\n repositories: number;\n};\n\n/**\n * Default page sizes for GitHub GraphQL API queries.\n * These values are reduced to prevent RESOURCE_LIMITS_EXCEEDED errors with large organizations.\n *\n * @public\n */\nexport const DEFAULT_PAGE_SIZES: GithubPageSizes = {\n teams: 25,\n teamMembers: 50,\n organizationMembers: 50,\n repositories: 25,\n};\n\n// Graphql types\n\nexport type QueryResponse = {\n organization?: OrganizationResponse;\n repositoryOwner?: RepositoryOwnerResponse;\n user?: UserResponse;\n};\n\ntype RepositoryOwnerResponse = {\n repositories?: Connection<RepositoryResponse>;\n repository?: RepositoryResponse;\n};\n\nexport type OrganizationResponse = {\n membersWithRole?: Connection<GithubUser>;\n team?: GithubTeamResponse;\n teams?: Connection<GithubTeamResponse>;\n repositories?: Connection<RepositoryResponse>;\n};\n\nexport type UserResponse = {\n organizations?: Connection<GithubOrg>;\n};\n\nexport type PageInfo = {\n hasNextPage: boolean;\n endCursor?: string;\n};\n\nexport type GithubOrg = {\n login: string;\n};\n\n/**\n * Github User\n *\n * @public\n */\nexport type GithubUser = {\n login: string;\n id?: string;\n bio?: string;\n avatarUrl?: string;\n email?: string;\n name?: string;\n organizationVerifiedDomainEmails?: string[];\n suspendedAt?: string;\n};\n\n/**\n * Github Team\n *\n * @public\n */\nexport type GithubTeam = {\n slug: string;\n combinedSlug: string;\n name?: string;\n description?: string;\n avatarUrl?: string;\n editTeamUrl?: string;\n parentTeam?: GithubTeam;\n members: GithubUser[];\n};\n\nexport type GithubTeamResponse = Omit<GithubTeam, 'members'> & {\n members: Connection<GithubUser>;\n};\n\nexport type RepositoryResponse = {\n name: string;\n url: string;\n isArchived: boolean;\n isFork: boolean;\n repositoryTopics: RepositoryTopics;\n defaultBranchRef: {\n name: string;\n } | null;\n catalogInfoFile: {\n __typename: string;\n id: string;\n text: string;\n } | null;\n visibility: string;\n};\n\ntype RepositoryTopics = {\n nodes: TopicNodes[];\n};\n\ntype TopicNodes = {\n topic: {\n name: string;\n };\n};\n\nexport type Connection<T> = {\n pageInfo: PageInfo;\n nodes: T[];\n};\n\n/**\n * Gets all the users out of a Github organization.\n *\n * Note that the users will not have their memberships filled in.\n *\n * @param client - An octokit graphql client\n * @param org - The slug of the org to read\n * @param tokenType - The type of GitHub credential\n * @param userTransformer - Optional transformer for user entities\n * @param pageSizes - Optional page sizes configuration\n * @param excludeSuspendedUsers - Optional flag to exclude suspended users (only for GitHub Enterprise instances)\n */\nexport async function getOrganizationUsers(\n client: typeof graphql,\n org: string,\n tokenType: GithubCredentialType,\n userTransformer: UserTransformer = defaultUserTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n excludeSuspendedUsers: boolean = false,\n): Promise<{ users: Entity[] }> {\n const suspendedAtField = excludeSuspendedUsers ? 'suspendedAt,' : '';\n const query = `\n query users($org: String!, $email: Boolean!, $cursor: String, $organizationMembersPageSize: Int!) {\n organization(login: $org) {\n membersWithRole(first: $organizationMembersPageSize, after: $cursor) {\n pageInfo { hasNextPage, endCursor }\n nodes {\n avatarUrl,\n bio,\n email @include(if: $email),\n id,\n login,\n name,\n ${suspendedAtField}\n organizationVerifiedDomainEmails(login: $org)\n }\n }\n }\n }`;\n\n // There is no user -> teams edge, so we leave the memberships empty for\n // now and let the team iteration handle it instead\n\n const users = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.membersWithRole,\n transformer: userTransformer,\n variables: {\n org,\n email: tokenType === 'token',\n organizationMembersPageSize: pageSizes.organizationMembers,\n },\n filter: u => (excludeSuspendedUsers ? !u.suspendedAt : true),\n });\n\n return { users };\n}\n\n/**\n * Gets all the teams out of a Github organization.\n *\n * Note that the teams will not have any relations apart from parent filled in.\n *\n * @param client - An octokit graphql client\n * @param org - The slug of the org to read\n * @param teamTransformer - Optional transformer for team entities\n * @param pageSizes - Optional page sizes configuration\n */\nexport async function getOrganizationTeams(\n client: typeof graphql,\n org: string,\n teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{\n teams: Entity[];\n}> {\n const query = `\n query teams($org: String!, $cursor: String, $teamsPageSize: Int!, $membersPageSize: Int!) {\n organization(login: $org) {\n teams(first: $teamsPageSize, after: $cursor) {\n pageInfo { hasNextPage, endCursor }\n nodes {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam { slug }\n members(first: $membersPageSize, membership: IMMEDIATE) {\n pageInfo { hasNextPage }\n nodes {\n avatarUrl,\n bio,\n email,\n id,\n login,\n name,\n organizationVerifiedDomainEmails(login: $org)\n }\n }\n }\n }\n }\n }`;\n\n const materialisedTeams = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const memberNames: GithubUser[] = [];\n\n if (!item.members.pageInfo.hasNextPage) {\n // We got all the members in one go, run the fast path\n for (const user of item.members.nodes) {\n memberNames.push(user);\n }\n } else {\n // There were more immediate members than page size - run the slow\n // path of fetching them explicitly\n const { members } = await getTeamMembers(\n ctx.client,\n ctx.org,\n item.slug,\n pageSizes,\n );\n for (const userLogin of members) {\n memberNames.push(userLogin);\n }\n }\n\n const team: GithubTeam = {\n ...item,\n members: memberNames,\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const teams = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.teams,\n transformer: materialisedTeams,\n variables: {\n org,\n teamsPageSize: pageSizes.teams,\n membersPageSize: pageSizes.teamMembers,\n },\n });\n\n return { teams };\n}\n\nexport async function getOrganizationTeamsFromUsers(\n client: typeof graphql,\n org: string,\n userLogins: string[],\n teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{\n teams: Entity[];\n}> {\n const query = `\n query teams($org: String!, $cursor: String, $userLogins: [String!] = \"\", $teamsPageSize: Int!, $membersPageSize: Int!) {\n organization(login: $org) {\n teams(first: $teamsPageSize, after: $cursor, userLogins: $userLogins) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam {\n slug\n }\n members(first: $membersPageSize, membership: IMMEDIATE) {\n pageInfo {\n hasNextPage\n }\n nodes {\n avatarUrl,\n bio,\n email,\n id,\n login,\n name,\n organizationVerifiedDomainEmails(login: $org)\n }\n }\n }\n }\n }\n}`;\n\n const materialisedTeams = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const memberNames: GithubUser[] = [];\n\n if (!item.members.pageInfo.hasNextPage) {\n // We got all the members in one go, run the fast path\n for (const user of item.members.nodes) {\n memberNames.push(user);\n }\n } else {\n // There were more immediate members than page size - run the slow\n // path of fetching them explicitly\n const { members } = await getTeamMembers(\n ctx.client,\n ctx.org,\n item.slug,\n pageSizes,\n );\n for (const userLogin of members) {\n memberNames.push(userLogin);\n }\n }\n\n const team: GithubTeam = {\n ...item,\n members: memberNames,\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const teams = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.teams,\n transformer: materialisedTeams,\n variables: {\n org,\n userLogins,\n teamsPageSize: pageSizes.teams,\n membersPageSize: pageSizes.teamMembers,\n },\n });\n\n return { teams };\n}\n\nexport async function getOrganizationTeamsForUser(\n client: typeof graphql,\n org: string,\n userLogin: string,\n teamTransformer: TeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{ teams: Entity[] }> {\n const query = `\n query teams($org: String!, $cursor: String, $userLogins: [String!] = \"\", $teamsPageSize: Int!) {\n organization(login: $org) {\n teams(first: $teamsPageSize, after: $cursor, userLogins: $userLogins) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam {\n slug\n }\n }\n }\n }\n}`;\n\n const materialisedTeams = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const team: GithubTeam = {\n ...item,\n members: [{ login: userLogin }],\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const teams = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.teams,\n transformer: materialisedTeams,\n variables: { org, userLogins: [userLogin], teamsPageSize: pageSizes.teams },\n });\n\n return { teams };\n}\n\nexport async function getOrganizationsFromUser(\n client: typeof graphql,\n user: string,\n): Promise<{\n orgs: string[];\n}> {\n const query = `\n query orgs($user: String!) {\n user(login: $user) {\n organizations(first: 100) {\n nodes { login }\n pageInfo { hasNextPage, endCursor }\n }\n }\n }`;\n\n const orgs = await queryWithPaging({\n client,\n query,\n org: '',\n connection: r => r.user?.organizations,\n transformer: async o => o.login,\n variables: { user },\n });\n\n return { orgs };\n}\n\nexport async function getOrganizationTeam(\n client: typeof graphql,\n org: string,\n teamSlug: string,\n teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{\n team: Entity;\n}> {\n const query = `\n query teams($org: String!, $teamSlug: String!, $membersPageSize: Int!) {\n organization(login: $org) {\n team(slug:$teamSlug) {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam { slug }\n members(first: $membersPageSize, membership: IMMEDIATE) {\n pageInfo { hasNextPage }\n nodes { login }\n }\n }\n }\n }`;\n\n const materialisedTeam = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const memberNames: GithubUser[] = [];\n\n if (!item.members.pageInfo.hasNextPage) {\n // We got all the members in one go, run the fast path\n for (const user of item.members.nodes) {\n memberNames.push(user);\n }\n } else {\n // There were more immediate members than page size - run the slow\n // path of fetching them explicitly\n const { members } = await getTeamMembers(\n ctx.client,\n ctx.org,\n item.slug,\n pageSizes,\n );\n for (const userLogin of members) {\n memberNames.push(userLogin);\n }\n }\n\n const team: GithubTeam = {\n ...item,\n members: memberNames,\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const response: QueryResponse = await client(query, {\n org,\n teamSlug,\n membersPageSize: pageSizes.teamMembers,\n });\n\n if (!response.organization?.team)\n throw new Error(`Found no match for team ${teamSlug}`);\n\n const team = await materialisedTeam(response.organization?.team, {\n query,\n client,\n org,\n });\n\n if (!team) throw new Error(`Can't transform for team ${teamSlug}`);\n\n return { team };\n}\n\nexport async function getOrganizationRepositories(\n client: typeof graphql,\n org: string,\n catalogPath: string,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n branch?: string,\n): Promise<{ repositories: RepositoryResponse[] }> {\n let relativeCatalogPathRef: string;\n // We must strip the leading slash or the query for objects does not work\n if (catalogPath.startsWith('/')) {\n relativeCatalogPathRef = catalogPath.substring(1);\n } else {\n relativeCatalogPathRef = catalogPath;\n }\n const branchRef = branch ?? 'HEAD';\n const catalogPathRef = `${branchRef}:${relativeCatalogPathRef}`;\n const query = `\n query repositories($org: String!, $catalogPathRef: String!, $cursor: String, $repositoriesPageSize: Int!) {\n repositoryOwner(login: $org) {\n login\n repositories(first: $repositoriesPageSize, after: $cursor) {\n nodes {\n name\n catalogInfoFile: object(expression: $catalogPathRef) {\n __typename\n ... on Blob {\n id\n text\n }\n }\n url\n isArchived\n isFork\n visibility\n repositoryTopics(first: 100) {\n nodes {\n ... on RepositoryTopic {\n topic {\n name\n }\n }\n }\n }\n defaultBranchRef {\n name\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }`;\n\n const repositories = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.repositoryOwner?.repositories,\n transformer: async x => x,\n variables: {\n org,\n catalogPathRef,\n repositoriesPageSize: pageSizes.repositories,\n },\n });\n\n return { repositories };\n}\n\nexport async function getOrganizationRepository(\n client: typeof graphql,\n org: string,\n repoName: string,\n catalogPath: string,\n branch?: string,\n): Promise<RepositoryResponse | null> {\n let relativeCatalogPathRef: string;\n // We must strip the leading slash or the query for objects does not work\n if (catalogPath.startsWith('/')) {\n relativeCatalogPathRef = catalogPath.substring(1);\n } else {\n relativeCatalogPathRef = catalogPath;\n }\n const branchRef = branch ?? 'HEAD';\n const catalogPathRef = `${branchRef}:${relativeCatalogPathRef}`;\n const query = `\n query repository($org: String!, $repoName: String!, $catalogPathRef: String!) {\n repositoryOwner(login: $org) {\n repository(name: $repoName) {\n name\n catalogInfoFile: object(expression: $catalogPathRef) {\n __typename\n ... on Blob {\n id\n text\n }\n }\n url\n isArchived\n isFork\n visibility\n repositoryTopics(first: 100) {\n nodes {\n ... on RepositoryTopic {\n topic {\n name\n }\n }\n }\n }\n defaultBranchRef {\n name\n }\n }\n }\n }`;\n\n const response: QueryResponse = await client(query, {\n org,\n repoName,\n catalogPathRef,\n });\n\n return response.repositoryOwner?.repository || null;\n}\n\n/**\n * Gets all the users out of a Github organization team.\n *\n * Note that the users will not have their memberships filled in.\n *\n * @param client - An octokit graphql client\n * @param org - The slug of the org to read\n * @param teamSlug - The slug of the team to read\n * @param pageSizes - Optional page sizes configuration\n */\nexport async function getTeamMembers(\n client: typeof graphql,\n org: string,\n teamSlug: string,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{ members: GithubUser[] }> {\n const query = `\n query members($org: String!, $teamSlug: String!, $cursor: String, $membersPageSize: Int!) {\n organization(login: $org) {\n team(slug: $teamSlug) {\n members(first: $membersPageSize, after: $cursor, membership: IMMEDIATE) {\n pageInfo { hasNextPage, endCursor }\n nodes { login }\n }\n }\n }\n }`;\n\n const members = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.team?.members,\n transformer: async user => user,\n variables: { org, teamSlug, membersPageSize: pageSizes.teamMembers },\n });\n\n return { members };\n}\n\n//\n// Helpers\n//\n\n/**\n * Assists in repeatedly executing a query with a paged response.\n *\n * Requires that the query accepts a $cursor variable.\n *\n * @param params - Object containing all parameters\n * @param params.client - The octokit client\n * @param params.query - The query to execute\n * @param params.org - The slug of the org to read\n * @param params.connection - A function that, given the response, picks out the actual\n * Connection object that's being iterated\n * @param params.transformer - A function that, given one of the nodes in the Connection,\n * returns the model mapped form of it\n * @param params.variables - The variable values that the query needs, minus the cursor\n * @param params.filter - An optional filter function to filter the nodes before transforming them\n */\nexport async function queryWithPaging<\n GraphqlType,\n OutputType,\n Variables extends {},\n Response = QueryResponse,\n>(params: {\n client: typeof graphql;\n query: string;\n org: string;\n connection: (response: Response) => Connection<GraphqlType> | undefined;\n transformer: (\n item: GraphqlType,\n ctx: TransformerContext,\n ) => Promise<OutputType | undefined>;\n variables: Variables;\n filter?: (item: GraphqlType) => boolean;\n}): Promise<OutputType[]> {\n const { client, query, org, connection, transformer, variables, filter } =\n params;\n const result: OutputType[] = [];\n const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));\n\n let cursor: string | undefined = undefined;\n for (let j = 0; j < 1000 /* just for sanity */; ++j) {\n const response: Response = await client(query, {\n ...variables,\n cursor,\n });\n\n const conn = connection(response);\n if (!conn) {\n throw new Error(`Found no match for ${JSON.stringify(variables)}`);\n }\n\n for (const node of conn.nodes) {\n if (filter && !filter(node)) {\n continue;\n }\n const transformedNode = await transformer(node, {\n client,\n query,\n org,\n });\n\n if (transformedNode) {\n result.push(transformedNode);\n }\n }\n\n if (!conn.pageInfo.hasNextPage) {\n break;\n } else {\n await sleep(1000);\n cursor = conn.pageInfo.endCursor;\n }\n }\n\n return result;\n}\n\nexport type DeferredEntitiesBuilder = (\n org: string,\n entities: Entity[],\n) => { added: DeferredEntity[]; removed: DeferredEntity[] };\n\nexport const createAddEntitiesOperation =\n (id: string, host: string) => (org: string, entities: Entity[]) => ({\n removed: [],\n added: entities.map(entity => ({\n locationKey: `github-org-provider:${id}`,\n entity: withLocations(`https://${host}`, org, entity),\n })),\n });\n\nexport const createRemoveEntitiesOperation =\n (id: string, host: string) => (org: string, entities: Entity[]) => ({\n added: [],\n removed: entities.map(entity => ({\n locationKey: `github-org-provider:${id}`,\n entity: withLocations(`https://${host}`, org, entity),\n })),\n });\n\nexport const createReplaceEntitiesOperation =\n (id: string, host: string) => (org: string, entities: Entity[]) => {\n const entitiesToReplace = entities.map(entity => ({\n locationKey: `github-org-provider:${id}`,\n entity: withLocations(`https://${host}`, org, entity),\n }));\n\n return {\n removed: entitiesToReplace,\n added: entitiesToReplace,\n };\n };\n\n/**\n * Creates a GraphQL Client with Throttling and Retries\n */\nexport const createGraphqlClient = (args: {\n headers:\n | {\n [name: string]: string;\n }\n | undefined;\n baseUrl: string;\n logger: LoggerService;\n}): typeof graphql => {\n const { headers, baseUrl, logger } = args;\n const ThrottledOctokit = Octokit.plugin(throttling, retry);\n const octokit = new ThrottledOctokit({\n throttle: {\n onRateLimit: (retryAfter, rateLimitData, _, retryCount) => {\n logger.warn(\n `Request quota exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`,\n );\n\n if (retryCount < 2) {\n logger.warn(\n `Retrying after ${retryAfter} seconds for the ${retryCount} time due to Rate Limit!`,\n );\n return true;\n }\n\n return false;\n },\n onSecondaryRateLimit: (retryAfter, rateLimitData, _, retryCount) => {\n logger.warn(\n `Secondary Rate Limit Exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`,\n );\n\n if (retryCount < 2) {\n logger.warn(\n `Retrying after ${retryAfter} seconds for the ${retryCount} time due to Secondary Rate Limit!`,\n );\n return true;\n }\n\n return false;\n },\n },\n });\n\n const client = octokit.graphql.defaults({\n headers,\n baseUrl,\n });\n\n return client;\n};\n"],"names":["defaultUserTransformer","defaultOrganizationTeamTransformer","team","withLocations","Octokit","throttling","retry"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github.cjs.js","sources":["../../src/lib/github.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n GithubCredentials,\n GithubCredentialsProvider,\n GithubCredentialType,\n} from '@backstage/integration';\nimport { graphql } from '@octokit/graphql';\nimport { OctokitResponse, RequestParameters } from '@octokit/types';\nimport {\n defaultOrganizationTeamTransformer,\n defaultUserTransformer,\n TeamTransformer,\n TransformerContext,\n UserTransformer,\n} from './defaultTransformers';\nimport { withLocations } from './withLocations';\n\nimport { DeferredEntity } from '@backstage/plugin-catalog-node';\nimport { Octokit } from '@octokit/core';\nimport { CacheService, LoggerService } from '@backstage/backend-plugin-api';\nimport { throttling, ThrottlingOptions } from '@octokit/plugin-throttling';\nimport { retry } from '@octokit/plugin-retry';\nimport { JsonValue } from '@backstage/types';\n\n/**\n * Configuration for GitHub GraphQL API page sizes.\n *\n * @public\n */\nexport type GithubPageSizes = {\n /**\n * Number of teams to fetch per page when querying organization teams.\n * Default: 25\n */\n teams: number;\n /**\n * Number of team members to fetch per page when querying team members.\n * Default: 50\n */\n teamMembers: number;\n /**\n * Number of organization members to fetch per page when querying org members.\n * Default: 50\n */\n organizationMembers: number;\n /**\n * Number of repositories to fetch per page when querying repositories.\n * Default: 25\n */\n repositories: number;\n};\n\n/**\n * Default page sizes for GitHub GraphQL API queries.\n * These values are reduced to prevent RESOURCE_LIMITS_EXCEEDED errors with large organizations.\n *\n * @public\n */\nexport const DEFAULT_PAGE_SIZES: GithubPageSizes = {\n teams: 25,\n teamMembers: 50,\n organizationMembers: 50,\n repositories: 25,\n};\n\n// Graphql types\n\nexport type QueryResponse = {\n organization?: OrganizationResponse;\n repositoryOwner?: RepositoryOwnerResponse;\n user?: UserResponse;\n};\n\ntype RepositoryOwnerResponse = {\n repositories?: Connection<RepositoryResponse>;\n repository?: RepositoryResponse;\n};\n\nexport type OrganizationResponse = {\n membersWithRole?: Connection<GithubUser>;\n team?: GithubTeamResponse;\n teams?: Connection<GithubTeamResponse>;\n repositories?: Connection<RepositoryResponse>;\n};\n\nexport type UserResponse = {\n organizations?: Connection<GithubOrg>;\n};\n\nexport type PageInfo = {\n hasNextPage: boolean;\n endCursor?: string;\n};\n\nexport type GithubOrg = {\n login: string;\n};\n\n/**\n * Github User\n *\n * @public\n */\nexport type GithubUser = {\n login: string;\n id?: string;\n bio?: string;\n avatarUrl?: string;\n email?: string;\n name?: string;\n organizationVerifiedDomainEmails?: string[];\n suspendedAt?: string;\n};\n\n/**\n * Github Team\n *\n * @public\n */\nexport type GithubTeam = {\n slug: string;\n combinedSlug: string;\n name?: string;\n description?: string;\n avatarUrl?: string;\n editTeamUrl?: string;\n parentTeam?: GithubTeam;\n members: GithubUser[];\n};\n\nexport type GithubTeamResponse = Omit<GithubTeam, 'members'> & {\n members: Connection<GithubUser>;\n};\n\nexport type RepositoryResponse = {\n name: string;\n url: string;\n isArchived: boolean;\n isFork: boolean;\n repositoryTopics: RepositoryTopics;\n defaultBranchRef: {\n name: string;\n } | null;\n catalogInfoFile: {\n __typename: string;\n id: string;\n text: string;\n } | null;\n visibility: string;\n};\n\ntype RepositoryTopics = {\n nodes: TopicNodes[];\n};\n\ntype TopicNodes = {\n topic: {\n name: string;\n };\n};\n\nexport type Connection<T> = {\n pageInfo: PageInfo;\n nodes: T[];\n};\n\n/**\n * Gets all the users out of a Github organization.\n *\n * Note that the users will not have their memberships filled in.\n *\n * @param client - An octokit graphql client\n * @param org - The slug of the org to read\n * @param tokenType - The type of GitHub credential\n * @param userTransformer - Optional transformer for user entities\n * @param pageSizes - Optional page sizes configuration\n * @param excludeSuspendedUsers - Optional flag to exclude suspended users (only for GitHub Enterprise instances)\n * @param restClient - Optional Octokit REST client; when provided alongside excludeSuspendedUsers, the REST API is used instead of the GraphQL suspendedAt field\n */\nexport async function getOrganizationUsers(\n client: typeof graphql,\n org: string,\n tokenType: GithubCredentialType,\n userTransformer: UserTransformer = defaultUserTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n excludeSuspendedUsers: boolean = false,\n restClient?: Octokit,\n): Promise<{ users: Entity[] }> {\n const useRestForSuspension = excludeSuspendedUsers && !!restClient;\n const suspendedAtField =\n excludeSuspendedUsers && !useRestForSuspension ? 'suspendedAt,' : '';\n const query = `\n query users($org: String!, $email: Boolean!, $cursor: String, $organizationMembersPageSize: Int!) {\n organization(login: $org) {\n membersWithRole(first: $organizationMembersPageSize, after: $cursor) {\n pageInfo { hasNextPage, endCursor }\n nodes {\n avatarUrl,\n bio,\n email @include(if: $email),\n id,\n login,\n name,\n ${suspendedAtField}\n organizationVerifiedDomainEmails(login: $org)\n }\n }\n }\n }`;\n\n let restFilter:\n | ((user: GithubUser) => Promise<boolean> | boolean)\n | undefined;\n\n if (useRestForSuspension) {\n const isEnterprise = await isGitHubEnterprise(restClient);\n if (isEnterprise) {\n restFilter = async (user: GithubUser) =>\n !(await isSuspended(user.login, restClient, { org }));\n }\n }\n\n // There is no user -> teams edge, so we leave the memberships empty for\n // now and let the team iteration handle it instead\n\n const users = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.membersWithRole,\n transformer: userTransformer,\n variables: {\n org,\n email: tokenType === 'token',\n organizationMembersPageSize: pageSizes.organizationMembers,\n },\n filter: useRestForSuspension\n ? restFilter\n : u => (excludeSuspendedUsers ? !u.suspendedAt : true),\n });\n\n return { users };\n}\n\n/**\n * Gets all the teams out of a Github organization.\n *\n * Note that the teams will not have any relations apart from parent filled in.\n *\n * @param client - An octokit graphql client\n * @param org - The slug of the org to read\n * @param teamTransformer - Optional transformer for team entities\n * @param pageSizes - Optional page sizes configuration\n */\nexport async function getOrganizationTeams(\n client: typeof graphql,\n org: string,\n teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{\n teams: Entity[];\n}> {\n const query = `\n query teams($org: String!, $cursor: String, $teamsPageSize: Int!, $membersPageSize: Int!) {\n organization(login: $org) {\n teams(first: $teamsPageSize, after: $cursor) {\n pageInfo { hasNextPage, endCursor }\n nodes {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam { slug }\n members(first: $membersPageSize, membership: IMMEDIATE) {\n pageInfo { hasNextPage }\n nodes {\n avatarUrl,\n bio,\n email,\n id,\n login,\n name,\n organizationVerifiedDomainEmails(login: $org)\n }\n }\n }\n }\n }\n }`;\n\n const materialisedTeams = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const memberNames: GithubUser[] = [];\n\n if (!item.members.pageInfo.hasNextPage) {\n // We got all the members in one go, run the fast path\n for (const user of item.members.nodes) {\n memberNames.push(user);\n }\n } else {\n // There were more immediate members than page size - run the slow\n // path of fetching them explicitly\n const { members } = await getTeamMembers(\n ctx.client,\n ctx.org,\n item.slug,\n pageSizes,\n );\n for (const userLogin of members) {\n memberNames.push(userLogin);\n }\n }\n\n const team: GithubTeam = {\n ...item,\n members: memberNames,\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const teams = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.teams,\n transformer: materialisedTeams,\n variables: {\n org,\n teamsPageSize: pageSizes.teams,\n membersPageSize: pageSizes.teamMembers,\n },\n });\n\n return { teams };\n}\n\nexport async function getOrganizationTeamsFromUsers(\n client: typeof graphql,\n org: string,\n userLogins: string[],\n teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{\n teams: Entity[];\n}> {\n const query = `\n query teams($org: String!, $cursor: String, $userLogins: [String!] = \"\", $teamsPageSize: Int!, $membersPageSize: Int!) {\n organization(login: $org) {\n teams(first: $teamsPageSize, after: $cursor, userLogins: $userLogins) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam {\n slug\n }\n members(first: $membersPageSize, membership: IMMEDIATE) {\n pageInfo {\n hasNextPage\n }\n nodes {\n avatarUrl,\n bio,\n email,\n id,\n login,\n name,\n organizationVerifiedDomainEmails(login: $org)\n }\n }\n }\n }\n }\n}`;\n\n const materialisedTeams = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const memberNames: GithubUser[] = [];\n\n if (!item.members.pageInfo.hasNextPage) {\n // We got all the members in one go, run the fast path\n for (const user of item.members.nodes) {\n memberNames.push(user);\n }\n } else {\n // There were more immediate members than page size - run the slow\n // path of fetching them explicitly\n const { members } = await getTeamMembers(\n ctx.client,\n ctx.org,\n item.slug,\n pageSizes,\n );\n for (const userLogin of members) {\n memberNames.push(userLogin);\n }\n }\n\n const team: GithubTeam = {\n ...item,\n members: memberNames,\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const teams = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.teams,\n transformer: materialisedTeams,\n variables: {\n org,\n userLogins,\n teamsPageSize: pageSizes.teams,\n membersPageSize: pageSizes.teamMembers,\n },\n });\n\n return { teams };\n}\n\nexport async function getOrganizationTeamsForUser(\n client: typeof graphql,\n org: string,\n userLogin: string,\n teamTransformer: TeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{ teams: Entity[] }> {\n const query = `\n query teams($org: String!, $cursor: String, $userLogins: [String!] = \"\", $teamsPageSize: Int!) {\n organization(login: $org) {\n teams(first: $teamsPageSize, after: $cursor, userLogins: $userLogins) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam {\n slug\n }\n }\n }\n }\n}`;\n\n const materialisedTeams = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const team: GithubTeam = {\n ...item,\n members: [{ login: userLogin }],\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const teams = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.teams,\n transformer: materialisedTeams,\n variables: { org, userLogins: [userLogin], teamsPageSize: pageSizes.teams },\n });\n\n return { teams };\n}\n\nexport async function getOrganizationsFromUser(\n client: typeof graphql,\n user: string,\n): Promise<{\n orgs: string[];\n}> {\n const query = `\n query orgs($user: String!) {\n user(login: $user) {\n organizations(first: 100) {\n nodes { login }\n pageInfo { hasNextPage, endCursor }\n }\n }\n }`;\n\n const orgs = await queryWithPaging({\n client,\n query,\n org: '',\n connection: r => r.user?.organizations,\n transformer: async o => o.login,\n variables: { user },\n });\n\n return { orgs };\n}\n\nexport async function getOrganizationTeam(\n client: typeof graphql,\n org: string,\n teamSlug: string,\n teamTransformer: TeamTransformer = defaultOrganizationTeamTransformer,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{\n team: Entity;\n}> {\n const query = `\n query teams($org: String!, $teamSlug: String!, $membersPageSize: Int!) {\n organization(login: $org) {\n team(slug:$teamSlug) {\n slug\n combinedSlug\n name\n description\n avatarUrl\n editTeamUrl\n parentTeam { slug }\n members(first: $membersPageSize, membership: IMMEDIATE) {\n pageInfo { hasNextPage }\n nodes { login }\n }\n }\n }\n }`;\n\n const materialisedTeam = async (\n item: GithubTeamResponse,\n ctx: TransformerContext,\n ): Promise<Entity | undefined> => {\n const memberNames: GithubUser[] = [];\n\n if (!item.members.pageInfo.hasNextPage) {\n // We got all the members in one go, run the fast path\n for (const user of item.members.nodes) {\n memberNames.push(user);\n }\n } else {\n // There were more immediate members than page size - run the slow\n // path of fetching them explicitly\n const { members } = await getTeamMembers(\n ctx.client,\n ctx.org,\n item.slug,\n pageSizes,\n );\n for (const userLogin of members) {\n memberNames.push(userLogin);\n }\n }\n\n const team: GithubTeam = {\n ...item,\n members: memberNames,\n };\n\n return await teamTransformer(team, ctx);\n };\n\n const response: QueryResponse = await client(query, {\n org,\n teamSlug,\n membersPageSize: pageSizes.teamMembers,\n });\n\n if (!response.organization?.team)\n throw new Error(`Found no match for team ${teamSlug}`);\n\n const team = await materialisedTeam(response.organization?.team, {\n query,\n client,\n org,\n });\n\n if (!team) throw new Error(`Can't transform for team ${teamSlug}`);\n\n return { team };\n}\n\nexport async function getOrganizationRepositories(\n client: typeof graphql,\n org: string,\n catalogPath: string,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n branch?: string,\n): Promise<{ repositories: RepositoryResponse[] }> {\n let relativeCatalogPathRef: string;\n // We must strip the leading slash or the query for objects does not work\n if (catalogPath.startsWith('/')) {\n relativeCatalogPathRef = catalogPath.substring(1);\n } else {\n relativeCatalogPathRef = catalogPath;\n }\n const branchRef = branch ?? 'HEAD';\n const catalogPathRef = `${branchRef}:${relativeCatalogPathRef}`;\n const query = `\n query repositories($org: String!, $catalogPathRef: String!, $cursor: String, $repositoriesPageSize: Int!) {\n repositoryOwner(login: $org) {\n login\n repositories(first: $repositoriesPageSize, after: $cursor) {\n nodes {\n name\n catalogInfoFile: object(expression: $catalogPathRef) {\n __typename\n ... on Blob {\n id\n text\n }\n }\n url\n isArchived\n isFork\n visibility\n repositoryTopics(first: 100) {\n nodes {\n ... on RepositoryTopic {\n topic {\n name\n }\n }\n }\n }\n defaultBranchRef {\n name\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }`;\n\n const repositories = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.repositoryOwner?.repositories,\n transformer: async x => x,\n variables: {\n org,\n catalogPathRef,\n repositoriesPageSize: pageSizes.repositories,\n },\n });\n\n return { repositories };\n}\n\nexport async function getOrganizationRepository(\n client: typeof graphql,\n org: string,\n repoName: string,\n catalogPath: string,\n branch?: string,\n): Promise<RepositoryResponse | null> {\n let relativeCatalogPathRef: string;\n // We must strip the leading slash or the query for objects does not work\n if (catalogPath.startsWith('/')) {\n relativeCatalogPathRef = catalogPath.substring(1);\n } else {\n relativeCatalogPathRef = catalogPath;\n }\n const branchRef = branch ?? 'HEAD';\n const catalogPathRef = `${branchRef}:${relativeCatalogPathRef}`;\n const query = `\n query repository($org: String!, $repoName: String!, $catalogPathRef: String!) {\n repositoryOwner(login: $org) {\n repository(name: $repoName) {\n name\n catalogInfoFile: object(expression: $catalogPathRef) {\n __typename\n ... on Blob {\n id\n text\n }\n }\n url\n isArchived\n isFork\n visibility\n repositoryTopics(first: 100) {\n nodes {\n ... on RepositoryTopic {\n topic {\n name\n }\n }\n }\n }\n defaultBranchRef {\n name\n }\n }\n }\n }`;\n\n const response: QueryResponse = await client(query, {\n org,\n repoName,\n catalogPathRef,\n });\n\n return response.repositoryOwner?.repository || null;\n}\n\n/**\n * Gets all the users out of a Github organization team.\n *\n * Note that the users will not have their memberships filled in.\n *\n * @param client - An octokit graphql client\n * @param org - The slug of the org to read\n * @param teamSlug - The slug of the team to read\n * @param pageSizes - Optional page sizes configuration\n */\nexport async function getTeamMembers(\n client: typeof graphql,\n org: string,\n teamSlug: string,\n pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,\n): Promise<{ members: GithubUser[] }> {\n const query = `\n query members($org: String!, $teamSlug: String!, $cursor: String, $membersPageSize: Int!) {\n organization(login: $org) {\n team(slug: $teamSlug) {\n members(first: $membersPageSize, after: $cursor, membership: IMMEDIATE) {\n pageInfo { hasNextPage, endCursor }\n nodes { login }\n }\n }\n }\n }`;\n\n const members = await queryWithPaging({\n client,\n query,\n org,\n connection: r => r.organization?.team?.members,\n transformer: async user => user,\n variables: { org, teamSlug, membersPageSize: pageSizes.teamMembers },\n });\n\n return { members };\n}\n\n//\n// Helpers\n//\n\n/**\n * Assists in repeatedly executing a query with a paged response.\n *\n * Requires that the query accepts a $cursor variable.\n *\n * @param params - Object containing all parameters\n * @param params.client - The octokit client\n * @param params.query - The query to execute\n * @param params.org - The slug of the org to read\n * @param params.connection - A function that, given the response, picks out the actual\n * Connection object that's being iterated\n * @param params.transformer - A function that, given one of the nodes in the Connection,\n * returns the model mapped form of it\n * @param params.variables - The variable values that the query needs, minus the cursor\n * @param params.filter - An optional filter function to filter the nodes before transforming them\n */\nexport async function queryWithPaging<\n GraphqlType,\n OutputType,\n Variables extends {},\n Response = QueryResponse,\n>(params: {\n client: typeof graphql;\n query: string;\n org: string;\n connection: (response: Response) => Connection<GraphqlType> | undefined;\n transformer: (\n item: GraphqlType,\n ctx: TransformerContext,\n ) => Promise<OutputType | undefined>;\n variables: Variables;\n filter?: (item: GraphqlType) => Promise<boolean> | boolean;\n}): Promise<OutputType[]> {\n const { client, query, org, connection, transformer, variables, filter } =\n params;\n const result: OutputType[] = [];\n const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));\n\n let cursor: string | undefined = undefined;\n for (let j = 0; j < 1000 /* just for sanity */; ++j) {\n const response: Response = await client(query, {\n ...variables,\n cursor,\n });\n\n const conn = connection(response);\n if (!conn) {\n throw new Error(`Found no match for ${JSON.stringify(variables)}`);\n }\n\n for (const node of conn.nodes) {\n if (filter && !(await filter(node))) {\n continue;\n }\n const transformedNode = await transformer(node, {\n client,\n query,\n org,\n });\n if (transformedNode) {\n result.push(transformedNode);\n }\n }\n\n if (!conn.pageInfo.hasNextPage) {\n break;\n } else {\n await sleep(1000);\n cursor = conn.pageInfo.endCursor;\n }\n }\n\n return result;\n}\n\nexport type DeferredEntitiesBuilder = (\n org: string,\n entities: Entity[],\n) => { added: DeferredEntity[]; removed: DeferredEntity[] };\n\nexport const createAddEntitiesOperation =\n (id: string, host: string) => (org: string, entities: Entity[]) => ({\n removed: [],\n added: entities.map(entity => ({\n locationKey: `github-org-provider:${id}`,\n entity: withLocations(`https://${host}`, org, entity),\n })),\n });\n\nexport const createRemoveEntitiesOperation =\n (id: string, host: string) => (org: string, entities: Entity[]) => ({\n added: [],\n removed: entities.map(entity => ({\n locationKey: `github-org-provider:${id}`,\n entity: withLocations(`https://${host}`, org, entity),\n })),\n });\n\nexport const createReplaceEntitiesOperation =\n (id: string, host: string) => (org: string, entities: Entity[]) => {\n const entitiesToReplace = entities.map(entity => ({\n locationKey: `github-org-provider:${id}`,\n entity: withLocations(`https://${host}`, org, entity),\n }));\n\n return {\n removed: entitiesToReplace,\n added: entitiesToReplace,\n };\n };\n\n/**\n * Creates a GraphQL Client with Throttling and Retries\n */\nexport const createGraphqlClient = (args: {\n headers:\n | {\n [name: string]: string;\n }\n | undefined;\n baseUrl: string;\n logger: LoggerService;\n}): typeof graphql => {\n const { headers, baseUrl, logger } = args;\n const ThrottledOctokit = Octokit.plugin(throttling, retry);\n const octokit = new ThrottledOctokit({\n throttle: {\n onRateLimit: (retryAfter, rateLimitData, _, retryCount) => {\n logger.warn(\n `Request quota exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`,\n );\n\n if (retryCount < 2) {\n logger.warn(\n `Retrying after ${retryAfter} seconds for the ${retryCount} time due to Rate Limit!`,\n );\n return true;\n }\n\n return false;\n },\n onSecondaryRateLimit: (retryAfter, rateLimitData, _, retryCount) => {\n logger.warn(\n `Secondary Rate Limit Exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`,\n );\n\n if (retryCount < 2) {\n logger.warn(\n `Retrying after ${retryAfter} seconds for the ${retryCount} time due to Secondary Rate Limit!`,\n );\n return true;\n }\n\n return false;\n },\n },\n });\n\n const client = octokit.graphql.defaults({\n headers,\n baseUrl,\n });\n\n return client;\n};\n\nfunction octokitThrottlingOptions(logger: LoggerService): ThrottlingOptions {\n return {\n onRateLimit: (retryAfter, rateLimitData, _, retryCount) => {\n logger.warn(\n `Request quota exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`,\n );\n\n if (retryCount < 2) {\n logger.warn(\n `Retrying after ${retryAfter} seconds for the ${retryCount} time due to Rate Limit!`,\n );\n return true;\n }\n\n return false;\n },\n onSecondaryRateLimit: (retryAfter, rateLimitData, _, retryCount) => {\n logger.warn(\n `Secondary Rate Limit Exhausted for request ${rateLimitData?.method} ${rateLimitData?.url}`,\n );\n\n if (retryCount < 2) {\n logger.warn(\n `Retrying after ${retryAfter} seconds for the ${retryCount} time due to Secondary Rate Limit!`,\n );\n return true;\n }\n\n return false;\n },\n };\n}\n\n/**\n * Creates an Octokit REST client with throttling, retry, and optional\n * conditional request caching.\n *\n * @public\n */\nexport function createRestClient(options: {\n baseUrl: string | undefined;\n orgUrl: string;\n credentialsProvider: GithubCredentialsProvider;\n logger: LoggerService;\n cache?: CacheService;\n}): Octokit & { auth: () => Promise<GithubCredentials> } {\n const getCredentials = () =>\n options.credentialsProvider.getCredentials({\n url: options.orgUrl,\n });\n\n const authStrategy = () => {\n const auth = () => getCredentials();\n auth.hook = async (\n request: (\n requestOptions: RequestParameters,\n ) => Promise<OctokitResponse<unknown>>,\n hookOptions: RequestParameters,\n ) => {\n const { headers } = await getCredentials();\n return request({\n ...hookOptions,\n headers: { ...hookOptions.headers, ...headers },\n });\n };\n return auth;\n };\n\n const ThrottledOctokit = Octokit.plugin(throttling, retry);\n\n const octokit = new ThrottledOctokit({\n baseUrl: options.baseUrl,\n authStrategy,\n throttle: octokitThrottlingOptions(options.logger),\n });\n\n if (options.cache) {\n installConditionalRequestCache(octokit, options.cache);\n }\n\n return octokit as Octokit & { auth: () => Promise<GithubCredentials> };\n}\n\ntype CachedGitHubResponse = {\n lastModified?: string;\n etag?: string;\n headers: JsonValue;\n data: JsonValue;\n};\n\nfunction installConditionalRequestCache(\n octokit: Octokit,\n cache: CacheService,\n): void {\n octokit.hook.wrap('request', async (request, wrappedOptions) => {\n const resolvedUrl = (wrappedOptions.url || '').replace(\n /\\{([^}]+)\\}/g,\n (_, key) => encodeURIComponent((wrappedOptions as any)[key]),\n );\n const cacheKey = `catalog-backend-module-github:${wrappedOptions.method}:${wrappedOptions.baseUrl}${resolvedUrl}`;\n const cached = await cache\n .get<CachedGitHubResponse>(cacheKey)\n .catch(() => undefined);\n\n if (cached?.lastModified) {\n wrappedOptions.headers['if-modified-since'] = cached.lastModified;\n } else if (cached?.etag) {\n wrappedOptions.headers['if-none-match'] = cached.etag;\n }\n\n try {\n const response = await request(wrappedOptions);\n\n const lastModified = response.headers['last-modified'];\n const etag = response.headers.etag;\n\n if (lastModified || etag) {\n cache\n .set(\n cacheKey,\n {\n lastModified,\n etag,\n headers: JSON.parse(JSON.stringify(response.headers)),\n data: JSON.parse(JSON.stringify(response.data)),\n },\n // The TTL can be long here, since we only use the cache for\n // conditional GitHub requests - it's never returned unless we get a\n // 304 back from GitHub indicating that it's still correct.\n { ttl: { years: 1 } },\n )\n .catch(() => {});\n }\n return response;\n } catch (error: any) {\n if (error?.status === 304 && cached) {\n return {\n ...error.response,\n headers: cached.headers,\n data: cached.data,\n };\n }\n throw error;\n }\n });\n}\n\n/**\n * Checks whether a user is suspended via the REST API.\n */\nexport async function isSuspended(\n username: string,\n octokit: Octokit,\n options: { org: string },\n): Promise<boolean> {\n const [userResponse, membershipResponse] = await Promise.all([\n octokit.request('GET /users/{username}', { username }),\n octokit.request('GET /orgs/{org}/memberships/{username}', {\n org: options.org,\n username,\n }),\n ]);\n\n // Octokit types are based on the public GitHub API, and since public GitHub\n // doesn't include the ability to suspend users, there's no \"suspended_at\"\n // field on the type, nor a \"suspended\" role on org memberships. However these\n // fields are present for GitHub Enterprise, so we augment the types to\n // include them.\n const userSuspendedAt = (\n userResponse.data as typeof userResponse.data & { suspended_at?: string }\n ).suspended_at;\n const membershipRole = (\n membershipResponse.data as\n | typeof membershipResponse.data\n | {\n role?: 'suspended';\n }\n ).role;\n\n const userSuspended = !!userSuspendedAt;\n const orgMembershipSuspended = membershipRole === 'suspended';\n\n return userSuspended || orgMembershipSuspended;\n}\n\n/**\n * Checks whether the GitHub instance is a GitHub Enterprise server.\n */\nexport async function isGitHubEnterprise(octokit: Octokit): Promise<boolean> {\n try {\n const response = await octokit.request('GET /versions');\n return !!response.headers['x-github-enterprise-version'];\n } catch {\n return false;\n }\n}\n"],"names":["defaultUserTransformer","defaultOrganizationTeamTransformer","team","withLocations","Octokit","throttling","retry"],"mappings":"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"}
|