@backstage/integration 2.0.3 → 2.1.0-next.0

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.
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var SingleInstanceGithubCredentialsProvider = require('./SingleInstanceGithubCredentialsProvider.cjs.js');
4
+ var errors = require('@backstage/errors');
4
5
 
5
6
  class DefaultGithubCredentialsProvider {
6
- constructor(providers) {
7
- this.providers = providers;
8
- }
9
7
  static fromIntegrations(integrations) {
10
8
  const credentialsProviders = /* @__PURE__ */ new Map();
11
9
  integrations.github.list().forEach((integration) => {
@@ -14,6 +12,24 @@ class DefaultGithubCredentialsProvider {
14
12
  });
15
13
  return new DefaultGithubCredentialsProvider(credentialsProviders);
16
14
  }
15
+ /**
16
+ * Creates a credentials provider backed by the connections service.
17
+ *
18
+ * @param connections - The connections service used to resolve GitHub credentials.
19
+ * @internal
20
+ */
21
+ static experimentalFromConnections(connections) {
22
+ return new DefaultGithubCredentialsProvider(
23
+ /* @__PURE__ */ new Map(),
24
+ connections
25
+ );
26
+ }
27
+ providers;
28
+ #connections;
29
+ constructor(providers, connections) {
30
+ this.providers = providers;
31
+ this.#connections = connections;
32
+ }
17
33
  /**
18
34
  * Returns {@link GithubCredentials} for a given URL.
19
35
  *
@@ -39,6 +55,62 @@ class DefaultGithubCredentialsProvider {
39
55
  * @returns A promise of {@link GithubCredentials}.
40
56
  */
41
57
  async getCredentials(opts) {
58
+ if (this.#connections) {
59
+ const connection = await this.#connections.find({
60
+ type: "github",
61
+ url: opts.url,
62
+ authMethods: ["app", "token", "none"]
63
+ }).catch((error) => {
64
+ throw new errors.ForwardedError(
65
+ "Failed getting credentials from connection",
66
+ error
67
+ );
68
+ });
69
+ const { auth } = connection;
70
+ const config = {
71
+ host: connection.host,
72
+ apiBaseUrl: connection.apiBaseUrl,
73
+ rawBaseUrl: connection.rawBaseUrl
74
+ };
75
+ let providerKey;
76
+ if (auth.method === "app") {
77
+ const appId = Number(auth.appId);
78
+ if (!Number.isSafeInteger(appId) || appId <= 0) {
79
+ throw new errors.InputError(
80
+ `Invalid GitHub App ID "${auth.appId}", expected a positive safe integer`
81
+ );
82
+ }
83
+ const normalizedOrgs = auth.orgs?.length ? Array.from(
84
+ new Set(auth.orgs.map((org) => org.toLocaleLowerCase("en-US")))
85
+ ).sort() : void 0;
86
+ config.apps = [
87
+ {
88
+ appId,
89
+ privateKey: auth.privateKey,
90
+ clientId: auth.clientId,
91
+ clientSecret: auth.clientSecret,
92
+ webhookSecret: auth.webhookSecret,
93
+ publicAccess: auth.publicAccess,
94
+ allowedInstallationOwners: normalizedOrgs
95
+ }
96
+ ];
97
+ providerKey = `${connection.host}:app:${appId}:${JSON.stringify(
98
+ normalizedOrgs ?? []
99
+ )}:${String(auth.publicAccess ?? false)}`;
100
+ } else if (auth.method === "token") {
101
+ config.token = auth.token;
102
+ } else {
103
+ providerKey = `${connection.host}:none`;
104
+ }
105
+ let provider2 = providerKey ? this.providers.get(providerKey) : void 0;
106
+ if (!provider2) {
107
+ provider2 = SingleInstanceGithubCredentialsProvider.SingleInstanceGithubCredentialsProvider.create(config);
108
+ if (providerKey) {
109
+ this.providers.set(providerKey, provider2);
110
+ }
111
+ }
112
+ return provider2.getCredentials(opts);
113
+ }
42
114
  const parsed = new URL(opts.url);
43
115
  const provider = this.providers.get(parsed.host);
44
116
  if (!provider) {
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultGithubCredentialsProvider.cjs.js","sources":["../../src/github/DefaultGithubCredentialsProvider.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { GithubCredentials, GithubCredentialsProvider } from './types';\nimport { ScmIntegrationRegistry } from '../registry';\nimport { SingleInstanceGithubCredentialsProvider } from './SingleInstanceGithubCredentialsProvider';\n\n/**\n * Handles the creation and caching of credentials for GitHub integrations.\n *\n * @public\n * @remarks\n *\n * TODO: Possibly move this to a backend only package so that it's not used in the frontend by mistake\n */\nexport class DefaultGithubCredentialsProvider\n implements GithubCredentialsProvider\n{\n static fromIntegrations(integrations: ScmIntegrationRegistry) {\n const credentialsProviders: Map<string, GithubCredentialsProvider> =\n new Map<string, GithubCredentialsProvider>();\n\n integrations.github.list().forEach(integration => {\n const credentialsProvider =\n SingleInstanceGithubCredentialsProvider.create(integration.config);\n credentialsProviders.set(integration.config.host, credentialsProvider);\n });\n return new DefaultGithubCredentialsProvider(credentialsProviders);\n }\n\n private constructor(\n private readonly providers: Map<string, GithubCredentialsProvider>,\n ) {}\n\n /**\n * Returns {@link GithubCredentials} for a given URL.\n *\n * @remarks\n *\n * Consecutive calls to this method with the same URL will return cached\n * credentials.\n *\n * The shortest lifetime for a token returned is 10 minutes.\n *\n * @example\n * ```ts\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage/foobar'\n * })\n *\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage'\n * })\n * ```\n *\n * @param opts - The organization or repository URL\n * @returns A promise of {@link GithubCredentials}.\n */\n async getCredentials(opts: { url: string }): Promise<GithubCredentials> {\n const parsed = new URL(opts.url);\n const provider = this.providers.get(parsed.host);\n\n if (!provider) {\n throw new Error(\n `There is no GitHub integration that matches ${opts.url}. Please add a configuration for an integration.`,\n );\n }\n\n return provider.getCredentials(opts);\n }\n}\n"],"names":["SingleInstanceGithubCredentialsProvider"],"mappings":";;;;AA4BO,MAAM,gCAAA,CAEb;AAAA,EAaU,YACW,SAAA,EACjB;AADiB,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AAAA,EAChB;AAAA,EAdH,OAAO,iBAAiB,YAAA,EAAsC;AAC5D,IAAA,MAAM,oBAAA,uBACA,GAAA,EAAuC;AAE7C,IAAA,YAAA,CAAa,MAAA,CAAO,IAAA,EAAK,CAAE,OAAA,CAAQ,CAAA,WAAA,KAAe;AAChD,MAAA,MAAM,mBAAA,GACJA,+EAAA,CAAwC,MAAA,CAAO,WAAA,CAAY,MAAM,CAAA;AACnE,MAAA,oBAAA,CAAqB,GAAA,CAAI,WAAA,CAAY,MAAA,CAAO,IAAA,EAAM,mBAAmB,CAAA;AAAA,IACvE,CAAC,CAAA;AACD,IAAA,OAAO,IAAI,iCAAiC,oBAAoB,CAAA;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,eAAe,IAAA,EAAmD;AACtE,IAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,IAAA,CAAK,GAAG,CAAA;AAC/B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,OAAO,IAAI,CAAA;AAE/C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,4CAAA,EAA+C,KAAK,GAAG,CAAA,gDAAA;AAAA,OACzD;AAAA,IACF;AAEA,IAAA,OAAO,QAAA,CAAS,eAAe,IAAI,CAAA;AAAA,EACrC;AACF;;;;"}
1
+ {"version":3,"file":"DefaultGithubCredentialsProvider.cjs.js","sources":["../../src/github/DefaultGithubCredentialsProvider.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { GithubCredentials, GithubCredentialsProvider } from './types';\nimport { ScmIntegrationRegistry } from '../registry';\nimport { SingleInstanceGithubCredentialsProvider } from './SingleInstanceGithubCredentialsProvider';\nimport type { ConnectionsService } from '@backstage/connections';\nimport { ForwardedError, InputError } from '@backstage/errors';\nimport type { GithubIntegrationConfig } from './config';\n\n/**\n * Handles the creation and caching of credentials for GitHub integrations.\n *\n * @public\n * @remarks\n *\n * TODO: Possibly move this to a backend only package so that it's not used in the frontend by mistake\n */\nexport class DefaultGithubCredentialsProvider\n implements GithubCredentialsProvider\n{\n static fromIntegrations(integrations: ScmIntegrationRegistry) {\n const credentialsProviders: Map<string, GithubCredentialsProvider> =\n new Map<string, GithubCredentialsProvider>();\n\n integrations.github.list().forEach(integration => {\n const credentialsProvider =\n SingleInstanceGithubCredentialsProvider.create(integration.config);\n credentialsProviders.set(integration.config.host, credentialsProvider);\n });\n return new DefaultGithubCredentialsProvider(credentialsProviders);\n }\n\n /**\n * Creates a credentials provider backed by the connections service.\n *\n * @param connections - The connections service used to resolve GitHub credentials.\n * @internal\n */\n static experimentalFromConnections(connections: ConnectionsService) {\n return new DefaultGithubCredentialsProvider(\n new Map<string, GithubCredentialsProvider>(),\n connections,\n );\n }\n\n private readonly providers: Map<string, GithubCredentialsProvider>;\n readonly #connections?: ConnectionsService;\n\n private constructor(\n providers: Map<string, GithubCredentialsProvider>,\n connections?: ConnectionsService,\n ) {\n this.providers = providers;\n this.#connections = connections;\n }\n\n /**\n * Returns {@link GithubCredentials} for a given URL.\n *\n * @remarks\n *\n * Consecutive calls to this method with the same URL will return cached\n * credentials.\n *\n * The shortest lifetime for a token returned is 10 minutes.\n *\n * @example\n * ```ts\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage/foobar'\n * })\n *\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage'\n * })\n * ```\n *\n * @param opts - The organization or repository URL\n * @returns A promise of {@link GithubCredentials}.\n */\n async getCredentials(opts: { url: string }): Promise<GithubCredentials> {\n if (this.#connections) {\n // Ask the connections service to select auth for this URL. A host may\n // have different GitHub Apps for different organizations, so this\n // selection cannot be done once when the provider is created.\n\n const connection = await this.#connections\n .find({\n type: 'github',\n url: opts.url,\n authMethods: ['app', 'token', 'none'],\n })\n .catch(error => {\n throw new ForwardedError(\n 'Failed getting credentials from connection',\n error,\n );\n });\n\n const { auth } = connection;\n\n // Adapt the connection schema to the existing provider configuration so\n // credential creation and token caching stay in one implementation.\n const config: GithubIntegrationConfig = {\n host: connection.host,\n apiBaseUrl: connection.apiBaseUrl,\n rawBaseUrl: connection.rawBaseUrl,\n };\n\n // Reusing an App provider preserves its installation-token cache. App\n // organizations are canonicalized because GitHub owner matching is\n // case-insensitive and does not depend on ordering. Static token\n // providers have no internal token cache, so they are recreated rather\n // than placing their secret token in a cache key.\n let providerKey: string | undefined;\n if (auth.method === 'app') {\n const appId = Number(auth.appId);\n if (!Number.isSafeInteger(appId) || appId <= 0) {\n throw new InputError(\n `Invalid GitHub App ID \"${auth.appId}\", expected a positive safe integer`,\n );\n }\n const normalizedOrgs = auth.orgs?.length\n ? Array.from(\n new Set(auth.orgs.map(org => org.toLocaleLowerCase('en-US'))),\n ).sort()\n : undefined;\n config.apps = [\n {\n appId,\n privateKey: auth.privateKey,\n clientId: auth.clientId,\n clientSecret: auth.clientSecret,\n webhookSecret: auth.webhookSecret,\n publicAccess: auth.publicAccess,\n allowedInstallationOwners: normalizedOrgs,\n },\n ];\n providerKey = `${connection.host}:app:${appId}:${JSON.stringify(\n normalizedOrgs ?? [],\n )}:${String(auth.publicAccess ?? false)}`;\n } else if (auth.method === 'token') {\n config.token = auth.token;\n } else {\n providerKey = `${connection.host}:none`;\n }\n\n let provider = providerKey ? this.providers.get(providerKey) : undefined;\n if (!provider) {\n provider = SingleInstanceGithubCredentialsProvider.create(config);\n if (providerKey) {\n this.providers.set(providerKey, provider);\n }\n }\n\n return provider.getCredentials(opts);\n }\n\n const parsed = new URL(opts.url);\n const provider = this.providers.get(parsed.host);\n\n if (!provider) {\n throw new Error(\n `There is no GitHub integration that matches ${opts.url}. Please add a configuration for an integration.`,\n );\n }\n\n return provider.getCredentials(opts);\n }\n}\n"],"names":["SingleInstanceGithubCredentialsProvider","ForwardedError","InputError","provider"],"mappings":"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"}
@@ -1,9 +1,7 @@
1
1
  import { SingleInstanceGithubCredentialsProvider } from './SingleInstanceGithubCredentialsProvider.esm.js';
2
+ import { ForwardedError, InputError } from '@backstage/errors';
2
3
 
3
4
  class DefaultGithubCredentialsProvider {
4
- constructor(providers) {
5
- this.providers = providers;
6
- }
7
5
  static fromIntegrations(integrations) {
8
6
  const credentialsProviders = /* @__PURE__ */ new Map();
9
7
  integrations.github.list().forEach((integration) => {
@@ -12,6 +10,24 @@ class DefaultGithubCredentialsProvider {
12
10
  });
13
11
  return new DefaultGithubCredentialsProvider(credentialsProviders);
14
12
  }
13
+ /**
14
+ * Creates a credentials provider backed by the connections service.
15
+ *
16
+ * @param connections - The connections service used to resolve GitHub credentials.
17
+ * @internal
18
+ */
19
+ static experimentalFromConnections(connections) {
20
+ return new DefaultGithubCredentialsProvider(
21
+ /* @__PURE__ */ new Map(),
22
+ connections
23
+ );
24
+ }
25
+ providers;
26
+ #connections;
27
+ constructor(providers, connections) {
28
+ this.providers = providers;
29
+ this.#connections = connections;
30
+ }
15
31
  /**
16
32
  * Returns {@link GithubCredentials} for a given URL.
17
33
  *
@@ -37,6 +53,62 @@ class DefaultGithubCredentialsProvider {
37
53
  * @returns A promise of {@link GithubCredentials}.
38
54
  */
39
55
  async getCredentials(opts) {
56
+ if (this.#connections) {
57
+ const connection = await this.#connections.find({
58
+ type: "github",
59
+ url: opts.url,
60
+ authMethods: ["app", "token", "none"]
61
+ }).catch((error) => {
62
+ throw new ForwardedError(
63
+ "Failed getting credentials from connection",
64
+ error
65
+ );
66
+ });
67
+ const { auth } = connection;
68
+ const config = {
69
+ host: connection.host,
70
+ apiBaseUrl: connection.apiBaseUrl,
71
+ rawBaseUrl: connection.rawBaseUrl
72
+ };
73
+ let providerKey;
74
+ if (auth.method === "app") {
75
+ const appId = Number(auth.appId);
76
+ if (!Number.isSafeInteger(appId) || appId <= 0) {
77
+ throw new InputError(
78
+ `Invalid GitHub App ID "${auth.appId}", expected a positive safe integer`
79
+ );
80
+ }
81
+ const normalizedOrgs = auth.orgs?.length ? Array.from(
82
+ new Set(auth.orgs.map((org) => org.toLocaleLowerCase("en-US")))
83
+ ).sort() : void 0;
84
+ config.apps = [
85
+ {
86
+ appId,
87
+ privateKey: auth.privateKey,
88
+ clientId: auth.clientId,
89
+ clientSecret: auth.clientSecret,
90
+ webhookSecret: auth.webhookSecret,
91
+ publicAccess: auth.publicAccess,
92
+ allowedInstallationOwners: normalizedOrgs
93
+ }
94
+ ];
95
+ providerKey = `${connection.host}:app:${appId}:${JSON.stringify(
96
+ normalizedOrgs ?? []
97
+ )}:${String(auth.publicAccess ?? false)}`;
98
+ } else if (auth.method === "token") {
99
+ config.token = auth.token;
100
+ } else {
101
+ providerKey = `${connection.host}:none`;
102
+ }
103
+ let provider2 = providerKey ? this.providers.get(providerKey) : void 0;
104
+ if (!provider2) {
105
+ provider2 = SingleInstanceGithubCredentialsProvider.create(config);
106
+ if (providerKey) {
107
+ this.providers.set(providerKey, provider2);
108
+ }
109
+ }
110
+ return provider2.getCredentials(opts);
111
+ }
40
112
  const parsed = new URL(opts.url);
41
113
  const provider = this.providers.get(parsed.host);
42
114
  if (!provider) {
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultGithubCredentialsProvider.esm.js","sources":["../../src/github/DefaultGithubCredentialsProvider.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { GithubCredentials, GithubCredentialsProvider } from './types';\nimport { ScmIntegrationRegistry } from '../registry';\nimport { SingleInstanceGithubCredentialsProvider } from './SingleInstanceGithubCredentialsProvider';\n\n/**\n * Handles the creation and caching of credentials for GitHub integrations.\n *\n * @public\n * @remarks\n *\n * TODO: Possibly move this to a backend only package so that it's not used in the frontend by mistake\n */\nexport class DefaultGithubCredentialsProvider\n implements GithubCredentialsProvider\n{\n static fromIntegrations(integrations: ScmIntegrationRegistry) {\n const credentialsProviders: Map<string, GithubCredentialsProvider> =\n new Map<string, GithubCredentialsProvider>();\n\n integrations.github.list().forEach(integration => {\n const credentialsProvider =\n SingleInstanceGithubCredentialsProvider.create(integration.config);\n credentialsProviders.set(integration.config.host, credentialsProvider);\n });\n return new DefaultGithubCredentialsProvider(credentialsProviders);\n }\n\n private constructor(\n private readonly providers: Map<string, GithubCredentialsProvider>,\n ) {}\n\n /**\n * Returns {@link GithubCredentials} for a given URL.\n *\n * @remarks\n *\n * Consecutive calls to this method with the same URL will return cached\n * credentials.\n *\n * The shortest lifetime for a token returned is 10 minutes.\n *\n * @example\n * ```ts\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage/foobar'\n * })\n *\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage'\n * })\n * ```\n *\n * @param opts - The organization or repository URL\n * @returns A promise of {@link GithubCredentials}.\n */\n async getCredentials(opts: { url: string }): Promise<GithubCredentials> {\n const parsed = new URL(opts.url);\n const provider = this.providers.get(parsed.host);\n\n if (!provider) {\n throw new Error(\n `There is no GitHub integration that matches ${opts.url}. Please add a configuration for an integration.`,\n );\n }\n\n return provider.getCredentials(opts);\n }\n}\n"],"names":[],"mappings":";;AA4BO,MAAM,gCAAA,CAEb;AAAA,EAaU,YACW,SAAA,EACjB;AADiB,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AAAA,EAChB;AAAA,EAdH,OAAO,iBAAiB,YAAA,EAAsC;AAC5D,IAAA,MAAM,oBAAA,uBACA,GAAA,EAAuC;AAE7C,IAAA,YAAA,CAAa,MAAA,CAAO,IAAA,EAAK,CAAE,OAAA,CAAQ,CAAA,WAAA,KAAe;AAChD,MAAA,MAAM,mBAAA,GACJ,uCAAA,CAAwC,MAAA,CAAO,WAAA,CAAY,MAAM,CAAA;AACnE,MAAA,oBAAA,CAAqB,GAAA,CAAI,WAAA,CAAY,MAAA,CAAO,IAAA,EAAM,mBAAmB,CAAA;AAAA,IACvE,CAAC,CAAA;AACD,IAAA,OAAO,IAAI,iCAAiC,oBAAoB,CAAA;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,eAAe,IAAA,EAAmD;AACtE,IAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,IAAA,CAAK,GAAG,CAAA;AAC/B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,OAAO,IAAI,CAAA;AAE/C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,4CAAA,EAA+C,KAAK,GAAG,CAAA,gDAAA;AAAA,OACzD;AAAA,IACF;AAEA,IAAA,OAAO,QAAA,CAAS,eAAe,IAAI,CAAA;AAAA,EACrC;AACF;;;;"}
1
+ {"version":3,"file":"DefaultGithubCredentialsProvider.esm.js","sources":["../../src/github/DefaultGithubCredentialsProvider.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { GithubCredentials, GithubCredentialsProvider } from './types';\nimport { ScmIntegrationRegistry } from '../registry';\nimport { SingleInstanceGithubCredentialsProvider } from './SingleInstanceGithubCredentialsProvider';\nimport type { ConnectionsService } from '@backstage/connections';\nimport { ForwardedError, InputError } from '@backstage/errors';\nimport type { GithubIntegrationConfig } from './config';\n\n/**\n * Handles the creation and caching of credentials for GitHub integrations.\n *\n * @public\n * @remarks\n *\n * TODO: Possibly move this to a backend only package so that it's not used in the frontend by mistake\n */\nexport class DefaultGithubCredentialsProvider\n implements GithubCredentialsProvider\n{\n static fromIntegrations(integrations: ScmIntegrationRegistry) {\n const credentialsProviders: Map<string, GithubCredentialsProvider> =\n new Map<string, GithubCredentialsProvider>();\n\n integrations.github.list().forEach(integration => {\n const credentialsProvider =\n SingleInstanceGithubCredentialsProvider.create(integration.config);\n credentialsProviders.set(integration.config.host, credentialsProvider);\n });\n return new DefaultGithubCredentialsProvider(credentialsProviders);\n }\n\n /**\n * Creates a credentials provider backed by the connections service.\n *\n * @param connections - The connections service used to resolve GitHub credentials.\n * @internal\n */\n static experimentalFromConnections(connections: ConnectionsService) {\n return new DefaultGithubCredentialsProvider(\n new Map<string, GithubCredentialsProvider>(),\n connections,\n );\n }\n\n private readonly providers: Map<string, GithubCredentialsProvider>;\n readonly #connections?: ConnectionsService;\n\n private constructor(\n providers: Map<string, GithubCredentialsProvider>,\n connections?: ConnectionsService,\n ) {\n this.providers = providers;\n this.#connections = connections;\n }\n\n /**\n * Returns {@link GithubCredentials} for a given URL.\n *\n * @remarks\n *\n * Consecutive calls to this method with the same URL will return cached\n * credentials.\n *\n * The shortest lifetime for a token returned is 10 minutes.\n *\n * @example\n * ```ts\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage/foobar'\n * })\n *\n * const { token, headers } = await getCredentials({\n * url: 'https://github.com/backstage'\n * })\n * ```\n *\n * @param opts - The organization or repository URL\n * @returns A promise of {@link GithubCredentials}.\n */\n async getCredentials(opts: { url: string }): Promise<GithubCredentials> {\n if (this.#connections) {\n // Ask the connections service to select auth for this URL. A host may\n // have different GitHub Apps for different organizations, so this\n // selection cannot be done once when the provider is created.\n\n const connection = await this.#connections\n .find({\n type: 'github',\n url: opts.url,\n authMethods: ['app', 'token', 'none'],\n })\n .catch(error => {\n throw new ForwardedError(\n 'Failed getting credentials from connection',\n error,\n );\n });\n\n const { auth } = connection;\n\n // Adapt the connection schema to the existing provider configuration so\n // credential creation and token caching stay in one implementation.\n const config: GithubIntegrationConfig = {\n host: connection.host,\n apiBaseUrl: connection.apiBaseUrl,\n rawBaseUrl: connection.rawBaseUrl,\n };\n\n // Reusing an App provider preserves its installation-token cache. App\n // organizations are canonicalized because GitHub owner matching is\n // case-insensitive and does not depend on ordering. Static token\n // providers have no internal token cache, so they are recreated rather\n // than placing their secret token in a cache key.\n let providerKey: string | undefined;\n if (auth.method === 'app') {\n const appId = Number(auth.appId);\n if (!Number.isSafeInteger(appId) || appId <= 0) {\n throw new InputError(\n `Invalid GitHub App ID \"${auth.appId}\", expected a positive safe integer`,\n );\n }\n const normalizedOrgs = auth.orgs?.length\n ? Array.from(\n new Set(auth.orgs.map(org => org.toLocaleLowerCase('en-US'))),\n ).sort()\n : undefined;\n config.apps = [\n {\n appId,\n privateKey: auth.privateKey,\n clientId: auth.clientId,\n clientSecret: auth.clientSecret,\n webhookSecret: auth.webhookSecret,\n publicAccess: auth.publicAccess,\n allowedInstallationOwners: normalizedOrgs,\n },\n ];\n providerKey = `${connection.host}:app:${appId}:${JSON.stringify(\n normalizedOrgs ?? [],\n )}:${String(auth.publicAccess ?? false)}`;\n } else if (auth.method === 'token') {\n config.token = auth.token;\n } else {\n providerKey = `${connection.host}:none`;\n }\n\n let provider = providerKey ? this.providers.get(providerKey) : undefined;\n if (!provider) {\n provider = SingleInstanceGithubCredentialsProvider.create(config);\n if (providerKey) {\n this.providers.set(providerKey, provider);\n }\n }\n\n return provider.getCredentials(opts);\n }\n\n const parsed = new URL(opts.url);\n const provider = this.providers.get(parsed.host);\n\n if (!provider) {\n throw new Error(\n `There is no GitHub integration that matches ${opts.url}. Please add a configuration for an integration.`,\n );\n }\n\n return provider.getCredentials(opts);\n }\n}\n"],"names":["provider"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1577,8 +1577,9 @@ declare function getGithubFileFetchUrl(url: string, config: GithubIntegrationCon
1577
1577
  * TODO: Possibly move this to a backend only package so that it's not used in the frontend by mistake
1578
1578
  */
1579
1579
  declare class DefaultGithubCredentialsProvider implements GithubCredentialsProvider {
1580
- private readonly providers;
1580
+ #private;
1581
1581
  static fromIntegrations(integrations: ScmIntegrationRegistry): DefaultGithubCredentialsProvider;
1582
+ private readonly providers;
1582
1583
  private constructor();
1583
1584
  /**
1584
1585
  * Returns {@link GithubCredentials} for a given URL.
package/package.json CHANGED
@@ -1,15 +1,12 @@
1
1
  {
2
2
  "name": "@backstage/integration",
3
- "version": "2.0.3",
3
+ "version": "2.1.0-next.0",
4
4
  "description": "Helpers for managing integrations towards external systems",
5
5
  "backstage": {
6
6
  "role": "common-library"
7
7
  },
8
8
  "publishConfig": {
9
- "access": "public",
10
- "main": "dist/index.cjs.js",
11
- "module": "dist/index.esm.js",
12
- "types": "dist/index.d.ts"
9
+ "access": "public"
13
10
  },
14
11
  "keywords": [
15
12
  "backstage"
@@ -22,11 +19,36 @@
22
19
  },
23
20
  "license": "Apache-2.0",
24
21
  "sideEffects": false,
25
- "main": "dist/index.cjs.js",
26
- "types": "dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "import": "./dist/index.esm.js",
25
+ "require": "./dist/index.cjs.js",
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.cjs.js"
28
+ },
29
+ "./alpha": {
30
+ "import": "./dist/alpha.esm.js",
31
+ "require": "./dist/alpha.cjs.js",
32
+ "types": "./dist/alpha.d.ts",
33
+ "default": "./dist/alpha.cjs.js"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "main": "./dist/index.cjs.js",
38
+ "types": "./dist/index.d.ts",
39
+ "typesVersions": {
40
+ "*": {
41
+ "alpha": [
42
+ "dist/alpha.d.ts"
43
+ ],
44
+ "package.json": [
45
+ "package.json"
46
+ ]
47
+ }
48
+ },
27
49
  "files": [
28
50
  "dist",
29
- "config.d.ts"
51
+ "config.schema.json"
30
52
  ],
31
53
  "scripts": {
32
54
  "build": "backstage-cli package build",
@@ -39,8 +61,9 @@
39
61
  "dependencies": {
40
62
  "@azure/identity": "^4.0.0",
41
63
  "@azure/storage-blob": "^12.5.0",
42
- "@backstage/config": "^1.3.8",
43
- "@backstage/errors": "^1.3.1",
64
+ "@backstage/config": "1.3.8",
65
+ "@backstage/connections": "0.3.0-next.1",
66
+ "@backstage/errors": "1.3.1",
44
67
  "@octokit/auth-app": "^4.0.0",
45
68
  "@octokit/rest": "^19.0.3",
46
69
  "cross-fetch": "^4.0.0",
@@ -50,18 +73,11 @@
50
73
  "p-throttle": "^4.1.1"
51
74
  },
52
75
  "devDependencies": {
53
- "@backstage/backend-test-utils": "^1.11.4",
54
- "@backstage/cli": "^0.36.3",
55
- "@backstage/config-loader": "^1.10.12",
56
- "msw": "^1.0.0"
57
- },
58
- "configSchema": "config.d.ts",
59
- "typesVersions": {
60
- "*": {
61
- "package.json": [
62
- "package.json"
63
- ]
64
- }
76
+ "@backstage/backend-test-utils": "1.11.6-next.0",
77
+ "@backstage/cli": "0.36.5-next.0",
78
+ "@backstage/config-loader": "1.11.0",
79
+ "msw": "^2.0.0"
65
80
  },
66
- "module": "dist/index.esm.js"
81
+ "configSchema": "config.schema.json",
82
+ "module": "./dist/index.esm.js"
67
83
  }