Refactor GitHubAppManager, add deps & config
This commit is contained in:
Vendored
+10
@@ -35,6 +35,16 @@ export interface Config {
|
||||
apiBaseUrl?: string;
|
||||
/** @visibility frontend */
|
||||
rawBaseUrl?: string;
|
||||
apps?: Array<{
|
||||
appId: number;
|
||||
/** @visiblity secret */
|
||||
privateKey: string;
|
||||
/** @visiblity secret */
|
||||
webhookSecret: string;
|
||||
clientId: string;
|
||||
/** @visiblity secret */
|
||||
clientSecret: string;
|
||||
}>;
|
||||
}>;
|
||||
|
||||
gitlab?: Array<{
|
||||
|
||||
@@ -31,7 +31,10 @@
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.2",
|
||||
"cross-fetch": "^3.0.6",
|
||||
"git-url-parse": "^11.4.3"
|
||||
"git-url-parse": "^11.4.3",
|
||||
"@octokit/rest": "^18.0.12",
|
||||
"@octokit/auth-app": "^2.10.5",
|
||||
"moment": "^2.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.5",
|
||||
|
||||
@@ -58,8 +58,22 @@ export type GitHubIntegrationConfig = {
|
||||
* If no token is specified, anonymous access is used.
|
||||
*/
|
||||
token?: string;
|
||||
|
||||
/**
|
||||
* The GitHub Apps configuration to use for requests to this provider.
|
||||
*
|
||||
* If no apps is specified, token or anonymous is used.
|
||||
*/
|
||||
apps?: GithubAppConfig[];
|
||||
};
|
||||
|
||||
export type GithubAppConfig = {
|
||||
appId: number;
|
||||
privateKey: string;
|
||||
webhookSecret: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
};
|
||||
/**
|
||||
* Reads a single GitHub integration config.
|
||||
*
|
||||
|
||||
@@ -19,6 +19,7 @@ import { createAppAuth } from '@octokit/auth-app';
|
||||
import { Octokit, RestEndpointMethodTypes } from '@octokit/rest';
|
||||
import gitUrlParse from 'git-url-parse';
|
||||
import moment from 'moment';
|
||||
import { InstallationAccessTokenAuthentication } from '@octokit/auth-app/dist-types/types';
|
||||
|
||||
type InstallationData = {
|
||||
installationId: number;
|
||||
@@ -26,15 +27,37 @@ type InstallationData = {
|
||||
repositorySelection: 'selected' | 'all';
|
||||
};
|
||||
|
||||
class Cache {
|
||||
private readonly tokenCache = new Map<
|
||||
string,
|
||||
{ token: string; expiresAt: Date }
|
||||
>();
|
||||
|
||||
async getToken(
|
||||
key: string,
|
||||
fn: () => Promise<{ token: string; expiresAt: Date }>,
|
||||
): Promise<{ accessToken: string }> {
|
||||
const item = this.tokenCache.get(key);
|
||||
if (item && this.isNotExpired(item.expiresAt)) {
|
||||
return { accessToken: item.token };
|
||||
}
|
||||
|
||||
const result = await fn();
|
||||
this.tokenCache.set(key, result);
|
||||
return { accessToken: result.token };
|
||||
}
|
||||
|
||||
// consider timestamps older than 50 minutes to be expired.
|
||||
private isNotExpired = (date: Date) =>
|
||||
moment(date).isAfter(moment().subtract(50, 'minutes'));
|
||||
}
|
||||
|
||||
// GithubAppManager issues tokens for a speicifc GitHub App
|
||||
class GithubAppManager {
|
||||
private readonly appClient: Octokit;
|
||||
private readonly baseAuthConfig: { appId: number; privateKey: string };
|
||||
private installations?: RestEndpointMethodTypes['apps']['listInstallations']['response'];
|
||||
private readonly tokenCache = new Map<
|
||||
string,
|
||||
{ accessToken: string; exp: Date }
|
||||
>();
|
||||
private readonly cache = new Cache();
|
||||
|
||||
constructor(config: GithubAppConfig) {
|
||||
this.baseAuthConfig = {
|
||||
@@ -47,9 +70,6 @@ class GithubAppManager {
|
||||
});
|
||||
}
|
||||
|
||||
private lessThanOneHourAgo = (date: Date) =>
|
||||
moment(date).isAfter(moment().subtract(1, 'hours'));
|
||||
|
||||
async getInstallationCredentials(
|
||||
owner: string,
|
||||
repo: string,
|
||||
@@ -65,34 +85,31 @@ class GithubAppManager {
|
||||
|
||||
// App is installed in the entire org
|
||||
if (repositorySelection === 'all') {
|
||||
const auth = createAppAuth({
|
||||
...this.baseAuthConfig,
|
||||
installationId,
|
||||
return this.cache.getToken(owner, async () => {
|
||||
const auth = createAppAuth({
|
||||
...this.baseAuthConfig,
|
||||
installationId,
|
||||
});
|
||||
const result = await auth({ type: 'installation' });
|
||||
const {
|
||||
token,
|
||||
expiresAt,
|
||||
} = result as InstallationAccessTokenAuthentication;
|
||||
return { token, expiresAt: new Date(expiresAt) };
|
||||
});
|
||||
const { token } = await auth({ type: 'installation' });
|
||||
return { accessToken: token };
|
||||
}
|
||||
|
||||
// App is not installed org wide which requires a specific app token.
|
||||
const cacheKey = `${owner}/${repo}`;
|
||||
if (this.tokenCache.has(cacheKey)) {
|
||||
const item = this.tokenCache.get(cacheKey);
|
||||
if (this.lessThanOneHourAgo(item?.exp!)) {
|
||||
return {
|
||||
accessToken: item?.accessToken!,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const res = await this.appClient.apps.createInstallationAccessToken({
|
||||
installation_id: installationId,
|
||||
repositories: [repo],
|
||||
return this.cache.getToken(`${owner}/${repo}`, async () => {
|
||||
const result = await this.appClient.apps.createInstallationAccessToken({
|
||||
installation_id: installationId,
|
||||
repositories: [repo],
|
||||
});
|
||||
return {
|
||||
token: result.data.token,
|
||||
expiresAt: new Date(result.data.expires_at),
|
||||
};
|
||||
});
|
||||
this.tokenCache.set(cacheKey, {
|
||||
accessToken: res.data.token,
|
||||
exp: new Date(res.data.expires_at),
|
||||
});
|
||||
return { accessToken: res.data.token };
|
||||
}
|
||||
|
||||
private async getInstallationData(owner: string): Promise<InstallationData> {
|
||||
|
||||
@@ -4661,32 +4661,27 @@
|
||||
dependencies:
|
||||
mkdirp "^1.0.4"
|
||||
|
||||
"@octokit/auth-token@^2.4.0":
|
||||
version "2.4.0"
|
||||
resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz#b64178975218b99e4dfe948253f0673cbbb59d9f"
|
||||
integrity sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==
|
||||
"@octokit/auth-app@^2.10.5":
|
||||
version "2.10.5"
|
||||
resolved "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-2.10.5.tgz#85d69cb96818f5da34bf0b81bb637d3675ad4e9a"
|
||||
integrity sha512-6yXyjtcBWpuPYSdZN8z8IIjGSqkPmiJzdmCdod8at41ANB1FtaKbUIDL5+IkG+svv68NIYs+XORbhBRFXYB3bw==
|
||||
dependencies:
|
||||
"@octokit/types" "^2.0.0"
|
||||
"@octokit/request" "^5.4.11"
|
||||
"@octokit/request-error" "^2.0.0"
|
||||
"@octokit/types" "^6.0.3"
|
||||
"@types/lru-cache" "^5.1.0"
|
||||
deprecation "^2.3.1"
|
||||
lru-cache "^6.0.0"
|
||||
universal-github-app-jwt "^1.0.1"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/auth-token@^2.4.4":
|
||||
"@octokit/auth-token@^2.4.0", "@octokit/auth-token@^2.4.4":
|
||||
version "2.4.4"
|
||||
resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56"
|
||||
integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.0"
|
||||
|
||||
"@octokit/core@^3.0.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/@octokit/core/-/core-3.1.0.tgz#9c3c9b23f7504668cfa057f143ccbf0c645a0ac9"
|
||||
integrity sha512-yPyQSmxIXLieEIRikk2w8AEtWkFdfG/LXcw1KvEtK3iP0ENZLW/WYQmdzOKqfSaLhooz4CJ9D+WY79C8ZliACw==
|
||||
dependencies:
|
||||
"@octokit/auth-token" "^2.4.0"
|
||||
"@octokit/graphql" "^4.3.1"
|
||||
"@octokit/request" "^5.4.0"
|
||||
"@octokit/types" "^5.0.0"
|
||||
before-after-hook "^2.1.0"
|
||||
universal-user-agent "^5.0.0"
|
||||
|
||||
"@octokit/core@^3.2.3":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.npmjs.org/@octokit/core/-/core-3.2.4.tgz#5791256057a962eca972e31818f02454897fd106"
|
||||
@@ -4708,15 +4703,6 @@
|
||||
is-plain-object "^3.0.0"
|
||||
universal-user-agent "^5.0.0"
|
||||
|
||||
"@octokit/graphql@^4.3.1":
|
||||
version "4.5.1"
|
||||
resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.1.tgz#162aed1490320b88ce34775b3f6b8de945529fa9"
|
||||
integrity sha512-qgMsROG9K2KxDs12CO3bySJaYoUu2aic90qpFrv7A8sEBzZ7UFGvdgPKiLw5gOPYEYbS0Xf8Tvf84tJutHPulQ==
|
||||
dependencies:
|
||||
"@octokit/request" "^5.3.0"
|
||||
"@octokit/types" "^5.0.0"
|
||||
universal-user-agent "^5.0.0"
|
||||
|
||||
"@octokit/graphql@^4.5.8":
|
||||
version "4.5.8"
|
||||
resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b"
|
||||
@@ -4743,13 +4729,6 @@
|
||||
dependencies:
|
||||
"@octokit/types" "^2.0.1"
|
||||
|
||||
"@octokit/plugin-paginate-rest@^2.2.0":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.3.tgz#a6ad4377e7e7832fb4bdd9d421e600cb7640ac27"
|
||||
integrity sha512-eKTs91wXnJH8Yicwa30jz6DF50kAh7vkcqCQ9D7/tvBAP5KKkg6I2nNof8Mp/65G0Arjsb4QcOJcIEQY+rK1Rg==
|
||||
dependencies:
|
||||
"@octokit/types" "^5.0.0"
|
||||
|
||||
"@octokit/plugin-paginate-rest@^2.6.2":
|
||||
version "2.7.0"
|
||||
resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.7.0.tgz#6bb7b043c246e0654119a6ec4e72a172c9e2c7f3"
|
||||
@@ -4757,12 +4736,7 @@
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.1"
|
||||
|
||||
"@octokit/plugin-request-log@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e"
|
||||
integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==
|
||||
|
||||
"@octokit/plugin-request-log@^1.0.2":
|
||||
"@octokit/plugin-request-log@^1.0.0", "@octokit/plugin-request-log@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44"
|
||||
integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg==
|
||||
@@ -4775,14 +4749,6 @@
|
||||
"@octokit/types" "^2.0.1"
|
||||
deprecation "^2.3.1"
|
||||
|
||||
"@octokit/plugin-rest-endpoint-methods@4.1.4":
|
||||
version "4.1.4"
|
||||
resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.1.4.tgz#ca60736d761b304fec02a2608caaec2d822e9835"
|
||||
integrity sha512-Y2tVpSa7HjV3DGIQrQOJcReJ2JtcN9FaGr9jDa332Flro923/h3/Iu9e7Y4GilnzfLclHEh5iCQoCkHm7tWOcg==
|
||||
dependencies:
|
||||
"@octokit/types" "^5.4.1"
|
||||
deprecation "^2.3.1"
|
||||
|
||||
"@octokit/plugin-rest-endpoint-methods@4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz#105cf93255432155de078c9efc33bd4e14d1cd63"
|
||||
@@ -4809,21 +4775,7 @@
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^5.2.0", "@octokit/request@^5.3.0", "@octokit/request@^5.4.0":
|
||||
version "5.4.5"
|
||||
resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.5.tgz#8df65bd812047521f7e9db6ff118c06ba84ac10b"
|
||||
integrity sha512-atAs5GAGbZedvJXXdjtKljin+e2SltEs48B3naJjqWupYl2IUBbB/CJisyjbNHcKpHzb3E+OYEZ46G8eakXgQg==
|
||||
dependencies:
|
||||
"@octokit/endpoint" "^6.0.1"
|
||||
"@octokit/request-error" "^2.0.0"
|
||||
"@octokit/types" "^5.0.0"
|
||||
deprecation "^2.0.0"
|
||||
is-plain-object "^3.0.0"
|
||||
node-fetch "^2.3.0"
|
||||
once "^1.4.0"
|
||||
universal-user-agent "^5.0.0"
|
||||
|
||||
"@octokit/request@^5.4.12":
|
||||
"@octokit/request@^5.2.0", "@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.12":
|
||||
version "5.4.12"
|
||||
resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc"
|
||||
integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg==
|
||||
@@ -4859,17 +4811,7 @@
|
||||
once "^1.4.0"
|
||||
universal-user-agent "^4.0.0"
|
||||
|
||||
"@octokit/rest@^18.0.0":
|
||||
version "18.0.5"
|
||||
resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.5.tgz#1f1498dcdc2d85d0f86b8168e4ff5779842b2742"
|
||||
integrity sha512-SPKI24tQXrr1XsnaIjv2x0rl4M5eF1+hj8+vMe3d/exZ7NnL5sTe1BuFyCyJyrc+j1HkXankvgGN9zT0rwBwtg==
|
||||
dependencies:
|
||||
"@octokit/core" "^3.0.0"
|
||||
"@octokit/plugin-paginate-rest" "^2.2.0"
|
||||
"@octokit/plugin-request-log" "^1.0.0"
|
||||
"@octokit/plugin-rest-endpoint-methods" "4.1.4"
|
||||
|
||||
"@octokit/rest@^18.0.12":
|
||||
"@octokit/rest@^18.0.0", "@octokit/rest@^18.0.12":
|
||||
version "18.0.12"
|
||||
resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.12.tgz#278bd41358c56d87c201e787e8adc0cac132503a"
|
||||
integrity sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw==
|
||||
@@ -4887,16 +4829,9 @@
|
||||
"@types/node" ">= 8"
|
||||
|
||||
"@octokit/types@^5.0.0", "@octokit/types@^5.0.1":
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmjs.org/@octokit/types/-/types-5.0.1.tgz#5459e9a5e9df8565dcc62c17a34491904d71971e"
|
||||
integrity sha512-GorvORVwp244fGKEt3cgt/P+M0MGy4xEDbckw+K5ojEezxyMDgCaYPKVct+/eWQfZXOT7uq0xRpmrl/+hliabA==
|
||||
dependencies:
|
||||
"@types/node" ">= 8"
|
||||
|
||||
"@octokit/types@^5.4.1":
|
||||
version "5.4.1"
|
||||
resolved "https://registry.npmjs.org/@octokit/types/-/types-5.4.1.tgz#d5d5f2b70ffc0e3f89467c3db749fa87fc3b7031"
|
||||
integrity sha512-OlMlSySBJoJ6uozkr/i03nO5dlYQyE05vmQNZhAh9MyO4DPBP88QlwsDVLmVjIMFssvIZB6WO0ctIGMRG+xsJQ==
|
||||
version "5.5.0"
|
||||
resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b"
|
||||
integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==
|
||||
dependencies:
|
||||
"@types/node" ">= 8"
|
||||
|
||||
@@ -6631,7 +6566,7 @@
|
||||
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||
|
||||
"@types/jsonwebtoken@^8.5.0":
|
||||
"@types/jsonwebtoken@^8.3.3", "@types/jsonwebtoken@^8.5.0":
|
||||
version "8.5.0"
|
||||
resolved "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5"
|
||||
integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==
|
||||
@@ -6694,6 +6629,11 @@
|
||||
resolved "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
|
||||
|
||||
"@types/lru-cache@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03"
|
||||
integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w==
|
||||
|
||||
"@types/markdown-to-jsx@^6.11.0":
|
||||
version "6.11.2"
|
||||
resolved "https://registry.npmjs.org/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.2.tgz#05d1aaffbf15be7be12c70535fa4fed65cc7c64f"
|
||||
@@ -18484,7 +18424,7 @@ modify-values@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
||||
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
|
||||
|
||||
moment@^2.25.3, moment@^2.26.0, moment@^2.27.0:
|
||||
moment@^2.25.3, moment@^2.26.0, moment@^2.27.0, moment@^2.29.1:
|
||||
version "2.29.1"
|
||||
resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||
@@ -25155,6 +25095,14 @@ unist-util-visit@^2.0.0:
|
||||
unist-util-is "^4.0.0"
|
||||
unist-util-visit-parents "^3.0.0"
|
||||
|
||||
universal-github-app-jwt@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.0.tgz#0abaa876101cdf1d3e4c546be2768841c0c1b514"
|
||||
integrity sha512-3b+ocAjjz4JTyqaOT+NNBd5BtTuvJTxWElIoeHSVelUV9J3Jp7avmQTdLKCaoqi/5Ox2o/q+VK19TJ233rVXVQ==
|
||||
dependencies:
|
||||
"@types/jsonwebtoken" "^8.3.3"
|
||||
jsonwebtoken "^8.5.1"
|
||||
|
||||
universal-user-agent@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557"
|
||||
|
||||
Reference in New Issue
Block a user