@backstage/backend-defaults 0.8.0-next.1 → 0.8.0-next.3

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/config.d.ts +4 -0
  3. package/dist/PackageDiscoveryService.cjs.js.map +1 -1
  4. package/dist/entrypoints/database/connectors/postgres.cjs.js +1 -1
  5. package/dist/entrypoints/database/connectors/postgres.cjs.js.map +1 -1
  6. package/dist/entrypoints/permissionsRegistry/permissionsRegistryServiceFactory.cjs.js +20 -3
  7. package/dist/entrypoints/permissionsRegistry/permissionsRegistryServiceFactory.cjs.js.map +1 -1
  8. package/dist/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.cjs.js +27 -2
  9. package/dist/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.cjs.js.map +1 -1
  10. package/dist/entrypoints/urlReader/lib/AwsS3UrlReader.cjs.js +27 -2
  11. package/dist/entrypoints/urlReader/lib/AwsS3UrlReader.cjs.js.map +1 -1
  12. package/dist/entrypoints/urlReader/lib/AzureUrlReader.cjs.js +30 -0
  13. package/dist/entrypoints/urlReader/lib/AzureUrlReader.cjs.js.map +1 -1
  14. package/dist/entrypoints/urlReader/lib/BitbucketCloudUrlReader.cjs.js +24 -0
  15. package/dist/entrypoints/urlReader/lib/BitbucketCloudUrlReader.cjs.js.map +1 -1
  16. package/dist/entrypoints/urlReader/lib/BitbucketServerUrlReader.cjs.js +24 -0
  17. package/dist/entrypoints/urlReader/lib/BitbucketServerUrlReader.cjs.js.map +1 -1
  18. package/dist/entrypoints/urlReader/lib/BitbucketUrlReader.cjs.js +24 -0
  19. package/dist/entrypoints/urlReader/lib/BitbucketUrlReader.cjs.js.map +1 -1
  20. package/dist/entrypoints/urlReader/lib/FetchUrlReader.cjs.js +27 -2
  21. package/dist/entrypoints/urlReader/lib/FetchUrlReader.cjs.js.map +1 -1
  22. package/dist/entrypoints/urlReader/lib/GerritUrlReader.cjs.js +27 -2
  23. package/dist/entrypoints/urlReader/lib/GerritUrlReader.cjs.js.map +1 -1
  24. package/dist/entrypoints/urlReader/lib/GiteaUrlReader.cjs.js +32 -2
  25. package/dist/entrypoints/urlReader/lib/GiteaUrlReader.cjs.js.map +1 -1
  26. package/dist/entrypoints/urlReader/lib/GithubUrlReader.cjs.js +25 -1
  27. package/dist/entrypoints/urlReader/lib/GithubUrlReader.cjs.js.map +1 -1
  28. package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js +27 -2
  29. package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js.map +1 -1
  30. package/dist/entrypoints/urlReader/lib/GoogleGcsUrlReader.cjs.js +26 -1
  31. package/dist/entrypoints/urlReader/lib/GoogleGcsUrlReader.cjs.js.map +1 -1
  32. package/dist/entrypoints/urlReader/lib/HarnessUrlReader.cjs.js +27 -2
  33. package/dist/entrypoints/urlReader/lib/HarnessUrlReader.cjs.js.map +1 -1
  34. package/dist/package.json.cjs.js +1 -1
  35. package/dist/permissionsRegistry.d.ts +2 -1
  36. package/dist/urlReader.d.ts +5 -5
  37. package/package.json +10 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @backstage/backend-defaults
2
2
 
3
+ ## 0.8.0-next.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 3740229: In the different `UrlReadersService`, the `search` method have been updated to use the `readUrl` if the given URL doesn't contain a pattern.
8
+ For `UrlReaders` that didn't implement the `search` method, `readUrl` is now called internally and throws if the given URL doesn't contain a pattern.
9
+ - 72cddf2: Updated `PermissionsRegistryService` to use `PermissionResourceRef`s and added the `getPermissionRuleset` method.
10
+ - Updated dependencies
11
+ - @backstage/plugin-permission-node@0.8.8-next.2
12
+ - @backstage/backend-plugin-api@1.2.0-next.2
13
+ - @backstage/backend-app-api@1.2.0-next.3
14
+ - @backstage/cli-node@0.2.13-next.1
15
+ - @backstage/config-loader@1.9.6-next.0
16
+ - @backstage/plugin-auth-node@0.6.0-next.2
17
+ - @backstage/plugin-events-node@0.4.8-next.2
18
+ - @backstage/backend-dev-utils@0.1.5
19
+ - @backstage/cli-common@0.1.15
20
+ - @backstage/config@1.3.2
21
+ - @backstage/errors@1.2.7
22
+ - @backstage/integration@1.16.1
23
+ - @backstage/integration-aws-node@0.1.15
24
+ - @backstage/types@1.2.1
25
+
26
+ ## 0.8.0-next.2
27
+
28
+ ### Patch Changes
29
+
30
+ - 92a56f6: Internal refactor to stop importing the removed `FeatureDiscoveryService` from `@backstage/backend-plugin-api`.
31
+ - 0d39029: Do not send `etag` or `If-Modified-Since` headers for gitlab artifact urls
32
+ - c7609de: Allow passing IP type to use with cloud-sql-connector
33
+ - Updated dependencies
34
+ - @backstage/backend-plugin-api@1.2.0-next.1
35
+ - @backstage/plugin-auth-node@0.6.0-next.1
36
+ - @backstage/cli-node@0.2.13-next.1
37
+ - @backstage/backend-app-api@1.2.0-next.2
38
+ - @backstage/backend-dev-utils@0.1.5
39
+ - @backstage/cli-common@0.1.15
40
+ - @backstage/config@1.3.2
41
+ - @backstage/config-loader@1.9.6-next.0
42
+ - @backstage/errors@1.2.7
43
+ - @backstage/integration@1.16.1
44
+ - @backstage/integration-aws-node@0.1.15
45
+ - @backstage/types@1.2.1
46
+ - @backstage/plugin-events-node@0.4.8-next.1
47
+ - @backstage/plugin-permission-node@0.8.8-next.1
48
+
3
49
  ## 0.8.0-next.1
4
50
 
5
51
  ### Patch Changes
package/config.d.ts CHANGED
@@ -429,6 +429,10 @@ export interface Config {
429
429
  * The instance connection name for the cloudsql instance, e.g. `project:region:instance`
430
430
  */
431
431
  instance: string;
432
+ /**
433
+ * The ip address type to use for the connection. Defaults to 'PUBLIC'
434
+ */
435
+ ipAddressType?: 'PUBLIC' | 'PRIVATE' | 'PSC';
432
436
  }
433
437
  | {
434
438
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"PackageDiscoveryService.cjs.js","sources":["../src/PackageDiscoveryService.ts"],"sourcesContent":["/*\n * Copyright 2024 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 fs from 'fs-extra';\nimport { resolve as resolvePath, dirname } from 'path';\n\nimport {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n ['object', 'function'].includes(typeof value) &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\n/** @internal */\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nexport class PackageDiscoveryService implements FeatureDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (\n !depPkg?.backstage?.role ||\n !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)\n ) {\n continue; // Not a backstage backend package, ignore\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const mod = require(modulePath);\n\n if (isBackendFeature(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default);\n }\n if (isBackendFeatureFactory(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default());\n }\n }\n }\n\n return { features };\n }\n}\n"],"names":["resolvePath","fs","dirname"],"mappings":"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"}
1
+ {"version":3,"file":"PackageDiscoveryService.cjs.js","sources":["../src/PackageDiscoveryService.ts"],"sourcesContent":["/*\n * Copyright 2024 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 fs from 'fs-extra';\nimport { resolve as resolvePath, dirname } from 'path';\n\nimport {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n ['object', 'function'].includes(typeof value) &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\n/** @internal */\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nexport class PackageDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (\n !depPkg?.backstage?.role ||\n !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)\n ) {\n continue; // Not a backstage backend package, ignore\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const mod = require(modulePath);\n\n if (isBackendFeature(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default);\n }\n if (isBackendFeatureFactory(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default());\n }\n }\n }\n\n return { features };\n }\n}\n"],"names":["resolvePath","fs","dirname"],"mappings":"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"}
@@ -64,7 +64,7 @@ async function buildPgDatabaseConfig(dbConfig, overrides) {
64
64
  const connector = new CloudSqlConnector();
65
65
  const clientOpts = await connector.getOptions({
66
66
  instanceConnectionName: config.connection.instance,
67
- ipType: IpAddressTypes.PUBLIC,
67
+ ipType: config.connection.ipAddressType ?? IpAddressTypes.PUBLIC,
68
68
  authType: AuthTypes.IAM
69
69
  });
70
70
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.cjs.js","sources":["../../../../src/entrypoints/database/connectors/postgres.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 { LifecycleService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { ForwardedError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport knexFactory, { Knex } from 'knex';\nimport { merge, omit } from 'lodash';\nimport limiterFactory from 'p-limit';\nimport { Client } from 'pg';\nimport { Connector } from '../types';\nimport defaultNameOverride from './defaultNameOverride';\nimport defaultSchemaOverride from './defaultSchemaOverride';\nimport { mergeDatabaseConfig } from './mergeDatabaseConfig';\nimport format from 'pg-format';\n\n// Limits the number of concurrent DDL operations to 1\nconst ddlLimiter = limiterFactory(1);\n\n/**\n * Creates a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function createPgDatabaseClient(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const knexConfig = await buildPgDatabaseConfig(dbConfig, overrides);\n const database = knexFactory(knexConfig);\n\n const role = dbConfig.getOptionalString('role');\n\n if (role) {\n database.client.pool.on(\n 'createSuccess',\n async (_event: number, pgClient: Client) => {\n const query = format('SET ROLE %I', role);\n await pgClient.query(query);\n },\n );\n }\n return database;\n}\n\n/**\n * Builds a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function buildPgDatabaseConfig(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const config = mergeDatabaseConfig(\n dbConfig.get(),\n {\n connection: getPgConnectionConfig(dbConfig, !!overrides),\n useNullAsDefault: true,\n },\n overrides,\n );\n\n const sanitizedConfig = JSON.parse(JSON.stringify(config));\n\n // Trim additional properties from the connection object passed to knex\n delete sanitizedConfig.connection.type;\n delete sanitizedConfig.connection.instance;\n\n if (config.connection.type === 'default' || !config.connection.type) {\n return sanitizedConfig;\n }\n\n if (config.connection.type !== 'cloudsql') {\n throw new Error(`Unknown connection type: ${config.connection.type}`);\n }\n\n if (config.client !== 'pg') {\n throw new Error('Cloud SQL only supports the pg client');\n }\n\n if (!config.connection.instance) {\n throw new Error('Missing instance connection name for Cloud SQL');\n }\n\n const {\n Connector: CloudSqlConnector,\n IpAddressTypes,\n AuthTypes,\n } = require('@google-cloud/cloud-sql-connector') as typeof import('@google-cloud/cloud-sql-connector');\n const connector = new CloudSqlConnector();\n const clientOpts = await connector.getOptions({\n instanceConnectionName: config.connection.instance,\n ipType: IpAddressTypes.PUBLIC,\n authType: AuthTypes.IAM,\n });\n\n return {\n ...sanitizedConfig,\n client: 'pg',\n connection: {\n ...sanitizedConfig.connection,\n ...clientOpts,\n },\n };\n}\n\n/**\n * Gets the postgres connection config\n *\n * @param dbConfig - The database config\n * @param parseConnectionString - Flag to explicitly control connection string parsing\n */\nexport function getPgConnectionConfig(\n dbConfig: Config,\n parseConnectionString?: boolean,\n): Knex.PgConnectionConfig | string {\n const connection = dbConfig.get('connection') as any;\n const isConnectionString =\n typeof connection === 'string' || connection instanceof String;\n const autoParse = typeof parseConnectionString !== 'boolean';\n\n const shouldParseConnectionString = autoParse\n ? isConnectionString\n : parseConnectionString && isConnectionString;\n\n return shouldParseConnectionString\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\n/**\n * Parses a connection string using pg-connection-string\n *\n * @param connectionString - The postgres connection string\n */\nexport function parsePgConnectionString(connectionString: string) {\n const parse = requirePgConnectionString();\n return parse(connectionString);\n}\n\nfunction requirePgConnectionString() {\n try {\n return require('pg-connection-string').parse;\n } catch (e) {\n throw new ForwardedError(\"Postgres: Install 'pg-connection-string'\", e);\n }\n}\n\n/**\n * Creates the missing Postgres database if it does not exist\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to create\n */\nexport async function ensurePgDatabaseExists(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = await createPgDatabaseClient(dbConfig, {\n connection: {\n database: 'postgres',\n },\n pool: {\n min: 0,\n acquireTimeoutMillis: 10000,\n },\n });\n\n try {\n const ensureDatabase = async (database: string) => {\n const result = await admin\n .from('pg_database')\n .where('datname', database)\n .count<Record<string, { count: string }>>();\n\n if (parseInt(result[0].count, 10) > 0) {\n return;\n }\n\n await admin.raw(`CREATE DATABASE ??`, [database]);\n };\n\n await Promise.all(\n databases.map(async database => {\n // For initial setup we use a smaller timeout but several retries. Given that this\n // is a separate connection pool we should never really run into issues with connection\n // acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.\n let lastErr: Error | undefined = undefined;\n for (let i = 0; i < 3; i++) {\n try {\n return await ddlLimiter(() => ensureDatabase(database));\n } catch (err) {\n lastErr = err;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n throw lastErr;\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Creates the missing Postgres schema if it does not exist\n *\n * @param dbConfig - The database config\n * @param schemas - The name of the schemas to create\n */\nexport async function ensurePgSchemaExists(\n dbConfig: Config,\n ...schemas: Array<string>\n): Promise<void> {\n const admin = await createPgDatabaseClient(dbConfig);\n const role = dbConfig.getOptionalString('role');\n\n try {\n const ensureSchema = async (database: string) => {\n if (role) {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ?? AUTHORIZATION ??`, [\n database,\n role,\n ]);\n } else {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [database]);\n }\n };\n\n await Promise.all(\n schemas.map(database => ddlLimiter(() => ensureSchema(database))),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Drops the Postgres databases.\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to drop\n */\nexport async function dropPgDatabase(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = await createPgDatabaseClient(dbConfig);\n try {\n await Promise.all(\n databases.map(async database => {\n await ddlLimiter(() => admin.raw(`DROP DATABASE ??`, [database]));\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\nfunction normalizeConnection(\n connection: Knex.StaticConnectionConfig | JsonObject | string | undefined,\n): Partial<Knex.StaticConnectionConfig> {\n if (typeof connection === 'undefined' || connection === null) {\n return {};\n }\n\n return typeof connection === 'string' || connection instanceof String\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\nexport class PgConnector implements Connector {\n constructor(\n private readonly config: Config,\n private readonly prefix: string,\n ) {}\n\n async getClient(\n pluginId: string,\n _deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n const pluginConfig = new ConfigReader(\n this.getConfigForPlugin(pluginId) as JsonObject,\n );\n\n const databaseName = this.getDatabaseName(pluginId);\n if (databaseName && this.getEnsureExistsConfig(pluginId)) {\n try {\n await ensurePgDatabaseExists(pluginConfig, databaseName);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`,\n );\n }\n }\n\n let schemaOverrides;\n if (this.getPluginDivisionModeConfig() === 'schema') {\n schemaOverrides = defaultSchemaOverride(pluginId);\n if (\n this.getEnsureSchemaExistsConfig(pluginId) ||\n this.getEnsureExistsConfig(pluginId)\n ) {\n try {\n await ensurePgSchemaExists(pluginConfig, pluginId);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`,\n );\n }\n }\n }\n\n const databaseClientOverrides = mergeDatabaseConfig(\n {},\n this.getDatabaseOverrides(pluginId),\n schemaOverrides,\n );\n\n const client = createPgDatabaseClient(\n pluginConfig,\n databaseClientOverrides,\n );\n\n return client;\n }\n\n /**\n * Provides the canonical database name for a given plugin.\n *\n * This method provides the effective database name which is determined using global\n * and plugin specific database config. If no explicit database name is configured\n * and `pluginDivisionMode` is not `schema`, this method will provide a generated name\n * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is\n * `schema`, it will fallback to using the default database for the knex instance.\n *\n * @param pluginId - Lookup the database name for given plugin\n * @returns String representing the plugin's database name\n */\n private getDatabaseName(pluginId: string): string | undefined {\n const connection = this.getConnectionConfig(pluginId);\n\n const databaseName = (connection as Knex.ConnectionConfig)?.database;\n\n // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied or fallback to default knex database\n if (this.getPluginDivisionModeConfig() === 'schema') {\n return databaseName;\n }\n\n // all other supported databases should fallback to an auto-prefixed name\n return databaseName ?? `${this.prefix}${pluginId}`;\n }\n\n /**\n * Provides the client type which should be used for a given plugin.\n *\n * The client type is determined by plugin specific config if present.\n * Otherwise the base client is used as the fallback.\n *\n * @param pluginId - Plugin to get the client type for\n * @returns Object with client type returned as `client` and boolean\n * representing whether or not the client was overridden as\n * `overridden`\n */\n private getClientType(pluginId: string): {\n client: string;\n overridden: boolean;\n } {\n const pluginClient = this.config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n\n const baseClient = this.config.getString('client');\n const client = pluginClient ?? baseClient;\n return {\n client,\n overridden: client !== baseClient,\n };\n }\n\n private getRoleConfig(pluginId: string): string | undefined {\n return (\n this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ??\n this.config.getOptionalString('role')\n );\n }\n\n /**\n * Provides the knexConfig which should be used for a given plugin.\n *\n * @param pluginId - Plugin to get the knexConfig for\n * @returns The merged knexConfig value or undefined if it isn't specified\n */\n private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined {\n const pluginConfig = this.config\n .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`)\n ?.get<JsonObject>();\n\n const baseConfig = this.config\n .getOptionalConfig('knexConfig')\n ?.get<JsonObject>();\n\n return merge(baseConfig, pluginConfig);\n }\n\n private getEnsureExistsConfig(pluginId: string): boolean {\n const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true;\n return (\n this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ??\n baseConfig\n );\n }\n\n private getEnsureSchemaExistsConfig(pluginId: string): boolean {\n const baseConfig =\n this.config.getOptionalBoolean('ensureSchemaExists') ?? false;\n return (\n this.config.getOptionalBoolean(\n `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`,\n ) ?? baseConfig\n );\n }\n\n private getPluginDivisionModeConfig(): string {\n return this.config.getOptionalString('pluginDivisionMode') ?? 'database';\n }\n\n /**\n * Provides a Knex connection plugin config by combining base and plugin\n * config.\n *\n * This method provides a baseConfig for a plugin database connector. If the\n * client type has not been overridden, the global connection config will be\n * included with plugin specific config as the base. Values from the plugin\n * connection take precedence over the base. Base database name is omitted\n * unless `pluginDivisionMode` is set to `schema`.\n */\n private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig {\n const { overridden } = this.getClientType(pluginId);\n\n let baseConnection = normalizeConnection(this.config.get('connection'));\n\n // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The\n // `database` property from the base connection is omitted unless `pluginDivisionMode`\n // is set to `schema`.\n if (this.getPluginDivisionModeConfig() !== 'schema') {\n baseConnection = omit(baseConnection, 'database');\n }\n\n // get and normalize optional plugin specific database connection\n const connection = normalizeConnection(\n this.config.getOptional(`${pluginPath(pluginId)}.connection`),\n );\n\n (\n baseConnection as Knex.PgConnectionConfig\n ).application_name ||= `backstage_plugin_${pluginId}`;\n\n return {\n // include base connection if client type has not been overridden\n ...(overridden ? {} : baseConnection),\n ...connection,\n } as Knex.StaticConnectionConfig;\n }\n\n /**\n * Provides a Knex database config for a given plugin.\n *\n * This method provides a Knex configuration object along with the plugin's\n * client type.\n *\n * @param pluginId - The plugin that the database config should correspond with\n */\n private getConfigForPlugin(pluginId: string): Knex.Config {\n const { client } = this.getClientType(pluginId);\n const role = this.getRoleConfig(pluginId);\n\n return {\n ...this.getAdditionalKnexConfig(pluginId),\n client,\n connection: this.getConnectionConfig(pluginId),\n ...(role && { role }),\n };\n }\n\n /**\n * Provides a partial `Knex.Config`• database name override for a given plugin.\n *\n * @param pluginId - Target plugin to get database name override\n * @returns Partial `Knex.Config` with database name override\n */\n private getDatabaseOverrides(pluginId: string): Knex.Config {\n const databaseName = this.getDatabaseName(pluginId);\n return databaseName ? defaultNameOverride(databaseName) : {};\n }\n}\n"],"names":["limiterFactory","knexFactory","format","mergeDatabaseConfig","ForwardedError","ConfigReader","defaultSchemaOverride","merge","omit","defaultNameOverride"],"mappings":"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"}
1
+ {"version":3,"file":"postgres.cjs.js","sources":["../../../../src/entrypoints/database/connectors/postgres.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 { LifecycleService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { ForwardedError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport knexFactory, { Knex } from 'knex';\nimport { merge, omit } from 'lodash';\nimport limiterFactory from 'p-limit';\nimport { Client } from 'pg';\nimport { Connector } from '../types';\nimport defaultNameOverride from './defaultNameOverride';\nimport defaultSchemaOverride from './defaultSchemaOverride';\nimport { mergeDatabaseConfig } from './mergeDatabaseConfig';\nimport format from 'pg-format';\n\n// Limits the number of concurrent DDL operations to 1\nconst ddlLimiter = limiterFactory(1);\n\n/**\n * Creates a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function createPgDatabaseClient(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const knexConfig = await buildPgDatabaseConfig(dbConfig, overrides);\n const database = knexFactory(knexConfig);\n\n const role = dbConfig.getOptionalString('role');\n\n if (role) {\n database.client.pool.on(\n 'createSuccess',\n async (_event: number, pgClient: Client) => {\n const query = format('SET ROLE %I', role);\n await pgClient.query(query);\n },\n );\n }\n return database;\n}\n\n/**\n * Builds a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function buildPgDatabaseConfig(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const config = mergeDatabaseConfig(\n dbConfig.get(),\n {\n connection: getPgConnectionConfig(dbConfig, !!overrides),\n useNullAsDefault: true,\n },\n overrides,\n );\n\n const sanitizedConfig = JSON.parse(JSON.stringify(config));\n\n // Trim additional properties from the connection object passed to knex\n delete sanitizedConfig.connection.type;\n delete sanitizedConfig.connection.instance;\n\n if (config.connection.type === 'default' || !config.connection.type) {\n return sanitizedConfig;\n }\n\n if (config.connection.type !== 'cloudsql') {\n throw new Error(`Unknown connection type: ${config.connection.type}`);\n }\n\n if (config.client !== 'pg') {\n throw new Error('Cloud SQL only supports the pg client');\n }\n\n if (!config.connection.instance) {\n throw new Error('Missing instance connection name for Cloud SQL');\n }\n\n const {\n Connector: CloudSqlConnector,\n IpAddressTypes,\n AuthTypes,\n } = require('@google-cloud/cloud-sql-connector') as typeof import('@google-cloud/cloud-sql-connector');\n const connector = new CloudSqlConnector();\n const clientOpts = await connector.getOptions({\n instanceConnectionName: config.connection.instance,\n ipType: config.connection.ipAddressType ?? IpAddressTypes.PUBLIC,\n authType: AuthTypes.IAM,\n });\n\n return {\n ...sanitizedConfig,\n client: 'pg',\n connection: {\n ...sanitizedConfig.connection,\n ...clientOpts,\n },\n };\n}\n\n/**\n * Gets the postgres connection config\n *\n * @param dbConfig - The database config\n * @param parseConnectionString - Flag to explicitly control connection string parsing\n */\nexport function getPgConnectionConfig(\n dbConfig: Config,\n parseConnectionString?: boolean,\n): Knex.PgConnectionConfig | string {\n const connection = dbConfig.get('connection') as any;\n const isConnectionString =\n typeof connection === 'string' || connection instanceof String;\n const autoParse = typeof parseConnectionString !== 'boolean';\n\n const shouldParseConnectionString = autoParse\n ? isConnectionString\n : parseConnectionString && isConnectionString;\n\n return shouldParseConnectionString\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\n/**\n * Parses a connection string using pg-connection-string\n *\n * @param connectionString - The postgres connection string\n */\nexport function parsePgConnectionString(connectionString: string) {\n const parse = requirePgConnectionString();\n return parse(connectionString);\n}\n\nfunction requirePgConnectionString() {\n try {\n return require('pg-connection-string').parse;\n } catch (e) {\n throw new ForwardedError(\"Postgres: Install 'pg-connection-string'\", e);\n }\n}\n\n/**\n * Creates the missing Postgres database if it does not exist\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to create\n */\nexport async function ensurePgDatabaseExists(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = await createPgDatabaseClient(dbConfig, {\n connection: {\n database: 'postgres',\n },\n pool: {\n min: 0,\n acquireTimeoutMillis: 10000,\n },\n });\n\n try {\n const ensureDatabase = async (database: string) => {\n const result = await admin\n .from('pg_database')\n .where('datname', database)\n .count<Record<string, { count: string }>>();\n\n if (parseInt(result[0].count, 10) > 0) {\n return;\n }\n\n await admin.raw(`CREATE DATABASE ??`, [database]);\n };\n\n await Promise.all(\n databases.map(async database => {\n // For initial setup we use a smaller timeout but several retries. Given that this\n // is a separate connection pool we should never really run into issues with connection\n // acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.\n let lastErr: Error | undefined = undefined;\n for (let i = 0; i < 3; i++) {\n try {\n return await ddlLimiter(() => ensureDatabase(database));\n } catch (err) {\n lastErr = err;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n throw lastErr;\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Creates the missing Postgres schema if it does not exist\n *\n * @param dbConfig - The database config\n * @param schemas - The name of the schemas to create\n */\nexport async function ensurePgSchemaExists(\n dbConfig: Config,\n ...schemas: Array<string>\n): Promise<void> {\n const admin = await createPgDatabaseClient(dbConfig);\n const role = dbConfig.getOptionalString('role');\n\n try {\n const ensureSchema = async (database: string) => {\n if (role) {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ?? AUTHORIZATION ??`, [\n database,\n role,\n ]);\n } else {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [database]);\n }\n };\n\n await Promise.all(\n schemas.map(database => ddlLimiter(() => ensureSchema(database))),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Drops the Postgres databases.\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to drop\n */\nexport async function dropPgDatabase(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = await createPgDatabaseClient(dbConfig);\n try {\n await Promise.all(\n databases.map(async database => {\n await ddlLimiter(() => admin.raw(`DROP DATABASE ??`, [database]));\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\nfunction normalizeConnection(\n connection: Knex.StaticConnectionConfig | JsonObject | string | undefined,\n): Partial<Knex.StaticConnectionConfig> {\n if (typeof connection === 'undefined' || connection === null) {\n return {};\n }\n\n return typeof connection === 'string' || connection instanceof String\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\nexport class PgConnector implements Connector {\n constructor(\n private readonly config: Config,\n private readonly prefix: string,\n ) {}\n\n async getClient(\n pluginId: string,\n _deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n const pluginConfig = new ConfigReader(\n this.getConfigForPlugin(pluginId) as JsonObject,\n );\n\n const databaseName = this.getDatabaseName(pluginId);\n if (databaseName && this.getEnsureExistsConfig(pluginId)) {\n try {\n await ensurePgDatabaseExists(pluginConfig, databaseName);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`,\n );\n }\n }\n\n let schemaOverrides;\n if (this.getPluginDivisionModeConfig() === 'schema') {\n schemaOverrides = defaultSchemaOverride(pluginId);\n if (\n this.getEnsureSchemaExistsConfig(pluginId) ||\n this.getEnsureExistsConfig(pluginId)\n ) {\n try {\n await ensurePgSchemaExists(pluginConfig, pluginId);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`,\n );\n }\n }\n }\n\n const databaseClientOverrides = mergeDatabaseConfig(\n {},\n this.getDatabaseOverrides(pluginId),\n schemaOverrides,\n );\n\n const client = createPgDatabaseClient(\n pluginConfig,\n databaseClientOverrides,\n );\n\n return client;\n }\n\n /**\n * Provides the canonical database name for a given plugin.\n *\n * This method provides the effective database name which is determined using global\n * and plugin specific database config. If no explicit database name is configured\n * and `pluginDivisionMode` is not `schema`, this method will provide a generated name\n * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is\n * `schema`, it will fallback to using the default database for the knex instance.\n *\n * @param pluginId - Lookup the database name for given plugin\n * @returns String representing the plugin's database name\n */\n private getDatabaseName(pluginId: string): string | undefined {\n const connection = this.getConnectionConfig(pluginId);\n\n const databaseName = (connection as Knex.ConnectionConfig)?.database;\n\n // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied or fallback to default knex database\n if (this.getPluginDivisionModeConfig() === 'schema') {\n return databaseName;\n }\n\n // all other supported databases should fallback to an auto-prefixed name\n return databaseName ?? `${this.prefix}${pluginId}`;\n }\n\n /**\n * Provides the client type which should be used for a given plugin.\n *\n * The client type is determined by plugin specific config if present.\n * Otherwise the base client is used as the fallback.\n *\n * @param pluginId - Plugin to get the client type for\n * @returns Object with client type returned as `client` and boolean\n * representing whether or not the client was overridden as\n * `overridden`\n */\n private getClientType(pluginId: string): {\n client: string;\n overridden: boolean;\n } {\n const pluginClient = this.config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n\n const baseClient = this.config.getString('client');\n const client = pluginClient ?? baseClient;\n return {\n client,\n overridden: client !== baseClient,\n };\n }\n\n private getRoleConfig(pluginId: string): string | undefined {\n return (\n this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ??\n this.config.getOptionalString('role')\n );\n }\n\n /**\n * Provides the knexConfig which should be used for a given plugin.\n *\n * @param pluginId - Plugin to get the knexConfig for\n * @returns The merged knexConfig value or undefined if it isn't specified\n */\n private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined {\n const pluginConfig = this.config\n .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`)\n ?.get<JsonObject>();\n\n const baseConfig = this.config\n .getOptionalConfig('knexConfig')\n ?.get<JsonObject>();\n\n return merge(baseConfig, pluginConfig);\n }\n\n private getEnsureExistsConfig(pluginId: string): boolean {\n const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true;\n return (\n this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ??\n baseConfig\n );\n }\n\n private getEnsureSchemaExistsConfig(pluginId: string): boolean {\n const baseConfig =\n this.config.getOptionalBoolean('ensureSchemaExists') ?? false;\n return (\n this.config.getOptionalBoolean(\n `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`,\n ) ?? baseConfig\n );\n }\n\n private getPluginDivisionModeConfig(): string {\n return this.config.getOptionalString('pluginDivisionMode') ?? 'database';\n }\n\n /**\n * Provides a Knex connection plugin config by combining base and plugin\n * config.\n *\n * This method provides a baseConfig for a plugin database connector. If the\n * client type has not been overridden, the global connection config will be\n * included with plugin specific config as the base. Values from the plugin\n * connection take precedence over the base. Base database name is omitted\n * unless `pluginDivisionMode` is set to `schema`.\n */\n private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig {\n const { overridden } = this.getClientType(pluginId);\n\n let baseConnection = normalizeConnection(this.config.get('connection'));\n\n // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The\n // `database` property from the base connection is omitted unless `pluginDivisionMode`\n // is set to `schema`.\n if (this.getPluginDivisionModeConfig() !== 'schema') {\n baseConnection = omit(baseConnection, 'database');\n }\n\n // get and normalize optional plugin specific database connection\n const connection = normalizeConnection(\n this.config.getOptional(`${pluginPath(pluginId)}.connection`),\n );\n\n (\n baseConnection as Knex.PgConnectionConfig\n ).application_name ||= `backstage_plugin_${pluginId}`;\n\n return {\n // include base connection if client type has not been overridden\n ...(overridden ? {} : baseConnection),\n ...connection,\n } as Knex.StaticConnectionConfig;\n }\n\n /**\n * Provides a Knex database config for a given plugin.\n *\n * This method provides a Knex configuration object along with the plugin's\n * client type.\n *\n * @param pluginId - The plugin that the database config should correspond with\n */\n private getConfigForPlugin(pluginId: string): Knex.Config {\n const { client } = this.getClientType(pluginId);\n const role = this.getRoleConfig(pluginId);\n\n return {\n ...this.getAdditionalKnexConfig(pluginId),\n client,\n connection: this.getConnectionConfig(pluginId),\n ...(role && { role }),\n };\n }\n\n /**\n * Provides a partial `Knex.Config`• database name override for a given plugin.\n *\n * @param pluginId - Target plugin to get database name override\n * @returns Partial `Knex.Config` with database name override\n */\n private getDatabaseOverrides(pluginId: string): Knex.Config {\n const databaseName = this.getDatabaseName(pluginId);\n return databaseName ? defaultNameOverride(databaseName) : {};\n }\n}\n"],"names":["limiterFactory","knexFactory","format","mergeDatabaseConfig","ForwardedError","ConfigReader","defaultSchemaOverride","merge","omit","defaultNameOverride"],"mappings":"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"}
@@ -3,14 +3,23 @@
3
3
  var backendPluginApi = require('@backstage/backend-plugin-api');
4
4
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
5
5
 
6
+ function assertRefPluginId(ref, pluginId) {
7
+ if (ref.pluginId !== pluginId) {
8
+ throw new Error(
9
+ `Resource type '${ref.resourceType}' belongs to plugin '${ref.pluginId}', but was used with plugin '${pluginId}'`
10
+ );
11
+ }
12
+ }
6
13
  const permissionsRegistryServiceFactory = backendPluginApi.createServiceFactory({
7
14
  service: backendPluginApi.coreServices.permissionsRegistry,
8
15
  deps: {
9
16
  lifecycle: backendPluginApi.coreServices.lifecycle,
10
- httpRouter: backendPluginApi.coreServices.httpRouter
17
+ httpRouter: backendPluginApi.coreServices.httpRouter,
18
+ pluginMetadata: backendPluginApi.coreServices.pluginMetadata
11
19
  },
12
- async factory({ httpRouter, lifecycle }) {
20
+ async factory({ httpRouter, lifecycle, pluginMetadata }) {
13
21
  const router = pluginPermissionNode.createPermissionIntegrationRouter();
22
+ const pluginId = pluginMetadata.getId();
14
23
  httpRouter.use(router);
15
24
  let started = false;
16
25
  lifecycle.addStartupHook(() => {
@@ -23,7 +32,11 @@ const permissionsRegistryServiceFactory = backendPluginApi.createServiceFactory(
23
32
  "Cannot add permission resource types after the plugin has started"
24
33
  );
25
34
  }
26
- router.addResourceType(resource);
35
+ assertRefPluginId(resource.resourceRef, pluginId);
36
+ router.addResourceType({
37
+ ...resource,
38
+ resourceType: resource.resourceRef.resourceType
39
+ });
27
40
  },
28
41
  addPermissions(permissions) {
29
42
  if (started) {
@@ -40,6 +53,10 @@ const permissionsRegistryServiceFactory = backendPluginApi.createServiceFactory(
40
53
  );
41
54
  }
42
55
  router.addPermissionRules(rules);
56
+ },
57
+ getPermissionRuleset(resourceRef) {
58
+ assertRefPluginId(resourceRef, pluginId);
59
+ return router.getPermissionRuleset(resourceRef);
43
60
  }
44
61
  };
45
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"permissionsRegistryServiceFactory.cjs.js","sources":["../../../src/entrypoints/permissionsRegistry/permissionsRegistryServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2022 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 {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\n\n/**\n * Permission system integration for registering resources and permissions.\n *\n * See {@link @backstage/core-plugin-api#PermissionsRegistryService}\n * and {@link https://backstage.io/docs/backend-system/core-services/permission-integrations | the service docs}\n * for more information.\n *\n * @public\n */\nexport const permissionsRegistryServiceFactory = createServiceFactory({\n service: coreServices.permissionsRegistry,\n deps: {\n lifecycle: coreServices.lifecycle,\n httpRouter: coreServices.httpRouter,\n },\n async factory({ httpRouter, lifecycle }) {\n const router = createPermissionIntegrationRouter();\n\n httpRouter.use(router);\n\n let started = false;\n lifecycle.addStartupHook(() => {\n started = true;\n });\n\n return {\n addResourceType(resource) {\n if (started) {\n throw new Error(\n 'Cannot add permission resource types after the plugin has started',\n );\n }\n router.addResourceType(resource);\n },\n addPermissions(permissions) {\n if (started) {\n throw new Error(\n 'Cannot add permissions after the plugin has started',\n );\n }\n router.addPermissions(permissions);\n },\n addPermissionRules(rules) {\n if (started) {\n throw new Error(\n 'Cannot add permission rules after the plugin has started',\n );\n }\n router.addPermissionRules(rules);\n },\n };\n },\n});\n"],"names":["createServiceFactory","coreServices","createPermissionIntegrationRouter"],"mappings":";;;;;AA+BO,MAAM,oCAAoCA,qCAAqB,CAAA;AAAA,EACpE,SAASC,6BAAa,CAAA,mBAAA;AAAA,EACtB,IAAM,EAAA;AAAA,IACJ,WAAWA,6BAAa,CAAA,SAAA;AAAA,IACxB,YAAYA,6BAAa,CAAA;AAAA,GAC3B;AAAA,EACA,MAAM,OAAA,CAAQ,EAAE,UAAA,EAAY,WAAa,EAAA;AACvC,IAAA,MAAM,SAASC,sDAAkC,EAAA;AAEjD,IAAA,UAAA,CAAW,IAAI,MAAM,CAAA;AAErB,IAAA,IAAI,OAAU,GAAA,KAAA;AACd,IAAA,SAAA,CAAU,eAAe,MAAM;AAC7B,MAAU,OAAA,GAAA,IAAA;AAAA,KACX,CAAA;AAED,IAAO,OAAA;AAAA,MACL,gBAAgB,QAAU,EAAA;AACxB,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAA,MAAA,CAAO,gBAAgB,QAAQ,CAAA;AAAA,OACjC;AAAA,MACA,eAAe,WAAa,EAAA;AAC1B,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAA,MAAA,CAAO,eAAe,WAAW,CAAA;AAAA,OACnC;AAAA,MACA,mBAAmB,KAAO,EAAA;AACxB,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAA,MAAA,CAAO,mBAAmB,KAAK,CAAA;AAAA;AACjC,KACF;AAAA;AAEJ,CAAC;;;;"}
1
+ {"version":3,"file":"permissionsRegistryServiceFactory.cjs.js","sources":["../../../src/entrypoints/permissionsRegistry/permissionsRegistryServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2022 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 {\n PermissionsRegistryService,\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport {\n PermissionResourceRef,\n createPermissionIntegrationRouter,\n} from '@backstage/plugin-permission-node';\n\nfunction assertRefPluginId(ref: PermissionResourceRef, pluginId: string) {\n if (ref.pluginId !== pluginId) {\n throw new Error(\n `Resource type '${ref.resourceType}' belongs to plugin '${ref.pluginId}', but was used with plugin '${pluginId}'`,\n );\n }\n}\n\n/**\n * Permission system integration for registering resources and permissions.\n *\n * See {@link @backstage/core-plugin-api#PermissionsRegistryService}\n * and {@link https://backstage.io/docs/backend-system/core-services/permission-integrations | the service docs}\n * for more information.\n *\n * @public\n */\nexport const permissionsRegistryServiceFactory = createServiceFactory({\n service: coreServices.permissionsRegistry,\n deps: {\n lifecycle: coreServices.lifecycle,\n httpRouter: coreServices.httpRouter,\n pluginMetadata: coreServices.pluginMetadata,\n },\n async factory({ httpRouter, lifecycle, pluginMetadata }) {\n const router = createPermissionIntegrationRouter();\n const pluginId = pluginMetadata.getId();\n\n httpRouter.use(router);\n\n let started = false;\n lifecycle.addStartupHook(() => {\n started = true;\n });\n\n return {\n addResourceType(resource) {\n if (started) {\n throw new Error(\n 'Cannot add permission resource types after the plugin has started',\n );\n }\n assertRefPluginId(resource.resourceRef, pluginId);\n router.addResourceType({\n ...resource,\n resourceType: resource.resourceRef.resourceType,\n });\n },\n addPermissions(permissions) {\n if (started) {\n throw new Error(\n 'Cannot add permissions after the plugin has started',\n );\n }\n router.addPermissions(permissions);\n },\n addPermissionRules(rules) {\n if (started) {\n throw new Error(\n 'Cannot add permission rules after the plugin has started',\n );\n }\n router.addPermissionRules(rules);\n },\n getPermissionRuleset(resourceRef) {\n assertRefPluginId(resourceRef, pluginId);\n return router.getPermissionRuleset(resourceRef);\n },\n } satisfies PermissionsRegistryService;\n },\n});\n"],"names":["createServiceFactory","coreServices","createPermissionIntegrationRouter"],"mappings":";;;;;AA0BA,SAAS,iBAAA,CAAkB,KAA4B,QAAkB,EAAA;AACvE,EAAI,IAAA,GAAA,CAAI,aAAa,QAAU,EAAA;AAC7B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,kBAAkB,GAAI,CAAA,YAAY,wBAAwB,GAAI,CAAA,QAAQ,gCAAgC,QAAQ,CAAA,CAAA;AAAA,KAChH;AAAA;AAEJ;AAWO,MAAM,oCAAoCA,qCAAqB,CAAA;AAAA,EACpE,SAASC,6BAAa,CAAA,mBAAA;AAAA,EACtB,IAAM,EAAA;AAAA,IACJ,WAAWA,6BAAa,CAAA,SAAA;AAAA,IACxB,YAAYA,6BAAa,CAAA,UAAA;AAAA,IACzB,gBAAgBA,6BAAa,CAAA;AAAA,GAC/B;AAAA,EACA,MAAM,OAAQ,CAAA,EAAE,UAAY,EAAA,SAAA,EAAW,gBAAkB,EAAA;AACvD,IAAA,MAAM,SAASC,sDAAkC,EAAA;AACjD,IAAM,MAAA,QAAA,GAAW,eAAe,KAAM,EAAA;AAEtC,IAAA,UAAA,CAAW,IAAI,MAAM,CAAA;AAErB,IAAA,IAAI,OAAU,GAAA,KAAA;AACd,IAAA,SAAA,CAAU,eAAe,MAAM;AAC7B,MAAU,OAAA,GAAA,IAAA;AAAA,KACX,CAAA;AAED,IAAO,OAAA;AAAA,MACL,gBAAgB,QAAU,EAAA;AACxB,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAkB,iBAAA,CAAA,QAAA,CAAS,aAAa,QAAQ,CAAA;AAChD,QAAA,MAAA,CAAO,eAAgB,CAAA;AAAA,UACrB,GAAG,QAAA;AAAA,UACH,YAAA,EAAc,SAAS,WAAY,CAAA;AAAA,SACpC,CAAA;AAAA,OACH;AAAA,MACA,eAAe,WAAa,EAAA;AAC1B,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAA,MAAA,CAAO,eAAe,WAAW,CAAA;AAAA,OACnC;AAAA,MACA,mBAAmB,KAAO,EAAA;AACxB,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAA,MAAA,CAAO,mBAAmB,KAAK,CAAA;AAAA,OACjC;AAAA,MACA,qBAAqB,WAAa,EAAA;AAChC,QAAA,iBAAA,CAAkB,aAAa,QAAQ,CAAA;AACvC,QAAO,OAAA,MAAA,CAAO,qBAAqB,WAAW,CAAA;AAAA;AAChD,KACF;AAAA;AAEJ,CAAC;;;;"}
@@ -258,8 +258,33 @@ class AwsCodeCommitUrlReader {
258
258
  );
259
259
  }
260
260
  }
261
- async search() {
262
- throw new Error("AwsCodeCommitReader does not implement search");
261
+ async search(url, options) {
262
+ const { path } = parseUrl(url, true);
263
+ if (path.match(/[*?]/)) {
264
+ throw new Error("Unsupported search pattern URL");
265
+ }
266
+ try {
267
+ const data = await this.readUrl(url, options);
268
+ return {
269
+ files: [
270
+ {
271
+ url,
272
+ content: data.buffer,
273
+ lastModifiedAt: data.lastModifiedAt
274
+ }
275
+ ],
276
+ etag: data.etag ?? ""
277
+ };
278
+ } catch (error) {
279
+ errors.assertError(error);
280
+ if (error.name === "NotFoundError") {
281
+ return {
282
+ files: [],
283
+ etag: ""
284
+ };
285
+ }
286
+ throw error;
287
+ }
263
288
  }
264
289
  toString() {
265
290
  const secretAccessKey = this.integration.config.secretAccessKey;
@@ -1 +1 @@
1
- {"version":3,"file":"AwsCodeCommitUrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.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 { ReaderFactory, ReadTreeResponseFactory } from './types';\nimport {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\nimport {\n AwsCredentialsManager,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport {\n AwsCodeCommitIntegration,\n ScmIntegrations,\n} from '@backstage/integration';\nimport { ForwardedError, NotModifiedError } from '@backstage/errors';\nimport { fromTemporaryCredentials } from '@aws-sdk/credential-providers';\nimport {\n CodeCommitClient,\n GetFileCommand,\n GetFileCommandInput,\n GetFileCommandOutput,\n GetFolderCommand,\n} from '@aws-sdk/client-codecommit';\nimport { AwsCredentialIdentityProvider } from '@aws-sdk/types';\nimport { Readable } from 'stream';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport { relative } from 'path/posix';\nimport { AbortController } from '@aws-sdk/abort-controller';\n\nexport function parseUrl(\n url: string,\n requireGitPath: boolean = false,\n): {\n path: string;\n repositoryName: string;\n region: string;\n commitSpecifier?: string;\n} {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.pathname.includes('/files/edit/')) {\n throw new Error(\n 'Please provide the view url to yaml file from CodeCommit, not the edit url',\n );\n }\n if (requireGitPath && !parsedUrl.pathname.includes('/browse/')) {\n throw new Error('Please provide full path to yaml file from CodeCommit');\n }\n\n const hostMatch = parsedUrl.host.match(\n /^([^\\.]+)\\.console\\.aws\\.amazon\\.com$/,\n );\n if (!hostMatch) {\n throw new Error(\n `Invalid AWS CodeCommit URL (unexpected host format): ${url}`,\n );\n }\n const [, region] = hostMatch;\n\n const pathMatch = parsedUrl.pathname.match(\n /^\\/codesuite\\/codecommit\\/repositories\\/([^\\/]+)\\/browse\\/((.*)\\/)?--\\/(.*)$/,\n );\n\n if (!pathMatch) {\n if (!requireGitPath) {\n const pathname = parsedUrl.pathname\n .split('/--/')[0]\n .replace('/codesuite/codecommit/repositories/', '');\n const [repositoryName, commitSpecifier] = pathname.split('/browse');\n\n return {\n region,\n repositoryName: repositoryName.replace(/^\\/|\\/$/g, ''),\n path: '/',\n commitSpecifier:\n commitSpecifier === ''\n ? undefined\n : commitSpecifier?.replace(/^\\/|\\/$/g, ''),\n };\n }\n throw new Error(\n `Invalid AWS CodeCommit URL (unexpected path format): ${url}`,\n );\n }\n const [, repositoryName, , commitSpecifier, path] = pathMatch;\n\n return {\n region,\n repositoryName,\n path,\n // the commitSpecifier is passed to AWS SDK which does not allow empty strings so replace empty string with undefined\n commitSpecifier: commitSpecifier === '' ? undefined : commitSpecifier,\n };\n}\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for AWS CodeCommit.\n *\n * @public\n */\nexport class AwsCodeCommitUrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n const credsManager = DefaultAwsCredentialsManager.fromConfig(config);\n\n return integrations.awsCodeCommit.list().map(integration => {\n const reader = new AwsCodeCommitUrlReader(credsManager, integration, {\n treeResponseFactory,\n });\n const predicate = (url: URL) => {\n return (\n url.host.endsWith(integration.config.host) &&\n url.pathname.startsWith('/codesuite/codecommit')\n );\n };\n\n return { reader, predicate };\n });\n };\n\n constructor(\n private readonly credsManager: AwsCredentialsManager,\n private readonly integration: AwsCodeCommitIntegration,\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {}\n\n /**\n * If accessKeyId and secretAccessKey are missing, the standard credentials provider chain will be used:\n * https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html\n */\n private static buildStaticCredentials(\n accessKeyId: string,\n secretAccessKey: string,\n ): AwsCredentialIdentityProvider {\n return async () => {\n return {\n accessKeyId,\n secretAccessKey,\n };\n };\n }\n\n private static async buildCredentials(\n credsManager: AwsCredentialsManager,\n region: string,\n integration?: AwsCodeCommitIntegration,\n ): Promise<AwsCredentialIdentityProvider> {\n // Fall back to the default credential chain if neither account ID\n // nor explicit credentials are provided\n if (!integration) {\n return (await credsManager.getCredentialProvider()).sdkCredentialProvider;\n }\n\n const accessKeyId = integration.config.accessKeyId;\n const secretAccessKey = integration.config.secretAccessKey;\n let explicitCredentials: AwsCredentialIdentityProvider;\n if (accessKeyId && secretAccessKey) {\n explicitCredentials = AwsCodeCommitUrlReader.buildStaticCredentials(\n accessKeyId,\n secretAccessKey,\n );\n } else {\n explicitCredentials = (await credsManager.getCredentialProvider())\n .sdkCredentialProvider;\n }\n\n const roleArn = integration.config.roleArn;\n if (roleArn) {\n return fromTemporaryCredentials({\n masterCredentials: explicitCredentials,\n params: {\n RoleSessionName: 'backstage-aws-code-commit-url-reader',\n RoleArn: roleArn,\n ExternalId: integration.config.externalId,\n },\n clientConfig: { region },\n });\n }\n\n return explicitCredentials;\n }\n\n private async buildCodeCommitClient(\n credsManager: AwsCredentialsManager,\n region: string,\n integration: AwsCodeCommitIntegration,\n ): Promise<CodeCommitClient> {\n const credentials = await AwsCodeCommitUrlReader.buildCredentials(\n credsManager,\n region,\n integration,\n );\n\n const codeCommit = new CodeCommitClient({\n customUserAgent: 'backstage-aws-codecommit-url-reader',\n region: region,\n credentials: credentials,\n });\n return codeCommit;\n }\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n // etag and lastModifiedAfter are not supported by the CodeCommit API\n try {\n const { path, repositoryName, region, commitSpecifier } = parseUrl(\n url,\n true,\n );\n const codeCommitClient = await this.buildCodeCommitClient(\n this.credsManager,\n region,\n this.integration,\n );\n const abortController = new AbortController();\n\n const input: GetFileCommandInput = {\n repositoryName: repositoryName,\n commitSpecifier: commitSpecifier,\n filePath: path,\n };\n\n options?.signal?.addEventListener('abort', () => abortController.abort());\n const getObjectCommand = new GetFileCommand(input);\n const response: GetFileCommandOutput = await codeCommitClient.send(\n getObjectCommand,\n {\n abortSignal: abortController.signal,\n },\n );\n\n if (options?.etag && options.etag === response.commitId) {\n throw new NotModifiedError();\n }\n\n return ReadUrlResponseFactory.fromReadable(\n Readable.from([response?.fileContent] || []),\n {\n etag: response.commitId,\n },\n );\n } catch (e) {\n if (e.$metadata && e.$metadata.httpStatusCode === 304) {\n throw new NotModifiedError();\n }\n if (e.name && e.name === 'NotModifiedError') {\n throw new NotModifiedError();\n }\n\n throw new ForwardedError('Could not retrieve file from CodeCommit', e);\n }\n }\n\n async readTreePath(\n codeCommitClient: CodeCommitClient,\n abortSignal: any,\n path: string,\n repositoryName: string,\n commitSpecifier?: string,\n etag?: string,\n ): Promise<string[]> {\n const getFolderCommand = new GetFolderCommand({\n folderPath: path,\n repositoryName: repositoryName,\n commitSpecifier: commitSpecifier,\n });\n const response = await codeCommitClient.send(getFolderCommand, {\n abortSignal: abortSignal,\n });\n\n if (etag && etag === response.commitId) {\n throw new NotModifiedError();\n }\n\n const output: string[] = [];\n if (response.files) {\n response.files.forEach(file => {\n if (file.absolutePath) {\n output.push(file.absolutePath);\n }\n });\n }\n if (!response.subFolders) {\n return output;\n }\n\n for (const subFolder of response.subFolders) {\n if (subFolder.absolutePath) {\n output.push(\n ...(await this.readTreePath(\n codeCommitClient,\n abortSignal,\n subFolder.absolutePath,\n repositoryName,\n commitSpecifier,\n etag,\n )),\n );\n }\n }\n return output;\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n // url: https://eu-west-1.console.aws.amazon.com/codesuite/codecommit/repositories/test-stijn-delete-techdocs/browse?region=eu-west-1\n try {\n const { path, repositoryName, region, commitSpecifier } = parseUrl(url);\n const codeCommitClient = await this.buildCodeCommitClient(\n this.credsManager,\n region,\n this.integration,\n );\n\n const abortController = new AbortController();\n options?.signal?.addEventListener('abort', () => abortController.abort());\n\n const allFiles: string[] = await this.readTreePath(\n codeCommitClient,\n abortController.signal,\n path,\n repositoryName,\n commitSpecifier,\n options?.etag,\n );\n const responses = [];\n\n for (let i = 0; i < allFiles.length; i++) {\n const getFileCommand = new GetFileCommand({\n repositoryName: repositoryName,\n filePath: String(allFiles[i]),\n commitSpecifier: commitSpecifier,\n });\n const response = await codeCommitClient.send(getFileCommand);\n const objectData = await Readable.from([response?.fileContent] || []);\n\n responses.push({\n data: objectData,\n path: relative(\n path.startsWith('/') ? path : `/${path}`,\n allFiles[i].startsWith('/') ? allFiles[i] : `/${allFiles[i]}`,\n ),\n });\n }\n\n return await this.deps.treeResponseFactory.fromReadableArray(responses);\n } catch (e) {\n if (e.name && e.name === 'NotModifiedError') {\n throw new NotModifiedError();\n }\n throw new ForwardedError(\n 'Could not retrieve file tree from CodeCommit',\n e,\n );\n }\n }\n\n async search(): Promise<UrlReaderServiceSearchResponse> {\n throw new Error('AwsCodeCommitReader does not implement search');\n }\n\n toString() {\n const secretAccessKey = this.integration.config.secretAccessKey;\n return `awsCodeCommit{host=${this.integration.config.host},authed=${Boolean(\n secretAccessKey,\n )}}`;\n }\n}\n"],"names":["repositoryName","commitSpecifier","ScmIntegrations","DefaultAwsCredentialsManager","fromTemporaryCredentials","CodeCommitClient","abortController","AbortController","GetFileCommand","NotModifiedError","ReadUrlResponseFactory","Readable","ForwardedError","GetFolderCommand","relative"],"mappings":";;;;;;;;;;;;AAgDgB,SAAA,QAAA,CACd,GACA,EAAA,cAAA,GAA0B,KAM1B,EAAA;AACA,EAAM,MAAA,SAAA,GAAY,IAAI,GAAA,CAAI,GAAG,CAAA;AAE7B,EAAA,IAAI,SAAU,CAAA,QAAA,CAAS,QAAS,CAAA,cAAc,CAAG,EAAA;AAC/C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA;AAEF,EAAA,IAAI,kBAAkB,CAAC,SAAA,CAAU,QAAS,CAAA,QAAA,CAAS,UAAU,CAAG,EAAA;AAC9D,IAAM,MAAA,IAAI,MAAM,uDAAuD,CAAA;AAAA;AAGzE,EAAM,MAAA,SAAA,GAAY,UAAU,IAAK,CAAA,KAAA;AAAA,IAC/B;AAAA,GACF;AACA,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wDAAwD,GAAG,CAAA;AAAA,KAC7D;AAAA;AAEF,EAAM,MAAA,GAAG,MAAM,CAAI,GAAA,SAAA;AAEnB,EAAM,MAAA,SAAA,GAAY,UAAU,QAAS,CAAA,KAAA;AAAA,IACnC;AAAA,GACF;AAEA,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAA,IAAI,CAAC,cAAgB,EAAA;AACnB,MAAM,MAAA,QAAA,GAAW,SAAU,CAAA,QAAA,CACxB,KAAM,CAAA,MAAM,EAAE,CAAC,CAAA,CACf,OAAQ,CAAA,qCAAA,EAAuC,EAAE,CAAA;AACpD,MAAA,MAAM,CAACA,eAAgBC,EAAAA,gBAAe,CAAI,GAAA,QAAA,CAAS,MAAM,SAAS,CAAA;AAElE,MAAO,OAAA;AAAA,QACL,MAAA;AAAA,QACA,cAAgBD,EAAAA,eAAAA,CAAe,OAAQ,CAAA,UAAA,EAAY,EAAE,CAAA;AAAA,QACrD,IAAM,EAAA,GAAA;AAAA,QACN,iBACEC,gBAAoB,KAAA,EAAA,GAChB,SACAA,gBAAiB,EAAA,OAAA,CAAQ,YAAY,EAAE;AAAA,OAC/C;AAAA;AAEF,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wDAAwD,GAAG,CAAA;AAAA,KAC7D;AAAA;AAEF,EAAA,MAAM,GAAG,cAAA,IAAkB,eAAA,EAAiB,IAAI,CAAI,GAAA,SAAA;AAEpD,EAAO,OAAA;AAAA,IACL,MAAA;AAAA,IACA,cAAA;AAAA,IACA,IAAA;AAAA;AAAA,IAEA,eAAA,EAAiB,eAAoB,KAAA,EAAA,GAAK,KAAY,CAAA,GAAA;AAAA,GACxD;AACF;AAOO,MAAM,sBAAmD,CAAA;AAAA,EAoB9D,WAAA,CACmB,YACA,EAAA,WAAA,EACA,IAGjB,EAAA;AALiB,IAAA,IAAA,CAAA,YAAA,GAAA,YAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAGhB,EAzBH,OAAO,OAAyB,GAAA,CAAC,EAAE,MAAA,EAAQ,qBAA0B,KAAA;AACnE,IAAM,MAAA,YAAA,GAAeC,2BAAgB,CAAA,UAAA,CAAW,MAAM,CAAA;AACtD,IAAM,MAAA,YAAA,GAAeC,+CAA6B,CAAA,UAAA,CAAW,MAAM,CAAA;AAEnE,IAAA,OAAO,YAAa,CAAA,aAAA,CAAc,IAAK,EAAA,CAAE,IAAI,CAAe,WAAA,KAAA;AAC1D,MAAA,MAAM,MAAS,GAAA,IAAI,sBAAuB,CAAA,YAAA,EAAc,WAAa,EAAA;AAAA,QACnE;AAAA,OACD,CAAA;AACD,MAAM,MAAA,SAAA,GAAY,CAAC,GAAa,KAAA;AAC9B,QACE,OAAA,GAAA,CAAI,IAAK,CAAA,QAAA,CAAS,WAAY,CAAA,MAAA,CAAO,IAAI,CACzC,IAAA,GAAA,CAAI,QAAS,CAAA,UAAA,CAAW,uBAAuB,CAAA;AAAA,OAEnD;AAEA,MAAO,OAAA,EAAE,QAAQ,SAAU,EAAA;AAAA,KAC5B,CAAA;AAAA,GACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAe,sBACb,CAAA,WAAA,EACA,eAC+B,EAAA;AAC/B,IAAA,OAAO,YAAY;AACjB,MAAO,OAAA;AAAA,QACL,WAAA;AAAA,QACA;AAAA,OACF;AAAA,KACF;AAAA;AACF,EAEA,aAAqB,gBAAA,CACnB,YACA,EAAA,MAAA,EACA,WACwC,EAAA;AAGxC,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAQ,OAAA,CAAA,MAAM,YAAa,CAAA,qBAAA,EAAyB,EAAA,qBAAA;AAAA;AAGtD,IAAM,MAAA,WAAA,GAAc,YAAY,MAAO,CAAA,WAAA;AACvC,IAAM,MAAA,eAAA,GAAkB,YAAY,MAAO,CAAA,eAAA;AAC3C,IAAI,IAAA,mBAAA;AACJ,IAAA,IAAI,eAAe,eAAiB,EAAA;AAClC,MAAA,mBAAA,GAAsB,sBAAuB,CAAA,sBAAA;AAAA,QAC3C,WAAA;AAAA,QACA;AAAA,OACF;AAAA,KACK,MAAA;AACL,MAAuB,mBAAA,GAAA,CAAA,MAAM,YAAa,CAAA,qBAAA,EACvC,EAAA,qBAAA;AAAA;AAGL,IAAM,MAAA,OAAA,GAAU,YAAY,MAAO,CAAA,OAAA;AACnC,IAAA,IAAI,OAAS,EAAA;AACX,MAAA,OAAOC,4CAAyB,CAAA;AAAA,QAC9B,iBAAmB,EAAA,mBAAA;AAAA,QACnB,MAAQ,EAAA;AAAA,UACN,eAAiB,EAAA,sCAAA;AAAA,UACjB,OAAS,EAAA,OAAA;AAAA,UACT,UAAA,EAAY,YAAY,MAAO,CAAA;AAAA,SACjC;AAAA,QACA,YAAA,EAAc,EAAE,MAAO;AAAA,OACxB,CAAA;AAAA;AAGH,IAAO,OAAA,mBAAA;AAAA;AACT,EAEA,MAAc,qBAAA,CACZ,YACA,EAAA,MAAA,EACA,WAC2B,EAAA;AAC3B,IAAM,MAAA,WAAA,GAAc,MAAM,sBAAuB,CAAA,gBAAA;AAAA,MAC/C,YAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAM,MAAA,UAAA,GAAa,IAAIC,iCAAiB,CAAA;AAAA,MACtC,eAAiB,EAAA,qCAAA;AAAA,MACjB,MAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAO,OAAA,UAAA;AAAA;AACT,EAEA,MAAM,OACJ,CAAA,GAAA,EACA,OAC0C,EAAA;AAE1C,IAAI,IAAA;AACF,MAAA,MAAM,EAAE,IAAA,EAAM,cAAgB,EAAA,MAAA,EAAQ,iBAAoB,GAAA,QAAA;AAAA,QACxD,GAAA;AAAA,QACA;AAAA,OACF;AACA,MAAM,MAAA,gBAAA,GAAmB,MAAM,IAAK,CAAA,qBAAA;AAAA,QAClC,IAAK,CAAA,YAAA;AAAA,QACL,MAAA;AAAA,QACA,IAAK,CAAA;AAAA,OACP;AACA,MAAM,MAAAC,iBAAA,GAAkB,IAAIC,+BAAgB,EAAA;AAE5C,MAAA,MAAM,KAA6B,GAAA;AAAA,QACjC,cAAA;AAAA,QACA,eAAA;AAAA,QACA,QAAU,EAAA;AAAA,OACZ;AAEA,MAAA,OAAA,EAAS,QAAQ,gBAAiB,CAAA,OAAA,EAAS,MAAMD,iBAAA,CAAgB,OAAO,CAAA;AACxE,MAAM,MAAA,gBAAA,GAAmB,IAAIE,+BAAA,CAAe,KAAK,CAAA;AACjD,MAAM,MAAA,QAAA,GAAiC,MAAM,gBAAiB,CAAA,IAAA;AAAA,QAC5D,gBAAA;AAAA,QACA;AAAA,UACE,aAAaF,iBAAgB,CAAA;AAAA;AAC/B,OACF;AAEA,MAAA,IAAI,OAAS,EAAA,IAAA,IAAQ,OAAQ,CAAA,IAAA,KAAS,SAAS,QAAU,EAAA;AACvD,QAAA,MAAM,IAAIG,uBAAiB,EAAA;AAAA;AAG7B,MAAA,OAAOC,6CAAuB,CAAA,YAAA;AAAA,QAC5BC,eAAS,CAAA,IAAA,CAAK,CAAC,QAAA,EAAU,WAAW,CAAO,CAAA;AAAA,QAC3C;AAAA,UACE,MAAM,QAAS,CAAA;AAAA;AACjB,OACF;AAAA,aACO,CAAG,EAAA;AACV,MAAA,IAAI,CAAE,CAAA,SAAA,IAAa,CAAE,CAAA,SAAA,CAAU,mBAAmB,GAAK,EAAA;AACrD,QAAA,MAAM,IAAIF,uBAAiB,EAAA;AAAA;AAE7B,MAAA,IAAI,CAAE,CAAA,IAAA,IAAQ,CAAE,CAAA,IAAA,KAAS,kBAAoB,EAAA;AAC3C,QAAA,MAAM,IAAIA,uBAAiB,EAAA;AAAA;AAG7B,MAAM,MAAA,IAAIG,qBAAe,CAAA,yCAAA,EAA2C,CAAC,CAAA;AAAA;AACvE;AACF,EAEA,MAAM,YACJ,CAAA,gBAAA,EACA,aACA,IACA,EAAA,cAAA,EACA,iBACA,IACmB,EAAA;AACnB,IAAM,MAAA,gBAAA,GAAmB,IAAIC,iCAAiB,CAAA;AAAA,MAC5C,UAAY,EAAA,IAAA;AAAA,MACZ,cAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,MAAM,QAAW,GAAA,MAAM,gBAAiB,CAAA,IAAA,CAAK,gBAAkB,EAAA;AAAA,MAC7D;AAAA,KACD,CAAA;AAED,IAAI,IAAA,IAAA,IAAQ,IAAS,KAAA,QAAA,CAAS,QAAU,EAAA;AACtC,MAAA,MAAM,IAAIJ,uBAAiB,EAAA;AAAA;AAG7B,IAAA,MAAM,SAAmB,EAAC;AAC1B,IAAA,IAAI,SAAS,KAAO,EAAA;AAClB,MAAS,QAAA,CAAA,KAAA,CAAM,QAAQ,CAAQ,IAAA,KAAA;AAC7B,QAAA,IAAI,KAAK,YAAc,EAAA;AACrB,UAAO,MAAA,CAAA,IAAA,CAAK,KAAK,YAAY,CAAA;AAAA;AAC/B,OACD,CAAA;AAAA;AAEH,IAAI,IAAA,CAAC,SAAS,UAAY,EAAA;AACxB,MAAO,OAAA,MAAA;AAAA;AAGT,IAAW,KAAA,MAAA,SAAA,IAAa,SAAS,UAAY,EAAA;AAC3C,MAAA,IAAI,UAAU,YAAc,EAAA;AAC1B,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,GAAI,MAAM,IAAK,CAAA,YAAA;AAAA,YACb,gBAAA;AAAA,YACA,WAAA;AAAA,YACA,SAAU,CAAA,YAAA;AAAA,YACV,cAAA;AAAA,YACA,eAAA;AAAA,YACA;AAAA;AACF,SACF;AAAA;AACF;AAEF,IAAO,OAAA,MAAA;AAAA;AACT,EAEA,MAAM,QACJ,CAAA,GAAA,EACA,OAC2C,EAAA;AAE3C,IAAI,IAAA;AACF,MAAA,MAAM,EAAE,IAAM,EAAA,cAAA,EAAgB,QAAQ,eAAgB,EAAA,GAAI,SAAS,GAAG,CAAA;AACtE,MAAM,MAAA,gBAAA,GAAmB,MAAM,IAAK,CAAA,qBAAA;AAAA,QAClC,IAAK,CAAA,YAAA;AAAA,QACL,MAAA;AAAA,QACA,IAAK,CAAA;AAAA,OACP;AAEA,MAAM,MAAAH,iBAAA,GAAkB,IAAIC,+BAAgB,EAAA;AAC5C,MAAA,OAAA,EAAS,QAAQ,gBAAiB,CAAA,OAAA,EAAS,MAAMD,iBAAA,CAAgB,OAAO,CAAA;AAExE,MAAM,MAAA,QAAA,GAAqB,MAAM,IAAK,CAAA,YAAA;AAAA,QACpC,gBAAA;AAAA,QACAA,iBAAgB,CAAA,MAAA;AAAA,QAChB,IAAA;AAAA,QACA,cAAA;AAAA,QACA,eAAA;AAAA,QACA,OAAS,EAAA;AAAA,OACX;AACA,MAAA,MAAM,YAAY,EAAC;AAEnB,MAAA,KAAA,IAAS,CAAI,GAAA,CAAA,EAAG,CAAI,GAAA,QAAA,CAAS,QAAQ,CAAK,EAAA,EAAA;AACxC,QAAM,MAAA,cAAA,GAAiB,IAAIE,+BAAe,CAAA;AAAA,UACxC,cAAA;AAAA,UACA,QAAU,EAAA,MAAA,CAAO,QAAS,CAAA,CAAC,CAAC,CAAA;AAAA,UAC5B;AAAA,SACD,CAAA;AACD,QAAA,MAAM,QAAW,GAAA,MAAM,gBAAiB,CAAA,IAAA,CAAK,cAAc,CAAA;AAC3D,QAAA,MAAM,aAAa,MAAMG,eAAA,CAAS,KAAK,CAAC,QAAA,EAAU,WAAW,CAAO,CAAA;AAEpE,QAAA,SAAA,CAAU,IAAK,CAAA;AAAA,UACb,IAAM,EAAA,UAAA;AAAA,UACN,IAAM,EAAAG,cAAA;AAAA,YACJ,KAAK,UAAW,CAAA,GAAG,CAAI,GAAA,IAAA,GAAO,IAAI,IAAI,CAAA,CAAA;AAAA,YACtC,QAAS,CAAA,CAAC,CAAE,CAAA,UAAA,CAAW,GAAG,CAAA,GAAI,QAAS,CAAA,CAAC,CAAI,GAAA,CAAA,CAAA,EAAI,QAAS,CAAA,CAAC,CAAC,CAAA;AAAA;AAC7D,SACD,CAAA;AAAA;AAGH,MAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,mBAAA,CAAoB,kBAAkB,SAAS,CAAA;AAAA,aAC/D,CAAG,EAAA;AACV,MAAA,IAAI,CAAE,CAAA,IAAA,IAAQ,CAAE,CAAA,IAAA,KAAS,kBAAoB,EAAA;AAC3C,QAAA,MAAM,IAAIL,uBAAiB,EAAA;AAAA;AAE7B,MAAA,MAAM,IAAIG,qBAAA;AAAA,QACR,8CAAA;AAAA,QACA;AAAA,OACF;AAAA;AACF;AACF,EAEA,MAAM,MAAkD,GAAA;AACtD,IAAM,MAAA,IAAI,MAAM,+CAA+C,CAAA;AAAA;AACjE,EAEA,QAAW,GAAA;AACT,IAAM,MAAA,eAAA,GAAkB,IAAK,CAAA,WAAA,CAAY,MAAO,CAAA,eAAA;AAChD,IAAA,OAAO,CAAsB,mBAAA,EAAA,IAAA,CAAK,WAAY,CAAA,MAAA,CAAO,IAAI,CAAW,QAAA,EAAA,OAAA;AAAA,MAClE;AAAA,KACD,CAAA,CAAA,CAAA;AAAA;AAEL;;;;;"}
1
+ {"version":3,"file":"AwsCodeCommitUrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.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 { ReaderFactory, ReadTreeResponseFactory } from './types';\nimport {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchOptions,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\nimport {\n AwsCredentialsManager,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport {\n AwsCodeCommitIntegration,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n assertError,\n ForwardedError,\n NotModifiedError,\n} from '@backstage/errors';\nimport { fromTemporaryCredentials } from '@aws-sdk/credential-providers';\nimport {\n CodeCommitClient,\n GetFileCommand,\n GetFileCommandInput,\n GetFileCommandOutput,\n GetFolderCommand,\n} from '@aws-sdk/client-codecommit';\nimport { AwsCredentialIdentityProvider } from '@aws-sdk/types';\nimport { Readable } from 'stream';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport { relative } from 'path/posix';\nimport { AbortController } from '@aws-sdk/abort-controller';\n\nexport function parseUrl(\n url: string,\n requireGitPath: boolean = false,\n): {\n path: string;\n repositoryName: string;\n region: string;\n commitSpecifier?: string;\n} {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.pathname.includes('/files/edit/')) {\n throw new Error(\n 'Please provide the view url to yaml file from CodeCommit, not the edit url',\n );\n }\n if (requireGitPath && !parsedUrl.pathname.includes('/browse/')) {\n throw new Error('Please provide full path to yaml file from CodeCommit');\n }\n\n const hostMatch = parsedUrl.host.match(\n /^([^\\.]+)\\.console\\.aws\\.amazon\\.com$/,\n );\n if (!hostMatch) {\n throw new Error(\n `Invalid AWS CodeCommit URL (unexpected host format): ${url}`,\n );\n }\n const [, region] = hostMatch;\n\n const pathMatch = parsedUrl.pathname.match(\n /^\\/codesuite\\/codecommit\\/repositories\\/([^\\/]+)\\/browse\\/((.*)\\/)?--\\/(.*)$/,\n );\n\n if (!pathMatch) {\n if (!requireGitPath) {\n const pathname = parsedUrl.pathname\n .split('/--/')[0]\n .replace('/codesuite/codecommit/repositories/', '');\n const [repositoryName, commitSpecifier] = pathname.split('/browse');\n\n return {\n region,\n repositoryName: repositoryName.replace(/^\\/|\\/$/g, ''),\n path: '/',\n commitSpecifier:\n commitSpecifier === ''\n ? undefined\n : commitSpecifier?.replace(/^\\/|\\/$/g, ''),\n };\n }\n throw new Error(\n `Invalid AWS CodeCommit URL (unexpected path format): ${url}`,\n );\n }\n const [, repositoryName, , commitSpecifier, path] = pathMatch;\n\n return {\n region,\n repositoryName,\n path,\n // the commitSpecifier is passed to AWS SDK which does not allow empty strings so replace empty string with undefined\n commitSpecifier: commitSpecifier === '' ? undefined : commitSpecifier,\n };\n}\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for AWS CodeCommit.\n *\n * @public\n */\nexport class AwsCodeCommitUrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n const credsManager = DefaultAwsCredentialsManager.fromConfig(config);\n\n return integrations.awsCodeCommit.list().map(integration => {\n const reader = new AwsCodeCommitUrlReader(credsManager, integration, {\n treeResponseFactory,\n });\n const predicate = (url: URL) => {\n return (\n url.host.endsWith(integration.config.host) &&\n url.pathname.startsWith('/codesuite/codecommit')\n );\n };\n\n return { reader, predicate };\n });\n };\n\n constructor(\n private readonly credsManager: AwsCredentialsManager,\n private readonly integration: AwsCodeCommitIntegration,\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {}\n\n /**\n * If accessKeyId and secretAccessKey are missing, the standard credentials provider chain will be used:\n * https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html\n */\n private static buildStaticCredentials(\n accessKeyId: string,\n secretAccessKey: string,\n ): AwsCredentialIdentityProvider {\n return async () => {\n return {\n accessKeyId,\n secretAccessKey,\n };\n };\n }\n\n private static async buildCredentials(\n credsManager: AwsCredentialsManager,\n region: string,\n integration?: AwsCodeCommitIntegration,\n ): Promise<AwsCredentialIdentityProvider> {\n // Fall back to the default credential chain if neither account ID\n // nor explicit credentials are provided\n if (!integration) {\n return (await credsManager.getCredentialProvider()).sdkCredentialProvider;\n }\n\n const accessKeyId = integration.config.accessKeyId;\n const secretAccessKey = integration.config.secretAccessKey;\n let explicitCredentials: AwsCredentialIdentityProvider;\n if (accessKeyId && secretAccessKey) {\n explicitCredentials = AwsCodeCommitUrlReader.buildStaticCredentials(\n accessKeyId,\n secretAccessKey,\n );\n } else {\n explicitCredentials = (await credsManager.getCredentialProvider())\n .sdkCredentialProvider;\n }\n\n const roleArn = integration.config.roleArn;\n if (roleArn) {\n return fromTemporaryCredentials({\n masterCredentials: explicitCredentials,\n params: {\n RoleSessionName: 'backstage-aws-code-commit-url-reader',\n RoleArn: roleArn,\n ExternalId: integration.config.externalId,\n },\n clientConfig: { region },\n });\n }\n\n return explicitCredentials;\n }\n\n private async buildCodeCommitClient(\n credsManager: AwsCredentialsManager,\n region: string,\n integration: AwsCodeCommitIntegration,\n ): Promise<CodeCommitClient> {\n const credentials = await AwsCodeCommitUrlReader.buildCredentials(\n credsManager,\n region,\n integration,\n );\n\n const codeCommit = new CodeCommitClient({\n customUserAgent: 'backstage-aws-codecommit-url-reader',\n region: region,\n credentials: credentials,\n });\n return codeCommit;\n }\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n // etag and lastModifiedAfter are not supported by the CodeCommit API\n try {\n const { path, repositoryName, region, commitSpecifier } = parseUrl(\n url,\n true,\n );\n const codeCommitClient = await this.buildCodeCommitClient(\n this.credsManager,\n region,\n this.integration,\n );\n const abortController = new AbortController();\n\n const input: GetFileCommandInput = {\n repositoryName: repositoryName,\n commitSpecifier: commitSpecifier,\n filePath: path,\n };\n\n options?.signal?.addEventListener('abort', () => abortController.abort());\n const getObjectCommand = new GetFileCommand(input);\n const response: GetFileCommandOutput = await codeCommitClient.send(\n getObjectCommand,\n {\n abortSignal: abortController.signal,\n },\n );\n\n if (options?.etag && options.etag === response.commitId) {\n throw new NotModifiedError();\n }\n\n return ReadUrlResponseFactory.fromReadable(\n Readable.from([response?.fileContent]),\n {\n etag: response.commitId,\n },\n );\n } catch (e) {\n if (e.$metadata && e.$metadata.httpStatusCode === 304) {\n throw new NotModifiedError();\n }\n if (e.name && e.name === 'NotModifiedError') {\n throw new NotModifiedError();\n }\n\n throw new ForwardedError('Could not retrieve file from CodeCommit', e);\n }\n }\n\n async readTreePath(\n codeCommitClient: CodeCommitClient,\n abortSignal: any,\n path: string,\n repositoryName: string,\n commitSpecifier?: string,\n etag?: string,\n ): Promise<string[]> {\n const getFolderCommand = new GetFolderCommand({\n folderPath: path,\n repositoryName: repositoryName,\n commitSpecifier: commitSpecifier,\n });\n const response = await codeCommitClient.send(getFolderCommand, {\n abortSignal: abortSignal,\n });\n\n if (etag && etag === response.commitId) {\n throw new NotModifiedError();\n }\n\n const output: string[] = [];\n if (response.files) {\n response.files.forEach(file => {\n if (file.absolutePath) {\n output.push(file.absolutePath);\n }\n });\n }\n if (!response.subFolders) {\n return output;\n }\n\n for (const subFolder of response.subFolders) {\n if (subFolder.absolutePath) {\n output.push(\n ...(await this.readTreePath(\n codeCommitClient,\n abortSignal,\n subFolder.absolutePath,\n repositoryName,\n commitSpecifier,\n etag,\n )),\n );\n }\n }\n return output;\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n // url: https://eu-west-1.console.aws.amazon.com/codesuite/codecommit/repositories/test-stijn-delete-techdocs/browse?region=eu-west-1\n try {\n const { path, repositoryName, region, commitSpecifier } = parseUrl(url);\n const codeCommitClient = await this.buildCodeCommitClient(\n this.credsManager,\n region,\n this.integration,\n );\n\n const abortController = new AbortController();\n options?.signal?.addEventListener('abort', () => abortController.abort());\n\n const allFiles: string[] = await this.readTreePath(\n codeCommitClient,\n abortController.signal,\n path,\n repositoryName,\n commitSpecifier,\n options?.etag,\n );\n const responses = [];\n\n for (let i = 0; i < allFiles.length; i++) {\n const getFileCommand = new GetFileCommand({\n repositoryName: repositoryName,\n filePath: String(allFiles[i]),\n commitSpecifier: commitSpecifier,\n });\n const response = await codeCommitClient.send(getFileCommand);\n const objectData = await Readable.from([response?.fileContent]);\n\n responses.push({\n data: objectData,\n path: relative(\n path.startsWith('/') ? path : `/${path}`,\n allFiles[i].startsWith('/') ? allFiles[i] : `/${allFiles[i]}`,\n ),\n });\n }\n\n return await this.deps.treeResponseFactory.fromReadableArray(responses);\n } catch (e) {\n if (e.name && e.name === 'NotModifiedError') {\n throw new NotModifiedError();\n }\n throw new ForwardedError(\n 'Could not retrieve file tree from CodeCommit',\n e,\n );\n }\n }\n\n async search(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n const { path } = parseUrl(url, true);\n\n if (path.match(/[*?]/)) {\n throw new Error('Unsupported search pattern URL');\n }\n\n try {\n const data = await this.readUrl(url, options);\n\n return {\n files: [\n {\n url: url,\n content: data.buffer,\n lastModifiedAt: data.lastModifiedAt,\n },\n ],\n etag: data.etag ?? '',\n };\n } catch (error) {\n assertError(error);\n if (error.name === 'NotFoundError') {\n return {\n files: [],\n etag: '',\n };\n }\n throw error;\n }\n }\n\n toString() {\n const secretAccessKey = this.integration.config.secretAccessKey;\n return `awsCodeCommit{host=${this.integration.config.host},authed=${Boolean(\n secretAccessKey,\n )}}`;\n }\n}\n"],"names":["repositoryName","commitSpecifier","ScmIntegrations","DefaultAwsCredentialsManager","fromTemporaryCredentials","CodeCommitClient","abortController","AbortController","GetFileCommand","NotModifiedError","ReadUrlResponseFactory","Readable","ForwardedError","GetFolderCommand","relative","assertError"],"mappings":"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"}
@@ -244,8 +244,33 @@ class AwsS3UrlReader {
244
244
  throw new errors.ForwardedError("Could not retrieve file tree from S3", e);
245
245
  }
246
246
  }
247
- async search() {
248
- throw new Error("AwsS3Reader does not implement search");
247
+ async search(url, options) {
248
+ const { path } = parseUrl(url, this.integration.config);
249
+ if (path.match(/[*?]/)) {
250
+ throw new Error("Unsupported search pattern URL");
251
+ }
252
+ try {
253
+ const data = await this.readUrl(url, options);
254
+ return {
255
+ files: [
256
+ {
257
+ url,
258
+ content: data.buffer,
259
+ lastModifiedAt: data.lastModifiedAt
260
+ }
261
+ ],
262
+ etag: data.etag ?? ""
263
+ };
264
+ } catch (error) {
265
+ errors.assertError(error);
266
+ if (error.name === "NotFoundError") {
267
+ return {
268
+ files: [],
269
+ etag: ""
270
+ };
271
+ }
272
+ throw error;
273
+ }
249
274
  }
250
275
  toString() {
251
276
  const secretAccessKey = this.integration.config.secretAccessKey;
@@ -1 +1 @@
1
- {"version":3,"file":"AwsS3UrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/AwsS3UrlReader.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 {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\nimport { ReaderFactory, ReadTreeResponseFactory } from './types';\nimport {\n AwsCredentialsManager,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport {\n AwsS3Integration,\n ScmIntegrations,\n AwsS3IntegrationConfig,\n} from '@backstage/integration';\nimport { ForwardedError, NotModifiedError } from '@backstage/errors';\nimport { fromTemporaryCredentials } from '@aws-sdk/credential-providers';\nimport { AwsCredentialIdentityProvider } from '@aws-sdk/types';\nimport {\n S3Client,\n ListObjectsV2Command,\n ListObjectsV2CommandOutput,\n GetObjectCommand,\n GetObjectCommandInput,\n} from '@aws-sdk/client-s3';\nimport { AbortController } from '@aws-sdk/abort-controller';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport { Readable } from 'stream';\nimport { relative } from 'path/posix';\n\nexport const DEFAULT_REGION = 'us-east-1';\n\n/**\n * Path style URLs: https://s3.(region).amazonaws.com/(bucket)/(key)\n * The region can also be on the old form: https://s3-(region).amazonaws.com/(bucket)/(key)\n * Virtual hosted style URLs: https://(bucket).s3.(region).amazonaws.com/(key)\n * See https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#path-style-access\n */\nexport function parseUrl(\n url: string,\n config: AwsS3IntegrationConfig,\n): { path: string; bucket: string; region: string } {\n const parsedUrl = new URL(url);\n\n /**\n * Removes the leading '/' from the pathname to be processed\n * as a parameter by AWS S3 SDK getObject method.\n */\n const pathname = parsedUrl.pathname.substring(1);\n const host = parsedUrl.host;\n\n // Treat Amazon hosted separately because it has special region logic\n if (config.host === 'amazonaws.com' || config.host === 'amazonaws.com.cn') {\n const match = host.match(\n /^(?:([a-z0-9.-]+)\\.)?s3(?:[.-]([a-z0-9-]+))?\\.amazonaws\\.com(\\.cn)?$/,\n );\n if (!match) {\n throw new Error(`Invalid AWS S3 URL ${url}`);\n }\n\n const [, hostBucket, hostRegion] = match;\n\n if (config.s3ForcePathStyle || !hostBucket) {\n const slashIndex = pathname.indexOf('/');\n if (slashIndex < 0) {\n throw new Error(\n `Invalid path-style AWS S3 URL ${url}, does not contain bucket in the path`,\n );\n }\n\n return {\n path: pathname.substring(slashIndex + 1),\n bucket: pathname.substring(0, slashIndex),\n region: hostRegion ?? DEFAULT_REGION,\n };\n }\n\n return {\n path: pathname,\n bucket: hostBucket,\n region: hostRegion ?? DEFAULT_REGION,\n };\n }\n\n const usePathStyle =\n config.s3ForcePathStyle || host.length === config.host.length;\n\n if (usePathStyle) {\n const slashIndex = pathname.indexOf('/');\n if (slashIndex < 0) {\n throw new Error(\n `Invalid path-style AWS S3 URL ${url}, does not contain bucket in the path`,\n );\n }\n\n return {\n path: pathname.substring(slashIndex + 1),\n bucket: pathname.substring(0, slashIndex),\n region: DEFAULT_REGION,\n };\n }\n\n return {\n path: pathname,\n bucket: host.substring(0, host.length - config.host.length - 1),\n region: DEFAULT_REGION,\n };\n}\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for AWS S3 buckets.\n *\n * @public\n */\nexport class AwsS3UrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n const credsManager = DefaultAwsCredentialsManager.fromConfig(config);\n\n return integrations.awsS3.list().map(integration => {\n const reader = new AwsS3UrlReader(credsManager, integration, {\n treeResponseFactory,\n });\n const predicate = (url: URL) =>\n url.host.endsWith(integration.config.host);\n return { reader, predicate };\n });\n };\n\n constructor(\n private readonly credsManager: AwsCredentialsManager,\n private readonly integration: AwsS3Integration,\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {}\n\n /**\n * If accessKeyId and secretAccessKey are missing, the standard credentials provider chain will be used:\n * https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html\n */\n private static buildStaticCredentials(\n accessKeyId: string,\n secretAccessKey: string,\n ): AwsCredentialIdentityProvider {\n return async () => {\n return {\n accessKeyId,\n secretAccessKey,\n };\n };\n }\n\n private static async buildCredentials(\n credsManager: AwsCredentialsManager,\n region: string,\n integration?: AwsS3Integration,\n ): Promise<AwsCredentialIdentityProvider> {\n // Fall back to the default credential chain if neither account ID\n // nor explicit credentials are provided\n if (!integration) {\n return (await credsManager.getCredentialProvider()).sdkCredentialProvider;\n }\n\n const accessKeyId = integration.config.accessKeyId;\n const secretAccessKey = integration.config.secretAccessKey;\n let explicitCredentials: AwsCredentialIdentityProvider;\n if (accessKeyId && secretAccessKey) {\n explicitCredentials = AwsS3UrlReader.buildStaticCredentials(\n accessKeyId,\n secretAccessKey,\n );\n } else {\n explicitCredentials = (await credsManager.getCredentialProvider())\n .sdkCredentialProvider;\n }\n\n const roleArn = integration.config.roleArn;\n if (roleArn) {\n return fromTemporaryCredentials({\n masterCredentials: explicitCredentials,\n params: {\n RoleSessionName: 'backstage-aws-s3-url-reader',\n RoleArn: roleArn,\n ExternalId: integration.config.externalId,\n },\n clientConfig: { region },\n });\n }\n\n return explicitCredentials;\n }\n\n private async buildS3Client(\n credsManager: AwsCredentialsManager,\n region: string,\n integration: AwsS3Integration,\n ): Promise<S3Client> {\n const credentials = await AwsS3UrlReader.buildCredentials(\n credsManager,\n region,\n integration,\n );\n\n const s3 = new S3Client({\n customUserAgent: 'backstage-aws-s3-url-reader',\n region: region,\n credentials: credentials,\n endpoint: integration.config.endpoint,\n forcePathStyle: integration.config.s3ForcePathStyle,\n });\n return s3;\n }\n\n private async retrieveS3ObjectData(stream: Readable): Promise<Readable> {\n return new Promise((resolve, reject) => {\n try {\n const chunks: any[] = [];\n stream.on('data', chunk => chunks.push(chunk));\n stream.on('error', (e: Error) =>\n reject(new ForwardedError('Unable to read stream', e)),\n );\n stream.on('end', () => resolve(Readable.from(Buffer.concat(chunks))));\n } catch (e) {\n throw new ForwardedError('Unable to parse the response data', e);\n }\n });\n }\n\n async read(url: string): Promise<Buffer> {\n const response = await this.readUrl(url);\n return response.buffer();\n }\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n const { etag, lastModifiedAfter } = options ?? {};\n\n try {\n const { path, bucket, region } = parseUrl(url, this.integration.config);\n const s3Client = await this.buildS3Client(\n this.credsManager,\n region,\n this.integration,\n );\n const abortController = new AbortController();\n\n const params: GetObjectCommandInput = {\n Bucket: bucket,\n Key: path,\n ...(etag && { IfNoneMatch: etag }),\n ...(lastModifiedAfter && {\n IfModifiedSince: lastModifiedAfter,\n }),\n };\n\n options?.signal?.addEventListener('abort', () => abortController.abort());\n const getObjectCommand = new GetObjectCommand(params);\n const response = await s3Client.send(getObjectCommand, {\n abortSignal: abortController.signal,\n });\n\n const s3ObjectData = await this.retrieveS3ObjectData(\n response.Body as Readable,\n );\n\n return ReadUrlResponseFactory.fromReadable(s3ObjectData, {\n etag: response.ETag,\n lastModifiedAt: response.LastModified,\n });\n } catch (e) {\n if (e.$metadata && e.$metadata.httpStatusCode === 304) {\n throw new NotModifiedError();\n }\n\n throw new ForwardedError('Could not retrieve file from S3', e);\n }\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n try {\n const { path, bucket, region } = parseUrl(url, this.integration.config);\n const s3Client = await this.buildS3Client(\n this.credsManager,\n region,\n this.integration,\n );\n const abortController = new AbortController();\n const allObjects: String[] = [];\n const responses = [];\n let continuationToken: string | undefined;\n let output: ListObjectsV2CommandOutput;\n do {\n const listObjectsV2Command = new ListObjectsV2Command({\n Bucket: bucket,\n ContinuationToken: continuationToken,\n Prefix: path,\n });\n options?.signal?.addEventListener('abort', () =>\n abortController.abort(),\n );\n output = await s3Client.send(listObjectsV2Command, {\n abortSignal: abortController.signal,\n });\n if (output.Contents) {\n output.Contents.forEach(contents => {\n allObjects.push(contents.Key!);\n });\n }\n continuationToken = output.NextContinuationToken;\n } while (continuationToken);\n\n for (let i = 0; i < allObjects.length; i++) {\n const getObjectCommand = new GetObjectCommand({\n Bucket: bucket,\n Key: String(allObjects[i]),\n });\n const response = await s3Client.send(getObjectCommand);\n const s3ObjectData = await this.retrieveS3ObjectData(\n response.Body as Readable,\n );\n\n responses.push({\n data: s3ObjectData,\n path: relative(path, String(allObjects[i])),\n lastModifiedAt: response?.LastModified ?? undefined,\n });\n }\n\n return await this.deps.treeResponseFactory.fromReadableArray(responses);\n } catch (e) {\n throw new ForwardedError('Could not retrieve file tree from S3', e);\n }\n }\n\n async search(): Promise<UrlReaderServiceSearchResponse> {\n throw new Error('AwsS3Reader does not implement search');\n }\n\n toString() {\n const secretAccessKey = this.integration.config.secretAccessKey;\n return `awsS3{host=${this.integration.config.host},authed=${Boolean(\n secretAccessKey,\n )}}`;\n }\n}\n"],"names":["ScmIntegrations","DefaultAwsCredentialsManager","fromTemporaryCredentials","S3Client","stream","ForwardedError","Readable","abortController","AbortController","GetObjectCommand","ReadUrlResponseFactory","NotModifiedError","ListObjectsV2Command","relative"],"mappings":"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"}
1
+ {"version":3,"file":"AwsS3UrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/AwsS3UrlReader.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 {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchOptions,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\nimport { ReaderFactory, ReadTreeResponseFactory } from './types';\nimport {\n AwsCredentialsManager,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport {\n AwsS3Integration,\n ScmIntegrations,\n AwsS3IntegrationConfig,\n} from '@backstage/integration';\nimport {\n assertError,\n ForwardedError,\n NotModifiedError,\n} from '@backstage/errors';\nimport { fromTemporaryCredentials } from '@aws-sdk/credential-providers';\nimport { AwsCredentialIdentityProvider } from '@aws-sdk/types';\nimport {\n S3Client,\n ListObjectsV2Command,\n ListObjectsV2CommandOutput,\n GetObjectCommand,\n GetObjectCommandInput,\n} from '@aws-sdk/client-s3';\nimport { AbortController } from '@aws-sdk/abort-controller';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport { Readable } from 'stream';\nimport { relative } from 'path/posix';\n\nexport const DEFAULT_REGION = 'us-east-1';\n\n/**\n * Path style URLs: https://s3.(region).amazonaws.com/(bucket)/(key)\n * The region can also be on the old form: https://s3-(region).amazonaws.com/(bucket)/(key)\n * Virtual hosted style URLs: https://(bucket).s3.(region).amazonaws.com/(key)\n * See https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#path-style-access\n */\nexport function parseUrl(\n url: string,\n config: AwsS3IntegrationConfig,\n): { path: string; bucket: string; region: string } {\n const parsedUrl = new URL(url);\n\n /**\n * Removes the leading '/' from the pathname to be processed\n * as a parameter by AWS S3 SDK getObject method.\n */\n const pathname = parsedUrl.pathname.substring(1);\n const host = parsedUrl.host;\n\n // Treat Amazon hosted separately because it has special region logic\n if (config.host === 'amazonaws.com' || config.host === 'amazonaws.com.cn') {\n const match = host.match(\n /^(?:([a-z0-9.-]+)\\.)?s3(?:[.-]([a-z0-9-]+))?\\.amazonaws\\.com(\\.cn)?$/,\n );\n if (!match) {\n throw new Error(`Invalid AWS S3 URL ${url}`);\n }\n\n const [, hostBucket, hostRegion] = match;\n\n if (config.s3ForcePathStyle || !hostBucket) {\n const slashIndex = pathname.indexOf('/');\n if (slashIndex < 0) {\n throw new Error(\n `Invalid path-style AWS S3 URL ${url}, does not contain bucket in the path`,\n );\n }\n\n return {\n path: pathname.substring(slashIndex + 1),\n bucket: pathname.substring(0, slashIndex),\n region: hostRegion ?? DEFAULT_REGION,\n };\n }\n\n return {\n path: pathname,\n bucket: hostBucket,\n region: hostRegion ?? DEFAULT_REGION,\n };\n }\n\n const usePathStyle =\n config.s3ForcePathStyle || host.length === config.host.length;\n\n if (usePathStyle) {\n const slashIndex = pathname.indexOf('/');\n if (slashIndex < 0) {\n throw new Error(\n `Invalid path-style AWS S3 URL ${url}, does not contain bucket in the path`,\n );\n }\n\n return {\n path: pathname.substring(slashIndex + 1),\n bucket: pathname.substring(0, slashIndex),\n region: DEFAULT_REGION,\n };\n }\n\n return {\n path: pathname,\n bucket: host.substring(0, host.length - config.host.length - 1),\n region: DEFAULT_REGION,\n };\n}\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for AWS S3 buckets.\n *\n * @public\n */\nexport class AwsS3UrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n const credsManager = DefaultAwsCredentialsManager.fromConfig(config);\n\n return integrations.awsS3.list().map(integration => {\n const reader = new AwsS3UrlReader(credsManager, integration, {\n treeResponseFactory,\n });\n const predicate = (url: URL) =>\n url.host.endsWith(integration.config.host);\n return { reader, predicate };\n });\n };\n\n constructor(\n private readonly credsManager: AwsCredentialsManager,\n private readonly integration: AwsS3Integration,\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {}\n\n /**\n * If accessKeyId and secretAccessKey are missing, the standard credentials provider chain will be used:\n * https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html\n */\n private static buildStaticCredentials(\n accessKeyId: string,\n secretAccessKey: string,\n ): AwsCredentialIdentityProvider {\n return async () => {\n return {\n accessKeyId,\n secretAccessKey,\n };\n };\n }\n\n private static async buildCredentials(\n credsManager: AwsCredentialsManager,\n region: string,\n integration?: AwsS3Integration,\n ): Promise<AwsCredentialIdentityProvider> {\n // Fall back to the default credential chain if neither account ID\n // nor explicit credentials are provided\n if (!integration) {\n return (await credsManager.getCredentialProvider()).sdkCredentialProvider;\n }\n\n const accessKeyId = integration.config.accessKeyId;\n const secretAccessKey = integration.config.secretAccessKey;\n let explicitCredentials: AwsCredentialIdentityProvider;\n if (accessKeyId && secretAccessKey) {\n explicitCredentials = AwsS3UrlReader.buildStaticCredentials(\n accessKeyId,\n secretAccessKey,\n );\n } else {\n explicitCredentials = (await credsManager.getCredentialProvider())\n .sdkCredentialProvider;\n }\n\n const roleArn = integration.config.roleArn;\n if (roleArn) {\n return fromTemporaryCredentials({\n masterCredentials: explicitCredentials,\n params: {\n RoleSessionName: 'backstage-aws-s3-url-reader',\n RoleArn: roleArn,\n ExternalId: integration.config.externalId,\n },\n clientConfig: { region },\n });\n }\n\n return explicitCredentials;\n }\n\n private async buildS3Client(\n credsManager: AwsCredentialsManager,\n region: string,\n integration: AwsS3Integration,\n ): Promise<S3Client> {\n const credentials = await AwsS3UrlReader.buildCredentials(\n credsManager,\n region,\n integration,\n );\n\n const s3 = new S3Client({\n customUserAgent: 'backstage-aws-s3-url-reader',\n region: region,\n credentials: credentials,\n endpoint: integration.config.endpoint,\n forcePathStyle: integration.config.s3ForcePathStyle,\n });\n return s3;\n }\n\n private async retrieveS3ObjectData(stream: Readable): Promise<Readable> {\n return new Promise((resolve, reject) => {\n try {\n const chunks: any[] = [];\n stream.on('data', chunk => chunks.push(chunk));\n stream.on('error', (e: Error) =>\n reject(new ForwardedError('Unable to read stream', e)),\n );\n stream.on('end', () => resolve(Readable.from(Buffer.concat(chunks))));\n } catch (e) {\n throw new ForwardedError('Unable to parse the response data', e);\n }\n });\n }\n\n async read(url: string): Promise<Buffer> {\n const response = await this.readUrl(url);\n return response.buffer();\n }\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n const { etag, lastModifiedAfter } = options ?? {};\n\n try {\n const { path, bucket, region } = parseUrl(url, this.integration.config);\n const s3Client = await this.buildS3Client(\n this.credsManager,\n region,\n this.integration,\n );\n const abortController = new AbortController();\n\n const params: GetObjectCommandInput = {\n Bucket: bucket,\n Key: path,\n ...(etag && { IfNoneMatch: etag }),\n ...(lastModifiedAfter && {\n IfModifiedSince: lastModifiedAfter,\n }),\n };\n\n options?.signal?.addEventListener('abort', () => abortController.abort());\n const getObjectCommand = new GetObjectCommand(params);\n const response = await s3Client.send(getObjectCommand, {\n abortSignal: abortController.signal,\n });\n\n const s3ObjectData = await this.retrieveS3ObjectData(\n response.Body as Readable,\n );\n\n return ReadUrlResponseFactory.fromReadable(s3ObjectData, {\n etag: response.ETag,\n lastModifiedAt: response.LastModified,\n });\n } catch (e) {\n if (e.$metadata && e.$metadata.httpStatusCode === 304) {\n throw new NotModifiedError();\n }\n\n throw new ForwardedError('Could not retrieve file from S3', e);\n }\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n try {\n const { path, bucket, region } = parseUrl(url, this.integration.config);\n const s3Client = await this.buildS3Client(\n this.credsManager,\n region,\n this.integration,\n );\n const abortController = new AbortController();\n const allObjects: String[] = [];\n const responses = [];\n let continuationToken: string | undefined;\n let output: ListObjectsV2CommandOutput;\n do {\n const listObjectsV2Command = new ListObjectsV2Command({\n Bucket: bucket,\n ContinuationToken: continuationToken,\n Prefix: path,\n });\n options?.signal?.addEventListener('abort', () =>\n abortController.abort(),\n );\n output = await s3Client.send(listObjectsV2Command, {\n abortSignal: abortController.signal,\n });\n if (output.Contents) {\n output.Contents.forEach(contents => {\n allObjects.push(contents.Key!);\n });\n }\n continuationToken = output.NextContinuationToken;\n } while (continuationToken);\n\n for (let i = 0; i < allObjects.length; i++) {\n const getObjectCommand = new GetObjectCommand({\n Bucket: bucket,\n Key: String(allObjects[i]),\n });\n const response = await s3Client.send(getObjectCommand);\n const s3ObjectData = await this.retrieveS3ObjectData(\n response.Body as Readable,\n );\n\n responses.push({\n data: s3ObjectData,\n path: relative(path, String(allObjects[i])),\n lastModifiedAt: response?.LastModified ?? undefined,\n });\n }\n\n return await this.deps.treeResponseFactory.fromReadableArray(responses);\n } catch (e) {\n throw new ForwardedError('Could not retrieve file tree from S3', e);\n }\n }\n\n async search(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n const { path } = parseUrl(url, this.integration.config);\n\n if (path.match(/[*?]/)) {\n throw new Error('Unsupported search pattern URL');\n }\n\n try {\n const data = await this.readUrl(url, options);\n\n return {\n files: [\n {\n url: url,\n content: data.buffer,\n lastModifiedAt: data.lastModifiedAt,\n },\n ],\n etag: data.etag ?? '',\n };\n } catch (error) {\n assertError(error);\n if (error.name === 'NotFoundError') {\n return {\n files: [],\n etag: '',\n };\n }\n throw error;\n }\n }\n\n toString() {\n const secretAccessKey = this.integration.config.secretAccessKey;\n return `awsS3{host=${this.integration.config.host},authed=${Boolean(\n secretAccessKey,\n )}}`;\n }\n}\n"],"names":["ScmIntegrations","DefaultAwsCredentialsManager","fromTemporaryCredentials","S3Client","stream","ForwardedError","Readable","abortController","AbortController","GetObjectCommand","ReadUrlResponseFactory","NotModifiedError","ListObjectsV2Command","relative","assertError"],"mappings":"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"}