@red-hat-developer-hub/backstage-plugin-bulk-import-backend 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/catalog/catalogHttpClient.cjs.js +11 -6
  3. package/dist/catalog/catalogHttpClient.cjs.js.map +1 -1
  4. package/dist/catalog/catalogInfoGenerator.cjs.js.map +1 -1
  5. package/dist/catalog/catalogUtils.cjs.js.map +1 -1
  6. package/dist/generated/openapidocument.cjs.js +29 -2
  7. package/dist/generated/openapidocument.cjs.js.map +1 -1
  8. package/dist/github/GithubAppManager.cjs.js.map +1 -1
  9. package/dist/github/githubApiService.cjs.js.map +1 -1
  10. package/dist/github/types.cjs.js.map +1 -1
  11. package/dist/github/utils/ghUtils.cjs.js.map +1 -1
  12. package/dist/github/utils/orgUtils.cjs.js.map +1 -1
  13. package/dist/github/utils/prUtils.cjs.js.map +1 -1
  14. package/dist/github/utils/repoUtils.cjs.js.map +1 -1
  15. package/dist/github/utils/utils.cjs.js.map +1 -1
  16. package/dist/helpers/auditLogUtils.cjs.js.map +1 -1
  17. package/dist/helpers/auth.cjs.js.map +1 -1
  18. package/dist/helpers/loggingUtils.cjs.js.map +1 -1
  19. package/dist/helpers/pagination.cjs.js.map +1 -1
  20. package/dist/index.d.ts +9 -0
  21. package/dist/plugin.cjs.js.map +1 -1
  22. package/dist/service/handlers/handlers.cjs.js.map +1 -1
  23. package/dist/service/handlers/import/bulkImports.cjs.js +14 -5
  24. package/dist/service/handlers/import/bulkImports.cjs.js.map +1 -1
  25. package/dist/service/handlers/import/importStatus.cjs.js.map +1 -1
  26. package/dist/service/handlers/organization/organizations.cjs.js.map +1 -1
  27. package/dist/service/handlers/ping/ping.cjs.js.map +1 -1
  28. package/dist/service/handlers/repository/repositories.cjs.js +1 -1
  29. package/dist/service/handlers/repository/repositories.cjs.js.map +1 -1
  30. package/dist/service/router.cjs.js.map +1 -1
  31. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"repoUtils.cjs.js","sources":["../../../src/github/utils/repoUtils.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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport type {\n GithubCredentials,\n GithubIntegrationConfig,\n ScmIntegrations,\n} from '@backstage/integration';\n\nimport { Octokit } from '@octokit/rest';\nimport gitUrlParse from 'git-url-parse';\n\nimport { getBranchName } from '../../catalog/catalogUtils';\nimport { logErrorIfNeeded } from '../../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n} from '../../service/handlers/handlers';\nimport type { CustomGithubCredentialsProvider } from '../GithubAppManager';\nimport type {\n ExtendedGithubCredentials,\n GithubAppCredentials,\n GithubFetchError,\n GithubRepository,\n} from '../types';\nimport { getAllAppOrgs } from './orgUtils';\nimport {\n computeTotalCountFromGitHubToken,\n createCredentialError,\n handleError,\n} from './utils';\n\nexport type ValidatedRepo = {\n ghConfig: GithubIntegrationConfig;\n credentials: ExtendedGithubCredentials[];\n owner: string;\n repo: string;\n branchName: string;\n};\n\nexport async function validateAndBuildRepoData(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n integrations: ScmIntegrations,\n config: Config,\n input: {\n repoUrl: string;\n },\n): Promise<ValidatedRepo> {\n const ghConfig = integrations.github.byUrl(input.repoUrl)?.config;\n if (!ghConfig) {\n throw new Error(`Could not find GH integration from ${input.repoUrl}`);\n }\n\n const gitUrl = gitUrlParse(input.repoUrl);\n const owner = gitUrl.organization;\n const repo = gitUrl.name;\n\n const credentials = await githubCredentialsProvider.getAllCredentials({\n host: ghConfig.host,\n });\n if (credentials.length === 0) {\n throw new Error(`No credentials for GH integration`);\n }\n\n const branchName = getBranchName(config);\n return { ghConfig, owner, repo, credentials, branchName };\n}\n\nexport async function searchRepos(\n octokit: Octokit,\n ghSearchQuery: string,\n pageNumber: number = DefaultPageNumber,\n pageSize: number = DefaultPageSize,\n): Promise<{ totalCount?: number; repositories: GithubRepository[] }> {\n const repoSearchResp = await octokit.rest.search.repos({\n q: ghSearchQuery,\n order: 'asc',\n page: pageNumber,\n per_page: pageSize,\n });\n return {\n totalCount: repoSearchResp?.data?.total_count,\n repositories:\n repoSearchResp?.data?.items?.map(repo => {\n return {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch,\n updated_at: repo.updated_at,\n };\n }) ?? [],\n };\n}\n\n/**\n * Adds the repositories accessible by the provided github app to the provided repositories Map<string, GithubRepository>\n * If any errors occurs, adds them to the provided errors Map<number, GithubFetchError>\n */\nexport async function addGithubAppRepositories(\n deps: {\n logger: LoggerService;\n githubCredentialsProvider: CustomGithubCredentialsProvider;\n },\n octokit: Octokit,\n credential: GithubAppCredentials,\n ghConfig: GithubIntegrationConfig,\n repositories: Map<string, GithubRepository>,\n errors: Map<number, GithubFetchError>,\n reqParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<{ totalCount?: number }> {\n const search = reqParams?.search;\n const pageNumber = reqParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = reqParams?.pageSize ?? DefaultPageSize;\n let totalCount: number | undefined;\n try {\n if (search) {\n const allOrgsMap = await getAllAppOrgs(\n deps.githubCredentialsProvider,\n ghConfig,\n credential.accountLogin,\n );\n const orgSearch: string[] = [];\n for (const [_orgUrl, ghOrg] of allOrgsMap) {\n orgSearch.push(`org:${ghOrg.name}`);\n }\n const query = `${search} in:name ${orgSearch.join(' ')}`;\n const searchResp = await searchRepos(\n octokit,\n query,\n pageNumber,\n pageSize,\n );\n totalCount = searchResp.totalCount;\n searchResp.repositories.forEach(repo =>\n repositories.set(repo.full_name, repo),\n );\n } else {\n const resp = await octokit.apps.listReposAccessibleToInstallation({\n page: pageNumber,\n per_page: pageSize,\n });\n const repos = resp?.data?.repositories ?? resp?.data;\n repos?.forEach(repo => {\n repositories.set(repo.full_name, {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch,\n updated_at: repo.updated_at,\n });\n });\n totalCount = resp?.data?.total_count;\n }\n } catch (err: any) {\n logErrorIfNeeded(\n deps.logger,\n `Fetching repositories with access token for github app ${credential.appId}`,\n err,\n );\n const credentialError = createCredentialError(credential, err as Error);\n if (credentialError) {\n errors.set(credential.appId, credentialError);\n }\n }\n return { totalCount };\n}\n\n/**\n * Adds the user or organization repositories accessible by the github token to the provided repositories Map<string, GithubRepository> if they're owned by the specified owner\n * If any errors occurs, adds them to the provided errors Map<number, GithubFetchError>\n */\nexport async function addGithubTokenRepositories(\n deps: {\n logger: LoggerService;\n },\n octokit: Octokit,\n credential: GithubCredentials,\n repositories: Map<string, GithubRepository>,\n errors: Map<number, GithubFetchError>,\n reqParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<{ totalCount?: number }> {\n const search = reqParams?.search;\n const pageNumber = reqParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = reqParams?.pageSize ?? DefaultPageSize;\n let totalCount: number | undefined;\n try {\n if (search) {\n // Get currently authenticated user\n const username = (await octokit.rest.users.getAuthenticated())?.data\n ?.login;\n let query = `${search} in:name user:${username}`;\n\n const allOrgsResp = await octokit.paginate(\n octokit.rest.orgs.listForAuthenticatedUser,\n {\n sort: 'full_name',\n direction: 'asc',\n },\n );\n const orgSearch: string[] = [];\n allOrgsResp?.forEach(org => orgSearch.push(`org:${org.login}`));\n if (orgSearch.length > 0) {\n query += ` ${orgSearch.join(' ')}`;\n }\n\n const searchResp = await searchRepos(\n octokit,\n query,\n pageNumber,\n pageSize,\n );\n totalCount = searchResp.totalCount;\n searchResp.repositories.forEach(repo =>\n repositories.set(repo.full_name, repo),\n );\n } else {\n /**\n * The listForAuthenticatedUser endpoint will grab all the repositories the github token has explicit access to.\n * These would include repositories they own, repositories where they are a collaborator,\n * and repositories that they can access through an organization membership.\n */\n const resp = await octokit.rest.repos.listForAuthenticatedUser({\n page: pageNumber,\n per_page: pageSize,\n sort: 'full_name',\n direction: 'asc',\n });\n resp?.data?.forEach(repo => {\n repositories.set(repo.full_name, {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch,\n updated_at: repo.updated_at,\n });\n });\n\n totalCount = await computeTotalCountFromGitHubToken(\n deps,\n async (lastPageNumber: number) =>\n octokit.repos\n .listForAuthenticatedUser({\n page: lastPageNumber,\n per_page: 100,\n })\n .then(lastPageResp => lastPageResp.data.length),\n 'repos.listForAuthenticatedUser',\n resp?.data?.length,\n resp?.headers?.link,\n );\n }\n } catch (err) {\n handleError(\n deps,\n 'Fetching repositories with token from token',\n credential,\n errors,\n err,\n );\n }\n return { totalCount };\n}\n\nexport async function addGithubTokenOrgRepositories(\n deps: {\n logger: LoggerService;\n },\n octokit: Octokit,\n credential: GithubCredentials,\n org: string,\n repositories: Map<string, GithubRepository>,\n errors: Map<number, GithubFetchError>,\n reqParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<{ totalCount?: number }> {\n const search = reqParams?.search;\n const pageNumber = reqParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = reqParams?.pageSize ?? DefaultPageSize;\n let totalCount: number | undefined;\n try {\n if (search) {\n const query = `${search} in:name org:${org}`;\n const searchResp = await searchRepos(\n octokit,\n query,\n pageNumber,\n pageSize,\n );\n totalCount = searchResp.totalCount;\n searchResp.repositories.forEach(repo =>\n repositories.set(repo.full_name, repo),\n );\n } else {\n /**\n * The listForAuthenticatedUser endpoint will grab all the repositories the github token has explicit access to.\n * These would include repositories they own, repositories where they are a collaborator,\n * and repositories that they can access through an organization membership.\n */\n const resp = await octokit.rest.repos.listForOrg({\n org,\n page: pageNumber,\n per_page: pageSize,\n sort: 'full_name',\n direction: 'asc',\n });\n resp?.data?.forEach(repo => {\n const githubRepo: GithubRepository = {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch ?? 'main',\n updated_at: repo.updated_at,\n };\n repositories.set(githubRepo.full_name, githubRepo);\n });\n\n totalCount = await computeTotalCountFromGitHubToken(\n deps,\n async (lastPageNumber: number) =>\n octokit.repos\n .listForOrg({\n org,\n page: lastPageNumber,\n per_page: 100,\n })\n .then(lastPageResp => lastPageResp.data.length),\n 'repos.listForOrg',\n resp?.data?.length,\n resp?.headers?.link,\n );\n }\n } catch (err) {\n handleError(\n deps,\n 'Fetching org repositories with token from token',\n credential,\n errors,\n err,\n );\n }\n return { totalCount };\n}\n\nexport async function fileExistsInDefaultBranch(\n logger: LoggerService,\n octo: Octokit,\n owner: string,\n repo: string,\n fileName: string,\n defaultBranch: string = 'main',\n) {\n try {\n await octo.rest.repos.getContent({\n owner,\n repo,\n path: fileName,\n ref: defaultBranch,\n });\n return true;\n } catch (error: any) {\n if (error.status === 404) {\n return false;\n }\n logger.debug(\n `Unable to determine if a file named ${fileName} already exists in repo ${repo}: ${error}`,\n );\n return undefined;\n }\n}\n\nexport async function createOrUpdateFileInBranch(\n octo: Octokit,\n owner: string,\n repo: string,\n branchName: string,\n fileName: string,\n fileContent: string,\n): Promise<void> {\n try {\n const { data: existingFile } = await octo.rest.repos.getContent({\n owner: owner,\n repo: repo,\n path: fileName,\n ref: branchName,\n });\n // Response can either be a directory (array of files) or a single file element. In this case, we ensure it has the sha property to update it.\n if (Array.isArray(existingFile) || !('sha' in existingFile)) {\n throw new Error(\n `The content at path ${fileName} is not a file or the response from GitHub does not contain the 'sha' property.`,\n );\n }\n // If the file already exists, update it\n await octo.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: fileName,\n message: `Add ${fileName} config file`,\n content: btoa(fileContent),\n sha: existingFile.sha,\n branch: branchName,\n });\n } catch (error: any) {\n if (error.status === 404) {\n // If the file does not exist, create it\n await octo.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: fileName,\n message: `Add ${fileName} config file`,\n content: btoa(fileContent),\n branch: branchName,\n });\n } else {\n throw error;\n }\n }\n}\n"],"names":["gitUrlParse","getBranchName","DefaultPageNumber","DefaultPageSize","getAllAppOrgs","logErrorIfNeeded","createCredentialError","computeTotalCountFromGitHubToken","handleError"],"mappings":"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"}
1
+ {"version":3,"file":"repoUtils.cjs.js","sources":["../../../src/github/utils/repoUtils.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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport type {\n GithubCredentials,\n GithubIntegrationConfig,\n ScmIntegrations,\n} from '@backstage/integration';\n\nimport { Octokit } from '@octokit/rest';\nimport gitUrlParse from 'git-url-parse';\n\nimport { getBranchName } from '../../catalog/catalogUtils';\nimport { logErrorIfNeeded } from '../../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n} from '../../service/handlers/handlers';\nimport type { CustomGithubCredentialsProvider } from '../GithubAppManager';\nimport type {\n ExtendedGithubCredentials,\n GithubAppCredentials,\n GithubFetchError,\n GithubRepository,\n} from '../types';\nimport { getAllAppOrgs } from './orgUtils';\nimport {\n computeTotalCountFromGitHubToken,\n createCredentialError,\n handleError,\n} from './utils';\n\nexport type ValidatedRepo = {\n ghConfig: GithubIntegrationConfig;\n credentials: ExtendedGithubCredentials[];\n owner: string;\n repo: string;\n branchName: string;\n};\n\nexport async function validateAndBuildRepoData(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n integrations: ScmIntegrations,\n config: Config,\n input: {\n repoUrl: string;\n },\n): Promise<ValidatedRepo> {\n const ghConfig = integrations.github.byUrl(input.repoUrl)?.config;\n if (!ghConfig) {\n throw new Error(`Could not find GH integration from ${input.repoUrl}`);\n }\n\n const gitUrl = gitUrlParse(input.repoUrl);\n const owner = gitUrl.organization;\n const repo = gitUrl.name;\n\n const credentials = await githubCredentialsProvider.getAllCredentials({\n host: ghConfig.host,\n });\n if (credentials.length === 0) {\n throw new Error(`No credentials for GH integration`);\n }\n\n const branchName = getBranchName(config);\n return { ghConfig, owner, repo, credentials, branchName };\n}\n\nexport async function searchRepos(\n octokit: Octokit,\n ghSearchQuery: string,\n pageNumber: number = DefaultPageNumber,\n pageSize: number = DefaultPageSize,\n): Promise<{ totalCount?: number; repositories: GithubRepository[] }> {\n const repoSearchResp = await octokit.rest.search.repos({\n q: ghSearchQuery,\n order: 'asc',\n page: pageNumber,\n per_page: pageSize,\n });\n return {\n totalCount: repoSearchResp?.data?.total_count,\n repositories:\n repoSearchResp?.data?.items?.map(repo => {\n return {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch,\n updated_at: repo.updated_at,\n };\n }) ?? [],\n };\n}\n\n/**\n * Adds the repositories accessible by the provided github app to the provided repositories Map<string, GithubRepository>\n * If any errors occurs, adds them to the provided errors Map<number, GithubFetchError>\n */\nexport async function addGithubAppRepositories(\n deps: {\n logger: LoggerService;\n githubCredentialsProvider: CustomGithubCredentialsProvider;\n },\n octokit: Octokit,\n credential: GithubAppCredentials,\n ghConfig: GithubIntegrationConfig,\n repositories: Map<string, GithubRepository>,\n errors: Map<number, GithubFetchError>,\n reqParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<{ totalCount?: number }> {\n const search = reqParams?.search;\n const pageNumber = reqParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = reqParams?.pageSize ?? DefaultPageSize;\n let totalCount: number | undefined;\n try {\n if (search) {\n const allOrgsMap = await getAllAppOrgs(\n deps.githubCredentialsProvider,\n ghConfig,\n credential.accountLogin,\n );\n const orgSearch: string[] = [];\n for (const [_orgUrl, ghOrg] of allOrgsMap) {\n orgSearch.push(`org:${ghOrg.name}`);\n }\n const query = `${search} in:name ${orgSearch.join(' ')}`;\n const searchResp = await searchRepos(\n octokit,\n query,\n pageNumber,\n pageSize,\n );\n totalCount = searchResp.totalCount;\n searchResp.repositories.forEach(repo =>\n repositories.set(repo.full_name, repo),\n );\n } else {\n const resp = await octokit.apps.listReposAccessibleToInstallation({\n page: pageNumber,\n per_page: pageSize,\n });\n const repos = resp?.data?.repositories ?? resp?.data;\n repos?.forEach(repo => {\n repositories.set(repo.full_name, {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch,\n updated_at: repo.updated_at,\n });\n });\n totalCount = resp?.data?.total_count;\n }\n } catch (err: any) {\n logErrorIfNeeded(\n deps.logger,\n `Fetching repositories with access token for github app ${credential.appId}`,\n err,\n );\n const credentialError = createCredentialError(credential, err as Error);\n if (credentialError) {\n errors.set(credential.appId, credentialError);\n }\n }\n return { totalCount };\n}\n\n/**\n * Adds the user or organization repositories accessible by the github token to the provided repositories Map<string, GithubRepository> if they're owned by the specified owner\n * If any errors occurs, adds them to the provided errors Map<number, GithubFetchError>\n */\nexport async function addGithubTokenRepositories(\n deps: {\n logger: LoggerService;\n },\n octokit: Octokit,\n credential: GithubCredentials,\n repositories: Map<string, GithubRepository>,\n errors: Map<number, GithubFetchError>,\n reqParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<{ totalCount?: number }> {\n const search = reqParams?.search;\n const pageNumber = reqParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = reqParams?.pageSize ?? DefaultPageSize;\n let totalCount: number | undefined;\n try {\n if (search) {\n // Get currently authenticated user\n const username = (await octokit.rest.users.getAuthenticated())?.data\n ?.login;\n let query = `${search} in:name user:${username}`;\n\n const allOrgsResp = await octokit.paginate(\n octokit.rest.orgs.listForAuthenticatedUser,\n {\n sort: 'full_name',\n direction: 'asc',\n },\n );\n const orgSearch: string[] = [];\n allOrgsResp?.forEach(org => orgSearch.push(`org:${org.login}`));\n if (orgSearch.length > 0) {\n query += ` ${orgSearch.join(' ')}`;\n }\n\n const searchResp = await searchRepos(\n octokit,\n query,\n pageNumber,\n pageSize,\n );\n totalCount = searchResp.totalCount;\n searchResp.repositories.forEach(repo =>\n repositories.set(repo.full_name, repo),\n );\n } else {\n /**\n * The listForAuthenticatedUser endpoint will grab all the repositories the github token has explicit access to.\n * These would include repositories they own, repositories where they are a collaborator,\n * and repositories that they can access through an organization membership.\n */\n const resp = await octokit.rest.repos.listForAuthenticatedUser({\n page: pageNumber,\n per_page: pageSize,\n sort: 'full_name',\n direction: 'asc',\n });\n resp?.data?.forEach(repo => {\n repositories.set(repo.full_name, {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch,\n updated_at: repo.updated_at,\n });\n });\n\n totalCount = await computeTotalCountFromGitHubToken(\n deps,\n async (lastPageNumber: number) =>\n octokit.repos\n .listForAuthenticatedUser({\n page: lastPageNumber,\n per_page: 100,\n })\n .then(lastPageResp => lastPageResp.data.length),\n 'repos.listForAuthenticatedUser',\n resp?.data?.length,\n resp?.headers?.link,\n );\n }\n } catch (err) {\n handleError(\n deps,\n 'Fetching repositories with token from token',\n credential,\n errors,\n err,\n );\n }\n return { totalCount };\n}\n\nexport async function addGithubTokenOrgRepositories(\n deps: {\n logger: LoggerService;\n },\n octokit: Octokit,\n credential: GithubCredentials,\n org: string,\n repositories: Map<string, GithubRepository>,\n errors: Map<number, GithubFetchError>,\n reqParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<{ totalCount?: number }> {\n const search = reqParams?.search;\n const pageNumber = reqParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = reqParams?.pageSize ?? DefaultPageSize;\n let totalCount: number | undefined;\n try {\n if (search) {\n const query = `${search} in:name org:${org}`;\n const searchResp = await searchRepos(\n octokit,\n query,\n pageNumber,\n pageSize,\n );\n totalCount = searchResp.totalCount;\n searchResp.repositories.forEach(repo =>\n repositories.set(repo.full_name, repo),\n );\n } else {\n /**\n * The listForAuthenticatedUser endpoint will grab all the repositories the github token has explicit access to.\n * These would include repositories they own, repositories where they are a collaborator,\n * and repositories that they can access through an organization membership.\n */\n const resp = await octokit.rest.repos.listForOrg({\n org,\n page: pageNumber,\n per_page: pageSize,\n sort: 'full_name',\n direction: 'asc',\n });\n resp?.data?.forEach(repo => {\n const githubRepo: GithubRepository = {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch ?? 'main',\n updated_at: repo.updated_at,\n };\n repositories.set(githubRepo.full_name, githubRepo);\n });\n\n totalCount = await computeTotalCountFromGitHubToken(\n deps,\n async (lastPageNumber: number) =>\n octokit.repos\n .listForOrg({\n org,\n page: lastPageNumber,\n per_page: 100,\n })\n .then(lastPageResp => lastPageResp.data.length),\n 'repos.listForOrg',\n resp?.data?.length,\n resp?.headers?.link,\n );\n }\n } catch (err) {\n handleError(\n deps,\n 'Fetching org repositories with token from token',\n credential,\n errors,\n err,\n );\n }\n return { totalCount };\n}\n\nexport async function fileExistsInDefaultBranch(\n logger: LoggerService,\n octo: Octokit,\n owner: string,\n repo: string,\n fileName: string,\n defaultBranch: string = 'main',\n) {\n try {\n await octo.rest.repos.getContent({\n owner,\n repo,\n path: fileName,\n ref: defaultBranch,\n });\n return true;\n } catch (error: any) {\n if (error.status === 404) {\n return false;\n }\n logger.debug(\n `Unable to determine if a file named ${fileName} already exists in repo ${repo}: ${error}`,\n );\n return undefined;\n }\n}\n\nexport async function createOrUpdateFileInBranch(\n octo: Octokit,\n owner: string,\n repo: string,\n branchName: string,\n fileName: string,\n fileContent: string,\n): Promise<void> {\n try {\n const { data: existingFile } = await octo.rest.repos.getContent({\n owner: owner,\n repo: repo,\n path: fileName,\n ref: branchName,\n });\n // Response can either be a directory (array of files) or a single file element. In this case, we ensure it has the sha property to update it.\n if (Array.isArray(existingFile) || !('sha' in existingFile)) {\n throw new Error(\n `The content at path ${fileName} is not a file or the response from GitHub does not contain the 'sha' property.`,\n );\n }\n // If the file already exists, update it\n await octo.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: fileName,\n message: `Add ${fileName} config file`,\n content: btoa(fileContent),\n sha: existingFile.sha,\n branch: branchName,\n });\n } catch (error: any) {\n if (error.status === 404) {\n // If the file does not exist, create it\n await octo.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: fileName,\n message: `Add ${fileName} config file`,\n content: btoa(fileContent),\n branch: branchName,\n });\n } else {\n throw error;\n }\n }\n}\n"],"names":["gitUrlParse","getBranchName","DefaultPageNumber","DefaultPageSize","getAllAppOrgs","logErrorIfNeeded","createCredentialError","computeTotalCountFromGitHubToken","handleError"],"mappings":";;;;;;;;;;;;;;;;AAuDA,eAAsB,wBACpB,CAAA,yBAAA,EACA,YACA,EAAA,MAAA,EACA,KAGwB,EAAA;AACxB,EAAA,MAAM,WAAW,YAAa,CAAA,MAAA,CAAO,KAAM,CAAA,KAAA,CAAM,OAAO,CAAG,EAAA,MAAA;AAC3D,EAAA,IAAI,CAAC,QAAU,EAAA;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAsC,mCAAA,EAAA,KAAA,CAAM,OAAO,CAAE,CAAA,CAAA;AAAA;AAGvE,EAAM,MAAA,MAAA,GAASA,4BAAY,CAAA,KAAA,CAAM,OAAO,CAAA;AACxC,EAAA,MAAM,QAAQ,MAAO,CAAA,YAAA;AACrB,EAAA,MAAM,OAAO,MAAO,CAAA,IAAA;AAEpB,EAAM,MAAA,WAAA,GAAc,MAAM,yBAAA,CAA0B,iBAAkB,CAAA;AAAA,IACpE,MAAM,QAAS,CAAA;AAAA,GAChB,CAAA;AACD,EAAI,IAAA,WAAA,CAAY,WAAW,CAAG,EAAA;AAC5B,IAAM,MAAA,IAAI,MAAM,CAAmC,iCAAA,CAAA,CAAA;AAAA;AAGrD,EAAM,MAAA,UAAA,GAAaC,2BAAc,MAAM,CAAA;AACvC,EAAA,OAAO,EAAE,QAAA,EAAU,KAAO,EAAA,IAAA,EAAM,aAAa,UAAW,EAAA;AAC1D;AAEA,eAAsB,YACpB,OACA,EAAA,aAAA,EACA,UAAqB,GAAAC,0BAAA,EACrB,WAAmBC,wBACiD,EAAA;AACpE,EAAA,MAAM,cAAiB,GAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,OAAO,KAAM,CAAA;AAAA,IACrD,CAAG,EAAA,aAAA;AAAA,IACH,KAAO,EAAA,KAAA;AAAA,IACP,IAAM,EAAA,UAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACX,CAAA;AACD,EAAO,OAAA;AAAA,IACL,UAAA,EAAY,gBAAgB,IAAM,EAAA,WAAA;AAAA,IAClC,YACE,EAAA,cAAA,EAAgB,IAAM,EAAA,KAAA,EAAO,IAAI,CAAQ,IAAA,KAAA;AACvC,MAAO,OAAA;AAAA,QACL,MAAM,IAAK,CAAA,IAAA;AAAA,QACX,WAAW,IAAK,CAAA,SAAA;AAAA,QAChB,KAAK,IAAK,CAAA,GAAA;AAAA,QACV,UAAU,IAAK,CAAA,QAAA;AAAA,QACf,gBAAgB,IAAK,CAAA,cAAA;AAAA,QACrB,YAAY,IAAK,CAAA;AAAA,OACnB;AAAA,KACD,KAAK;AAAC,GACX;AACF;AAMA,eAAsB,yBACpB,IAIA,EAAA,OAAA,EACA,YACA,QACA,EAAA,YAAA,EACA,QACA,SAKkC,EAAA;AAClC,EAAA,MAAM,SAAS,SAAW,EAAA,MAAA;AAC1B,EAAM,MAAA,UAAA,GAAa,WAAW,UAAc,IAAAD,0BAAA;AAC5C,EAAM,MAAA,QAAA,GAAW,WAAW,QAAY,IAAAC,wBAAA;AACxC,EAAI,IAAA,UAAA;AACJ,EAAI,IAAA;AACF,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,MAAM,aAAa,MAAMC,sBAAA;AAAA,QACvB,IAAK,CAAA,yBAAA;AAAA,QACL,QAAA;AAAA,QACA,UAAW,CAAA;AAAA,OACb;AACA,MAAA,MAAM,YAAsB,EAAC;AAC7B,MAAA,KAAA,MAAW,CAAC,OAAA,EAAS,KAAK,CAAA,IAAK,UAAY,EAAA;AACzC,QAAA,SAAA,CAAU,IAAK,CAAA,CAAA,IAAA,EAAO,KAAM,CAAA,IAAI,CAAE,CAAA,CAAA;AAAA;AAEpC,MAAA,MAAM,QAAQ,CAAG,EAAA,MAAM,YAAY,SAAU,CAAA,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AACtD,MAAA,MAAM,aAAa,MAAM,WAAA;AAAA,QACvB,OAAA;AAAA,QACA,KAAA;AAAA,QACA,UAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,UAAA,GAAa,UAAW,CAAA,UAAA;AACxB,MAAA,UAAA,CAAW,YAAa,CAAA,OAAA;AAAA,QAAQ,CAC9B,IAAA,KAAA,YAAA,CAAa,GAAI,CAAA,IAAA,CAAK,WAAW,IAAI;AAAA,OACvC;AAAA,KACK,MAAA;AACL,MAAA,MAAM,IAAO,GAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,iCAAkC,CAAA;AAAA,QAChE,IAAM,EAAA,UAAA;AAAA,QACN,QAAU,EAAA;AAAA,OACX,CAAA;AACD,MAAA,MAAM,KAAQ,GAAA,IAAA,EAAM,IAAM,EAAA,YAAA,IAAgB,IAAM,EAAA,IAAA;AAChD,MAAA,KAAA,EAAO,QAAQ,CAAQ,IAAA,KAAA;AACrB,QAAa,YAAA,CAAA,GAAA,CAAI,KAAK,SAAW,EAAA;AAAA,UAC/B,MAAM,IAAK,CAAA,IAAA;AAAA,UACX,WAAW,IAAK,CAAA,SAAA;AAAA,UAChB,KAAK,IAAK,CAAA,GAAA;AAAA,UACV,UAAU,IAAK,CAAA,QAAA;AAAA,UACf,gBAAgB,IAAK,CAAA,cAAA;AAAA,UACrB,YAAY,IAAK,CAAA;AAAA,SAClB,CAAA;AAAA,OACF,CAAA;AACD,MAAA,UAAA,GAAa,MAAM,IAAM,EAAA,WAAA;AAAA;AAC3B,WACO,GAAU,EAAA;AACjB,IAAAC,6BAAA;AAAA,MACE,IAAK,CAAA,MAAA;AAAA,MACL,CAAA,uDAAA,EAA0D,WAAW,KAAK,CAAA,CAAA;AAAA,MAC1E;AAAA,KACF;AACA,IAAM,MAAA,eAAA,GAAkBC,2BAAsB,CAAA,UAAA,EAAY,GAAY,CAAA;AACtE,IAAA,IAAI,eAAiB,EAAA;AACnB,MAAO,MAAA,CAAA,GAAA,CAAI,UAAW,CAAA,KAAA,EAAO,eAAe,CAAA;AAAA;AAC9C;AAEF,EAAA,OAAO,EAAE,UAAW,EAAA;AACtB;AAMA,eAAsB,2BACpB,IAGA,EAAA,OAAA,EACA,UACA,EAAA,YAAA,EACA,QACA,SAKkC,EAAA;AAClC,EAAA,MAAM,SAAS,SAAW,EAAA,MAAA;AAC1B,EAAM,MAAA,UAAA,GAAa,WAAW,UAAc,IAAAJ,0BAAA;AAC5C,EAAM,MAAA,QAAA,GAAW,WAAW,QAAY,IAAAC,wBAAA;AACxC,EAAI,IAAA,UAAA;AACJ,EAAI,IAAA;AACF,IAAA,IAAI,MAAQ,EAAA;AAEV,MAAA,MAAM,YAAY,MAAM,OAAA,CAAQ,KAAK,KAAM,CAAA,gBAAA,KAAqB,IAC5D,EAAA,KAAA;AACJ,MAAA,IAAI,KAAQ,GAAA,CAAA,EAAG,MAAM,CAAA,cAAA,EAAiB,QAAQ,CAAA,CAAA;AAE9C,MAAM,MAAA,WAAA,GAAc,MAAM,OAAQ,CAAA,QAAA;AAAA,QAChC,OAAA,CAAQ,KAAK,IAAK,CAAA,wBAAA;AAAA,QAClB;AAAA,UACE,IAAM,EAAA,WAAA;AAAA,UACN,SAAW,EAAA;AAAA;AACb,OACF;AACA,MAAA,MAAM,YAAsB,EAAC;AAC7B,MAAa,WAAA,EAAA,OAAA,CAAQ,SAAO,SAAU,CAAA,IAAA,CAAK,OAAO,GAAI,CAAA,KAAK,EAAE,CAAC,CAAA;AAC9D,MAAI,IAAA,SAAA,CAAU,SAAS,CAAG,EAAA;AACxB,QAAA,KAAA,IAAS,CAAI,CAAA,EAAA,SAAA,CAAU,IAAK,CAAA,GAAG,CAAC,CAAA,CAAA;AAAA;AAGlC,MAAA,MAAM,aAAa,MAAM,WAAA;AAAA,QACvB,OAAA;AAAA,QACA,KAAA;AAAA,QACA,UAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,UAAA,GAAa,UAAW,CAAA,UAAA;AACxB,MAAA,UAAA,CAAW,YAAa,CAAA,OAAA;AAAA,QAAQ,CAC9B,IAAA,KAAA,YAAA,CAAa,GAAI,CAAA,IAAA,CAAK,WAAW,IAAI;AAAA,OACvC;AAAA,KACK,MAAA;AAML,MAAA,MAAM,IAAO,GAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,MAAM,wBAAyB,CAAA;AAAA,QAC7D,IAAM,EAAA,UAAA;AAAA,QACN,QAAU,EAAA,QAAA;AAAA,QACV,IAAM,EAAA,WAAA;AAAA,QACN,SAAW,EAAA;AAAA,OACZ,CAAA;AACD,MAAM,IAAA,EAAA,IAAA,EAAM,QAAQ,CAAQ,IAAA,KAAA;AAC1B,QAAa,YAAA,CAAA,GAAA,CAAI,KAAK,SAAW,EAAA;AAAA,UAC/B,MAAM,IAAK,CAAA,IAAA;AAAA,UACX,WAAW,IAAK,CAAA,SAAA;AAAA,UAChB,KAAK,IAAK,CAAA,GAAA;AAAA,UACV,UAAU,IAAK,CAAA,QAAA;AAAA,UACf,gBAAgB,IAAK,CAAA,cAAA;AAAA,UACrB,YAAY,IAAK,CAAA;AAAA,SAClB,CAAA;AAAA,OACF,CAAA;AAED,MAAA,UAAA,GAAa,MAAMI,sCAAA;AAAA,QACjB,IAAA;AAAA,QACA,OAAO,cAAA,KACL,OAAQ,CAAA,KAAA,CACL,wBAAyB,CAAA;AAAA,UACxB,IAAM,EAAA,cAAA;AAAA,UACN,QAAU,EAAA;AAAA,SACX,CACA,CAAA,IAAA,CAAK,CAAgB,YAAA,KAAA,YAAA,CAAa,KAAK,MAAM,CAAA;AAAA,QAClD,gCAAA;AAAA,QACA,MAAM,IAAM,EAAA,MAAA;AAAA,QACZ,MAAM,OAAS,EAAA;AAAA,OACjB;AAAA;AACF,WACO,GAAK,EAAA;AACZ,IAAAC,iBAAA;AAAA,MACE,IAAA;AAAA,MACA,6CAAA;AAAA,MACA,UAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACF;AAAA;AAEF,EAAA,OAAO,EAAE,UAAW,EAAA;AACtB;AAEA,eAAsB,8BACpB,IAGA,EAAA,OAAA,EACA,YACA,GACA,EAAA,YAAA,EACA,QACA,SAKkC,EAAA;AAClC,EAAA,MAAM,SAAS,SAAW,EAAA,MAAA;AAC1B,EAAM,MAAA,UAAA,GAAa,WAAW,UAAc,IAAAN,0BAAA;AAC5C,EAAM,MAAA,QAAA,GAAW,WAAW,QAAY,IAAAC,wBAAA;AACxC,EAAI,IAAA,UAAA;AACJ,EAAI,IAAA;AACF,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,MAAM,KAAQ,GAAA,CAAA,EAAG,MAAM,CAAA,aAAA,EAAgB,GAAG,CAAA,CAAA;AAC1C,MAAA,MAAM,aAAa,MAAM,WAAA;AAAA,QACvB,OAAA;AAAA,QACA,KAAA;AAAA,QACA,UAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,UAAA,GAAa,UAAW,CAAA,UAAA;AACxB,MAAA,UAAA,CAAW,YAAa,CAAA,OAAA;AAAA,QAAQ,CAC9B,IAAA,KAAA,YAAA,CAAa,GAAI,CAAA,IAAA,CAAK,WAAW,IAAI;AAAA,OACvC;AAAA,KACK,MAAA;AAML,MAAA,MAAM,IAAO,GAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,MAAM,UAAW,CAAA;AAAA,QAC/C,GAAA;AAAA,QACA,IAAM,EAAA,UAAA;AAAA,QACN,QAAU,EAAA,QAAA;AAAA,QACV,IAAM,EAAA,WAAA;AAAA,QACN,SAAW,EAAA;AAAA,OACZ,CAAA;AACD,MAAM,IAAA,EAAA,IAAA,EAAM,QAAQ,CAAQ,IAAA,KAAA;AAC1B,QAAA,MAAM,UAA+B,GAAA;AAAA,UACnC,MAAM,IAAK,CAAA,IAAA;AAAA,UACX,WAAW,IAAK,CAAA,SAAA;AAAA,UAChB,KAAK,IAAK,CAAA,GAAA;AAAA,UACV,UAAU,IAAK,CAAA,QAAA;AAAA,UACf,cAAA,EAAgB,KAAK,cAAkB,IAAA,MAAA;AAAA,UACvC,YAAY,IAAK,CAAA;AAAA,SACnB;AACA,QAAa,YAAA,CAAA,GAAA,CAAI,UAAW,CAAA,SAAA,EAAW,UAAU,CAAA;AAAA,OAClD,CAAA;AAED,MAAA,UAAA,GAAa,MAAMI,sCAAA;AAAA,QACjB,IAAA;AAAA,QACA,OAAO,cAAA,KACL,OAAQ,CAAA,KAAA,CACL,UAAW,CAAA;AAAA,UACV,GAAA;AAAA,UACA,IAAM,EAAA,cAAA;AAAA,UACN,QAAU,EAAA;AAAA,SACX,CACA,CAAA,IAAA,CAAK,CAAgB,YAAA,KAAA,YAAA,CAAa,KAAK,MAAM,CAAA;AAAA,QAClD,kBAAA;AAAA,QACA,MAAM,IAAM,EAAA,MAAA;AAAA,QACZ,MAAM,OAAS,EAAA;AAAA,OACjB;AAAA;AACF,WACO,GAAK,EAAA;AACZ,IAAAC,iBAAA;AAAA,MACE,IAAA;AAAA,MACA,iDAAA;AAAA,MACA,UAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACF;AAAA;AAEF,EAAA,OAAO,EAAE,UAAW,EAAA;AACtB;AAEA,eAAsB,0BACpB,MACA,EAAA,IAAA,EACA,OACA,IACA,EAAA,QAAA,EACA,gBAAwB,MACxB,EAAA;AACA,EAAI,IAAA;AACF,IAAM,MAAA,IAAA,CAAK,IAAK,CAAA,KAAA,CAAM,UAAW,CAAA;AAAA,MAC/B,KAAA;AAAA,MACA,IAAA;AAAA,MACA,IAAM,EAAA,QAAA;AAAA,MACN,GAAK,EAAA;AAAA,KACN,CAAA;AACD,IAAO,OAAA,IAAA;AAAA,WACA,KAAY,EAAA;AACnB,IAAI,IAAA,KAAA,CAAM,WAAW,GAAK,EAAA;AACxB,MAAO,OAAA,KAAA;AAAA;AAET,IAAO,MAAA,CAAA,KAAA;AAAA,MACL,CAAuC,oCAAA,EAAA,QAAQ,CAA2B,wBAAA,EAAA,IAAI,KAAK,KAAK,CAAA;AAAA,KAC1F;AACA,IAAO,OAAA,KAAA,CAAA;AAAA;AAEX;AAEA,eAAsB,2BACpB,IACA,EAAA,KAAA,EACA,IACA,EAAA,UAAA,EACA,UACA,WACe,EAAA;AACf,EAAI,IAAA;AACF,IAAM,MAAA,EAAE,MAAM,YAAa,EAAA,GAAI,MAAM,IAAK,CAAA,IAAA,CAAK,MAAM,UAAW,CAAA;AAAA,MAC9D,KAAA;AAAA,MACA,IAAA;AAAA,MACA,IAAM,EAAA,QAAA;AAAA,MACN,GAAK,EAAA;AAAA,KACN,CAAA;AAED,IAAA,IAAI,MAAM,OAAQ,CAAA,YAAY,CAAK,IAAA,EAAE,SAAS,YAAe,CAAA,EAAA;AAC3D,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,uBAAuB,QAAQ,CAAA,+EAAA;AAAA,OACjC;AAAA;AAGF,IAAM,MAAA,IAAA,CAAK,IAAK,CAAA,KAAA,CAAM,0BAA2B,CAAA;AAAA,MAC/C,KAAA;AAAA,MACA,IAAA;AAAA,MACA,IAAM,EAAA,QAAA;AAAA,MACN,OAAA,EAAS,OAAO,QAAQ,CAAA,YAAA,CAAA;AAAA,MACxB,OAAA,EAAS,KAAK,WAAW,CAAA;AAAA,MACzB,KAAK,YAAa,CAAA,GAAA;AAAA,MAClB,MAAQ,EAAA;AAAA,KACT,CAAA;AAAA,WACM,KAAY,EAAA;AACnB,IAAI,IAAA,KAAA,CAAM,WAAW,GAAK,EAAA;AAExB,MAAM,MAAA,IAAA,CAAK,IAAK,CAAA,KAAA,CAAM,0BAA2B,CAAA;AAAA,QAC/C,KAAA;AAAA,QACA,IAAA;AAAA,QACA,IAAM,EAAA,QAAA;AAAA,QACN,OAAA,EAAS,OAAO,QAAQ,CAAA,YAAA,CAAA;AAAA,QACxB,OAAA,EAAS,KAAK,WAAW,CAAA;AAAA,QACzB,MAAQ,EAAA;AAAA,OACT,CAAA;AAAA,KACI,MAAA;AACL,MAAM,MAAA,KAAA;AAAA;AACR;AAEJ;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs.js","sources":["../../../src/github/utils/utils.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 type {\n CacheService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport {\n ScmIntegrations,\n type GithubIntegrationConfig,\n} from '@backstage/integration';\n\nimport { Octokit } from '@octokit/rest';\nimport gitUrlParse from 'git-url-parse';\n\nimport { logErrorIfNeeded } from '../../helpers';\nimport type { CustomGithubCredentialsProvider } from '../GithubAppManager';\nimport {\n isGithubAppCredential,\n type ExtendedGithubCredentials,\n type GithubFetchError,\n} from '../types';\nimport { buildOcto } from './ghUtils';\nimport { validateAndBuildRepoData, ValidatedRepo } from './repoUtils';\n\n/**\n * Creates the GithubFetchError to be stored in the returned errors array of the returned GithubRepositoryResponse object\n */\nexport function createCredentialError(\n credential: ExtendedGithubCredentials,\n err?: Error,\n): GithubFetchError | undefined {\n if (err) {\n if (isGithubAppCredential(credential)) {\n return {\n appId: credential.appId,\n type: 'app',\n error: {\n name: err.name,\n message: err.message,\n },\n };\n }\n return {\n type: 'token',\n error: {\n name: err.name,\n message: err.message,\n },\n };\n }\n if ('error' in credential) {\n return {\n appId: credential.appId,\n type: 'app',\n error: {\n name: credential.error.name,\n message: credential.error.message,\n },\n };\n }\n return undefined;\n}\n\nexport function verifyAndGetIntegrations(\n deps: {\n logger: LoggerService;\n },\n\n integrations: ScmIntegrations,\n) {\n const ghConfigs = integrations.github.list().map(ghInt => ghInt.config);\n if (ghConfigs.length === 0) {\n deps.logger.debug(\n 'No GitHub Integration in config => returning an empty list of repositories.',\n );\n throw new Error(\n \"Looks like there is no GitHub Integration in config. Please add a configuration entry under 'integrations.github\",\n );\n }\n return ghConfigs;\n}\n\nexport async function getCredentialsFromIntegrations(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n ghConfigs: GithubIntegrationConfig[],\n) {\n const credentialsByConfig = new Map<\n GithubIntegrationConfig,\n ExtendedGithubCredentials[]\n >();\n for (const ghConfig of ghConfigs) {\n const creds = await getCredentialsForConfig(\n githubCredentialsProvider,\n ghConfig,\n );\n credentialsByConfig.set(ghConfig, creds);\n }\n return credentialsByConfig;\n}\n\nexport async function getCredentialsForConfig(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n ghConfig: GithubIntegrationConfig,\n) {\n return await githubCredentialsProvider.getAllCredentials({\n host: ghConfig.host,\n });\n}\n\nexport async function extractConfigAndCreds(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n integrations: ScmIntegrations,\n input: {\n repoUrl: string;\n defaultBranch?: string;\n },\n) {\n const ghConfig = integrations.github.byUrl(input.repoUrl)?.config;\n if (!ghConfig) {\n throw new Error(`Could not find GH integration from ${input.repoUrl}`);\n }\n\n const credentials = await githubCredentialsProvider.getAllCredentials({\n host: ghConfig.host,\n });\n if (credentials.length === 0) {\n throw new Error(`No credentials for GH integration`);\n }\n\n const gitUrl = gitUrlParse(input.repoUrl);\n return { ghConfig, credentials, gitUrl };\n}\n\nexport function handleError(\n deps: {\n logger: LoggerService;\n },\n desc: string,\n credential: ExtendedGithubCredentials,\n errors: Map<number, GithubFetchError>,\n err: any,\n) {\n logErrorIfNeeded(deps.logger, `${desc} failed`, err);\n const credentialError = createCredentialError(credential, err as Error);\n if (credentialError) {\n errors.set(-1, credentialError);\n }\n}\n\nexport async function computeTotalCountFromGitHubToken(\n deps: {\n logger: LoggerService;\n },\n lastPageDataLengthProviderFn: (lastPageNumber: number) => Promise<number>,\n ghApiName: string,\n pageSize?: number,\n linkHeader?: string,\n): Promise<number | undefined> {\n // There is no direct way to get the total count of repositories other than using octokit.paginate,\n // but will make us retrieve all pages, thus increasing our response time.\n // Workaround here is to analyze the headers, and get the link to the last page.\n if (!linkHeader) {\n deps.logger.debug(\n `No link header found in response from ${ghApiName} GH endpoint => returning current page size`,\n );\n return pageSize;\n }\n const lastPageLink = linkHeader\n .split(',')\n .find(s => s.includes('rel=\"last\"'));\n if (!lastPageLink) {\n deps.logger.debug(\n `No rel='last' link found in response headers from ${ghApiName} GH endpoint => returning current page size`,\n );\n return pageSize;\n }\n const match = lastPageLink.match(/page=(\\d+)/);\n if (!match || match.length < 2) {\n deps.logger.debug(\n `Unable to extract page number from rel='last' link found in response headers from ${ghApiName} GH endpoint => returning current page size`,\n );\n return pageSize;\n }\n\n const lastPageNumber = parseInt(match[1], 10);\n // Fetch the last page to count its items, as it might contain fewer than the requested size\n const lastPageDataLength = await lastPageDataLengthProviderFn(lastPageNumber);\n return pageSize\n ? (lastPageNumber - 1) * pageSize + lastPageDataLength\n : undefined;\n}\n\nexport async function executeFunctionOnFirstSuccessfulIntegration<T>(\n deps: {\n logger: LoggerService;\n cache: CacheService;\n config: Config;\n githubCredentialsProvider: CustomGithubCredentialsProvider;\n },\n integrations: ScmIntegrations,\n params: {\n repoUrl: string;\n fn: (\n validatedRepo: ValidatedRepo,\n octo: Octokit,\n ) => Promise<{ successful: boolean; result?: T }>;\n },\n) {\n const validatedRepo = await validateAndBuildRepoData(\n deps.githubCredentialsProvider,\n integrations,\n deps.config,\n params,\n );\n for (const credential of validatedRepo.credentials) {\n const octo = buildOcto(\n {\n logger: deps.logger,\n cache: deps.cache,\n },\n { credential, owner: validatedRepo.owner },\n validatedRepo.ghConfig.apiBaseUrl,\n );\n if (!octo) {\n continue;\n }\n const res = await params.fn(validatedRepo, octo);\n if (!res.successful) {\n continue;\n }\n return res.result;\n }\n return undefined;\n}\n\nexport async function fetchFromAllIntegrations<T>(\n deps: {\n logger: LoggerService;\n cache: CacheService;\n githubCredentialsProvider: CustomGithubCredentialsProvider;\n },\n integrations: ScmIntegrations,\n params: {\n dataFetcher: (\n octo: Octokit,\n credential: ExtendedGithubCredentials,\n ghConfig: GithubIntegrationConfig,\n ) => Promise<{\n stopFetchingData?: boolean;\n result?: T;\n errors?: GithubFetchError[];\n }>;\n },\n) {\n const ghConfigs = verifyAndGetIntegrations(deps, integrations);\n\n const credentialsByConfig = await getCredentialsFromIntegrations(\n deps.githubCredentialsProvider,\n ghConfigs,\n );\n const errors = new Map<number, GithubFetchError>();\n const data: T[] = [];\n const dataErrs: GithubFetchError[] = [];\n let stopFetchingData: boolean | undefined = false;\n for (const [ghConfig, credentials] of credentialsByConfig) {\n if (stopFetchingData) {\n break;\n }\n deps.logger.debug(\n `Got ${credentials.length} credential(s) for ${ghConfig.host}`,\n );\n for (const credential of credentials) {\n const octokit = buildOcto(\n deps,\n { credential, errors },\n ghConfig.apiBaseUrl,\n );\n if (!octokit) {\n continue;\n }\n const res = await params.dataFetcher(octokit, credential, ghConfig);\n res.errors?.forEach(err => dataErrs.push(err));\n\n if (res.result) {\n data.push(res.result);\n }\n\n stopFetchingData = res.stopFetchingData;\n }\n }\n\n const aggregatedErrors = new Map<number, GithubFetchError>();\n errors.forEach((err, num) => aggregatedErrors.set(num, err));\n dataErrs.forEach((err, idx) => aggregatedErrors.set(idx, err));\n\n return { data, errors: aggregatedErrors };\n}\n\nexport function computeTotalCount<T>(\n data: T[],\n countList: number[],\n pageSize: number,\n) {\n let totalCount = countList.reduce(\n (accumulator, currentValue) => accumulator + currentValue,\n 0,\n );\n if (totalCount < pageSize) {\n totalCount = data.length;\n }\n return totalCount;\n}\n\nexport function extractLocationOwnerMap(locationUrls: string[]) {\n const locationGitOwnerMap = new Map<string, string>();\n for (const locationUrl of locationUrls) {\n const split = locationUrl.split('/blob/');\n if (split.length < 2) {\n continue;\n }\n locationGitOwnerMap.set(locationUrl, gitUrlParse(split[0]).owner);\n }\n return locationGitOwnerMap;\n}\n"],"names":["isGithubAppCredential","logErrorIfNeeded","validateAndBuildRepoData","buildOcto","gitUrlParse"],"mappings":"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"}
1
+ {"version":3,"file":"utils.cjs.js","sources":["../../../src/github/utils/utils.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 type {\n CacheService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport {\n ScmIntegrations,\n type GithubIntegrationConfig,\n} from '@backstage/integration';\n\nimport { Octokit } from '@octokit/rest';\nimport gitUrlParse from 'git-url-parse';\n\nimport { logErrorIfNeeded } from '../../helpers';\nimport type { CustomGithubCredentialsProvider } from '../GithubAppManager';\nimport {\n isGithubAppCredential,\n type ExtendedGithubCredentials,\n type GithubFetchError,\n} from '../types';\nimport { buildOcto } from './ghUtils';\nimport { validateAndBuildRepoData, ValidatedRepo } from './repoUtils';\n\n/**\n * Creates the GithubFetchError to be stored in the returned errors array of the returned GithubRepositoryResponse object\n */\nexport function createCredentialError(\n credential: ExtendedGithubCredentials,\n err?: Error,\n): GithubFetchError | undefined {\n if (err) {\n if (isGithubAppCredential(credential)) {\n return {\n appId: credential.appId,\n type: 'app',\n error: {\n name: err.name,\n message: err.message,\n },\n };\n }\n return {\n type: 'token',\n error: {\n name: err.name,\n message: err.message,\n },\n };\n }\n if ('error' in credential) {\n return {\n appId: credential.appId,\n type: 'app',\n error: {\n name: credential.error.name,\n message: credential.error.message,\n },\n };\n }\n return undefined;\n}\n\nexport function verifyAndGetIntegrations(\n deps: {\n logger: LoggerService;\n },\n\n integrations: ScmIntegrations,\n) {\n const ghConfigs = integrations.github.list().map(ghInt => ghInt.config);\n if (ghConfigs.length === 0) {\n deps.logger.debug(\n 'No GitHub Integration in config => returning an empty list of repositories.',\n );\n throw new Error(\n \"Looks like there is no GitHub Integration in config. Please add a configuration entry under 'integrations.github\",\n );\n }\n return ghConfigs;\n}\n\nexport async function getCredentialsFromIntegrations(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n ghConfigs: GithubIntegrationConfig[],\n) {\n const credentialsByConfig = new Map<\n GithubIntegrationConfig,\n ExtendedGithubCredentials[]\n >();\n for (const ghConfig of ghConfigs) {\n const creds = await getCredentialsForConfig(\n githubCredentialsProvider,\n ghConfig,\n );\n credentialsByConfig.set(ghConfig, creds);\n }\n return credentialsByConfig;\n}\n\nexport async function getCredentialsForConfig(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n ghConfig: GithubIntegrationConfig,\n) {\n return await githubCredentialsProvider.getAllCredentials({\n host: ghConfig.host,\n });\n}\n\nexport async function extractConfigAndCreds(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n integrations: ScmIntegrations,\n input: {\n repoUrl: string;\n defaultBranch?: string;\n },\n) {\n const ghConfig = integrations.github.byUrl(input.repoUrl)?.config;\n if (!ghConfig) {\n throw new Error(`Could not find GH integration from ${input.repoUrl}`);\n }\n\n const credentials = await githubCredentialsProvider.getAllCredentials({\n host: ghConfig.host,\n });\n if (credentials.length === 0) {\n throw new Error(`No credentials for GH integration`);\n }\n\n const gitUrl = gitUrlParse(input.repoUrl);\n return { ghConfig, credentials, gitUrl };\n}\n\nexport function handleError(\n deps: {\n logger: LoggerService;\n },\n desc: string,\n credential: ExtendedGithubCredentials,\n errors: Map<number, GithubFetchError>,\n err: any,\n) {\n logErrorIfNeeded(deps.logger, `${desc} failed`, err);\n const credentialError = createCredentialError(credential, err as Error);\n if (credentialError) {\n errors.set(-1, credentialError);\n }\n}\n\nexport async function computeTotalCountFromGitHubToken(\n deps: {\n logger: LoggerService;\n },\n lastPageDataLengthProviderFn: (lastPageNumber: number) => Promise<number>,\n ghApiName: string,\n pageSize?: number,\n linkHeader?: string,\n): Promise<number | undefined> {\n // There is no direct way to get the total count of repositories other than using octokit.paginate,\n // but will make us retrieve all pages, thus increasing our response time.\n // Workaround here is to analyze the headers, and get the link to the last page.\n if (!linkHeader) {\n deps.logger.debug(\n `No link header found in response from ${ghApiName} GH endpoint => returning current page size`,\n );\n return pageSize;\n }\n const lastPageLink = linkHeader\n .split(',')\n .find(s => s.includes('rel=\"last\"'));\n if (!lastPageLink) {\n deps.logger.debug(\n `No rel='last' link found in response headers from ${ghApiName} GH endpoint => returning current page size`,\n );\n return pageSize;\n }\n const match = lastPageLink.match(/page=(\\d+)/);\n if (!match || match.length < 2) {\n deps.logger.debug(\n `Unable to extract page number from rel='last' link found in response headers from ${ghApiName} GH endpoint => returning current page size`,\n );\n return pageSize;\n }\n\n const lastPageNumber = parseInt(match[1], 10);\n // Fetch the last page to count its items, as it might contain fewer than the requested size\n const lastPageDataLength = await lastPageDataLengthProviderFn(lastPageNumber);\n return pageSize\n ? (lastPageNumber - 1) * pageSize + lastPageDataLength\n : undefined;\n}\n\nexport async function executeFunctionOnFirstSuccessfulIntegration<T>(\n deps: {\n logger: LoggerService;\n cache: CacheService;\n config: Config;\n githubCredentialsProvider: CustomGithubCredentialsProvider;\n },\n integrations: ScmIntegrations,\n params: {\n repoUrl: string;\n fn: (\n validatedRepo: ValidatedRepo,\n octo: Octokit,\n ) => Promise<{ successful: boolean; result?: T }>;\n },\n) {\n const validatedRepo = await validateAndBuildRepoData(\n deps.githubCredentialsProvider,\n integrations,\n deps.config,\n params,\n );\n for (const credential of validatedRepo.credentials) {\n const octo = buildOcto(\n {\n logger: deps.logger,\n cache: deps.cache,\n },\n { credential, owner: validatedRepo.owner },\n validatedRepo.ghConfig.apiBaseUrl,\n );\n if (!octo) {\n continue;\n }\n const res = await params.fn(validatedRepo, octo);\n if (!res.successful) {\n continue;\n }\n return res.result;\n }\n return undefined;\n}\n\nexport async function fetchFromAllIntegrations<T>(\n deps: {\n logger: LoggerService;\n cache: CacheService;\n githubCredentialsProvider: CustomGithubCredentialsProvider;\n },\n integrations: ScmIntegrations,\n params: {\n dataFetcher: (\n octo: Octokit,\n credential: ExtendedGithubCredentials,\n ghConfig: GithubIntegrationConfig,\n ) => Promise<{\n stopFetchingData?: boolean;\n result?: T;\n errors?: GithubFetchError[];\n }>;\n },\n) {\n const ghConfigs = verifyAndGetIntegrations(deps, integrations);\n\n const credentialsByConfig = await getCredentialsFromIntegrations(\n deps.githubCredentialsProvider,\n ghConfigs,\n );\n const errors = new Map<number, GithubFetchError>();\n const data: T[] = [];\n const dataErrs: GithubFetchError[] = [];\n let stopFetchingData: boolean | undefined = false;\n for (const [ghConfig, credentials] of credentialsByConfig) {\n if (stopFetchingData) {\n break;\n }\n deps.logger.debug(\n `Got ${credentials.length} credential(s) for ${ghConfig.host}`,\n );\n for (const credential of credentials) {\n const octokit = buildOcto(\n deps,\n { credential, errors },\n ghConfig.apiBaseUrl,\n );\n if (!octokit) {\n continue;\n }\n const res = await params.dataFetcher(octokit, credential, ghConfig);\n res.errors?.forEach(err => dataErrs.push(err));\n\n if (res.result) {\n data.push(res.result);\n }\n\n stopFetchingData = res.stopFetchingData;\n }\n }\n\n const aggregatedErrors = new Map<number, GithubFetchError>();\n errors.forEach((err, num) => aggregatedErrors.set(num, err));\n dataErrs.forEach((err, idx) => aggregatedErrors.set(idx, err));\n\n return { data, errors: aggregatedErrors };\n}\n\nexport function computeTotalCount<T>(\n data: T[],\n countList: number[],\n pageSize: number,\n) {\n let totalCount = countList.reduce(\n (accumulator, currentValue) => accumulator + currentValue,\n 0,\n );\n if (totalCount < pageSize) {\n totalCount = data.length;\n }\n return totalCount;\n}\n\nexport function extractLocationOwnerMap(locationUrls: string[]) {\n const locationGitOwnerMap = new Map<string, string>();\n for (const locationUrl of locationUrls) {\n const split = locationUrl.split('/blob/');\n if (split.length < 2) {\n continue;\n }\n locationGitOwnerMap.set(locationUrl, gitUrlParse(split[0]).owner);\n }\n return locationGitOwnerMap;\n}\n"],"names":["isGithubAppCredential","logErrorIfNeeded","validateAndBuildRepoData","buildOcto","gitUrlParse"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"auditLogUtils.cjs.js","sources":["../../src/helpers/auditLogUtils.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 { NotFoundError, type NotAllowedError } from '@backstage/errors';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport express from 'express';\n\nconst EVENT_PREFIX = 'BulkImport';\nconst UNKNOWN_ENDPOINT_EVENT = `${EVENT_PREFIX}UnknownEndpoint`;\n\nexport async function auditLogRequestSuccess(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n req: express.Request,\n responseStatus: number,\n) {\n if (!openApiOperationId) {\n auditLogUnknownEndpoint(auditLogger, req);\n return;\n }\n auditLogger.auditLog({\n eventName: operationIdToEventName(openApiOperationId),\n stage: 'completion',\n status: 'succeeded',\n level: 'info',\n request: req,\n response: {\n status: responseStatus,\n },\n message: `'${req.method} ${\n req.path\n }' endpoint hit by ${await auditLogger.getActorId(req)}`,\n });\n}\n\nexport async function auditLogRequestError(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n req: express.Request,\n error: any,\n) {\n if (!openApiOperationId) {\n auditLogUnknownEndpoint(auditLogger, req);\n return;\n }\n auditLogger.auditLog({\n eventName: operationIdToEventName(openApiOperationId),\n stage: 'completion',\n status: 'failed',\n level: 'error',\n request: req,\n response: {\n status: 500,\n body: {\n errors: [\n {\n name: error.name,\n message: error.message || 'internal server error',\n },\n ],\n },\n },\n errors: [error],\n message: `Error while requesting the '${req.method} ${\n req.path\n }' endpoint (request from ${await auditLogger.getActorId(req)})`,\n });\n}\n\nexport async function auditLogUnknownEndpoint(\n auditLogger: AuditLogger,\n req: express.Request,\n) {\n const error = new NotFoundError(`'${req.method} ${req.path}' not found`);\n auditLogger.auditLog({\n eventName: UNKNOWN_ENDPOINT_EVENT,\n stage: 'initiation',\n status: 'failed',\n level: 'info',\n request: req,\n response: {\n status: 404,\n body: {\n errors: [\n {\n name: error.name,\n message: error.message,\n },\n ],\n },\n },\n errors: [error],\n message: `${await auditLogger.getActorId(req)} requested the unknown '${\n req.method\n } ${req.path}' endpoint`,\n });\n}\n\nexport async function auditLogAuthError(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n req: express.Request,\n error: NotAllowedError,\n) {\n if (!openApiOperationId) {\n auditLogUnknownEndpoint(auditLogger, req);\n return;\n }\n auditLogger.auditLog({\n eventName: operationIdToEventName(openApiOperationId),\n stage: 'authorization',\n status: 'failed',\n level: 'warn',\n request: req,\n response: {\n status: 403,\n body: {\n errors: [\n {\n name: error.name,\n message: error.message,\n },\n ],\n },\n },\n errors: [error],\n message: `${await auditLogger.getActorId(\n req,\n )} not authorized to request the '${req.method} ${req.path}' endpoint`,\n });\n}\n\nfunction operationIdToEventName(openApiOperationId: string): string {\n if (openApiOperationId.length === 0) {\n return EVENT_PREFIX;\n }\n return `${EVENT_PREFIX}${openApiOperationId\n .charAt(0)\n .toUpperCase()}${openApiOperationId.slice(1)}`;\n}\n"],"names":["NotFoundError"],"mappings":"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"}
1
+ {"version":3,"file":"auditLogUtils.cjs.js","sources":["../../src/helpers/auditLogUtils.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 { NotFoundError, type NotAllowedError } from '@backstage/errors';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport express from 'express';\n\nconst EVENT_PREFIX = 'BulkImport';\nconst UNKNOWN_ENDPOINT_EVENT = `${EVENT_PREFIX}UnknownEndpoint`;\n\nexport async function auditLogRequestSuccess(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n req: express.Request,\n responseStatus: number,\n) {\n if (!openApiOperationId) {\n auditLogUnknownEndpoint(auditLogger, req);\n return;\n }\n auditLogger.auditLog({\n eventName: operationIdToEventName(openApiOperationId),\n stage: 'completion',\n status: 'succeeded',\n level: 'info',\n request: req,\n response: {\n status: responseStatus,\n },\n message: `'${req.method} ${\n req.path\n }' endpoint hit by ${await auditLogger.getActorId(req)}`,\n });\n}\n\nexport async function auditLogRequestError(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n req: express.Request,\n error: any,\n) {\n if (!openApiOperationId) {\n auditLogUnknownEndpoint(auditLogger, req);\n return;\n }\n auditLogger.auditLog({\n eventName: operationIdToEventName(openApiOperationId),\n stage: 'completion',\n status: 'failed',\n level: 'error',\n request: req,\n response: {\n status: 500,\n body: {\n errors: [\n {\n name: error.name,\n message: error.message || 'internal server error',\n },\n ],\n },\n },\n errors: [error],\n message: `Error while requesting the '${req.method} ${\n req.path\n }' endpoint (request from ${await auditLogger.getActorId(req)})`,\n });\n}\n\nexport async function auditLogUnknownEndpoint(\n auditLogger: AuditLogger,\n req: express.Request,\n) {\n const error = new NotFoundError(`'${req.method} ${req.path}' not found`);\n auditLogger.auditLog({\n eventName: UNKNOWN_ENDPOINT_EVENT,\n stage: 'initiation',\n status: 'failed',\n level: 'info',\n request: req,\n response: {\n status: 404,\n body: {\n errors: [\n {\n name: error.name,\n message: error.message,\n },\n ],\n },\n },\n errors: [error],\n message: `${await auditLogger.getActorId(req)} requested the unknown '${\n req.method\n } ${req.path}' endpoint`,\n });\n}\n\nexport async function auditLogAuthError(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n req: express.Request,\n error: NotAllowedError,\n) {\n if (!openApiOperationId) {\n auditLogUnknownEndpoint(auditLogger, req);\n return;\n }\n auditLogger.auditLog({\n eventName: operationIdToEventName(openApiOperationId),\n stage: 'authorization',\n status: 'failed',\n level: 'warn',\n request: req,\n response: {\n status: 403,\n body: {\n errors: [\n {\n name: error.name,\n message: error.message,\n },\n ],\n },\n },\n errors: [error],\n message: `${await auditLogger.getActorId(\n req,\n )} not authorized to request the '${req.method} ${req.path}' endpoint`,\n });\n}\n\nfunction operationIdToEventName(openApiOperationId: string): string {\n if (openApiOperationId.length === 0) {\n return EVENT_PREFIX;\n }\n return `${EVENT_PREFIX}${openApiOperationId\n .charAt(0)\n .toUpperCase()}${openApiOperationId.slice(1)}`;\n}\n"],"names":["NotFoundError"],"mappings":";;;;AAqBA,MAAM,YAAe,GAAA,YAAA;AACrB,MAAM,sBAAA,GAAyB,GAAG,YAAY,CAAA,eAAA,CAAA;AAE9C,eAAsB,sBACpB,CAAA,WAAA,EACA,kBACA,EAAA,GAAA,EACA,cACA,EAAA;AACA,EAAA,IAAI,CAAC,kBAAoB,EAAA;AACvB,IAAA,uBAAA,CAAwB,aAAa,GAAG,CAAA;AACxC,IAAA;AAAA;AAEF,EAAA,WAAA,CAAY,QAAS,CAAA;AAAA,IACnB,SAAA,EAAW,uBAAuB,kBAAkB,CAAA;AAAA,IACpD,KAAO,EAAA,YAAA;AAAA,IACP,MAAQ,EAAA,WAAA;AAAA,IACR,KAAO,EAAA,MAAA;AAAA,IACP,OAAS,EAAA,GAAA;AAAA,IACT,QAAU,EAAA;AAAA,MACR,MAAQ,EAAA;AAAA,KACV;AAAA,IACA,OAAS,EAAA,CAAA,CAAA,EAAI,GAAI,CAAA,MAAM,CACrB,CAAA,EAAA,GAAA,CAAI,IACN,CAAA,kBAAA,EAAqB,MAAM,WAAA,CAAY,UAAW,CAAA,GAAG,CAAC,CAAA;AAAA,GACvD,CAAA;AACH;AAEA,eAAsB,oBACpB,CAAA,WAAA,EACA,kBACA,EAAA,GAAA,EACA,KACA,EAAA;AACA,EAAA,IAAI,CAAC,kBAAoB,EAAA;AACvB,IAAA,uBAAA,CAAwB,aAAa,GAAG,CAAA;AACxC,IAAA;AAAA;AAEF,EAAA,WAAA,CAAY,QAAS,CAAA;AAAA,IACnB,SAAA,EAAW,uBAAuB,kBAAkB,CAAA;AAAA,IACpD,KAAO,EAAA,YAAA;AAAA,IACP,MAAQ,EAAA,QAAA;AAAA,IACR,KAAO,EAAA,OAAA;AAAA,IACP,OAAS,EAAA,GAAA;AAAA,IACT,QAAU,EAAA;AAAA,MACR,MAAQ,EAAA,GAAA;AAAA,MACR,IAAM,EAAA;AAAA,QACJ,MAAQ,EAAA;AAAA,UACN;AAAA,YACE,MAAM,KAAM,CAAA,IAAA;AAAA,YACZ,OAAA,EAAS,MAAM,OAAW,IAAA;AAAA;AAC5B;AACF;AACF,KACF;AAAA,IACA,MAAA,EAAQ,CAAC,KAAK,CAAA;AAAA,IACd,OAAS,EAAA,CAAA,4BAAA,EAA+B,GAAI,CAAA,MAAM,CAChD,CAAA,EAAA,GAAA,CAAI,IACN,CAAA,yBAAA,EAA4B,MAAM,WAAA,CAAY,UAAW,CAAA,GAAG,CAAC,CAAA,CAAA;AAAA,GAC9D,CAAA;AACH;AAEsB,eAAA,uBAAA,CACpB,aACA,GACA,EAAA;AACA,EAAM,MAAA,KAAA,GAAQ,IAAIA,oBAAc,CAAA,CAAA,CAAA,EAAI,IAAI,MAAM,CAAA,CAAA,EAAI,GAAI,CAAA,IAAI,CAAa,WAAA,CAAA,CAAA;AACvE,EAAA,WAAA,CAAY,QAAS,CAAA;AAAA,IACnB,SAAW,EAAA,sBAAA;AAAA,IACX,KAAO,EAAA,YAAA;AAAA,IACP,MAAQ,EAAA,QAAA;AAAA,IACR,KAAO,EAAA,MAAA;AAAA,IACP,OAAS,EAAA,GAAA;AAAA,IACT,QAAU,EAAA;AAAA,MACR,MAAQ,EAAA,GAAA;AAAA,MACR,IAAM,EAAA;AAAA,QACJ,MAAQ,EAAA;AAAA,UACN;AAAA,YACE,MAAM,KAAM,CAAA,IAAA;AAAA,YACZ,SAAS,KAAM,CAAA;AAAA;AACjB;AACF;AACF,KACF;AAAA,IACA,MAAA,EAAQ,CAAC,KAAK,CAAA;AAAA,IACd,OAAS,EAAA,CAAA,EAAG,MAAM,WAAA,CAAY,UAAW,CAAA,GAAG,CAAC,CAAA,wBAAA,EAC3C,GAAI,CAAA,MACN,CAAI,CAAA,EAAA,GAAA,CAAI,IAAI,CAAA,UAAA;AAAA,GACb,CAAA;AACH;AAEA,eAAsB,iBACpB,CAAA,WAAA,EACA,kBACA,EAAA,GAAA,EACA,KACA,EAAA;AACA,EAAA,IAAI,CAAC,kBAAoB,EAAA;AACvB,IAAA,uBAAA,CAAwB,aAAa,GAAG,CAAA;AACxC,IAAA;AAAA;AAEF,EAAA,WAAA,CAAY,QAAS,CAAA;AAAA,IACnB,SAAA,EAAW,uBAAuB,kBAAkB,CAAA;AAAA,IACpD,KAAO,EAAA,eAAA;AAAA,IACP,MAAQ,EAAA,QAAA;AAAA,IACR,KAAO,EAAA,MAAA;AAAA,IACP,OAAS,EAAA,GAAA;AAAA,IACT,QAAU,EAAA;AAAA,MACR,MAAQ,EAAA,GAAA;AAAA,MACR,IAAM,EAAA;AAAA,QACJ,MAAQ,EAAA;AAAA,UACN;AAAA,YACE,MAAM,KAAM,CAAA,IAAA;AAAA,YACZ,SAAS,KAAM,CAAA;AAAA;AACjB;AACF;AACF,KACF;AAAA,IACA,MAAA,EAAQ,CAAC,KAAK,CAAA;AAAA,IACd,OAAA,EAAS,CAAG,EAAA,MAAM,WAAY,CAAA,UAAA;AAAA,MAC5B;AAAA,KACD,CAAmC,gCAAA,EAAA,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,IAAI,IAAI,CAAA,UAAA;AAAA,GAC3D,CAAA;AACH;AAEA,SAAS,uBAAuB,kBAAoC,EAAA;AAClE,EAAI,IAAA,kBAAA,CAAmB,WAAW,CAAG,EAAA;AACnC,IAAO,OAAA,YAAA;AAAA;AAET,EAAA,OAAO,CAAG,EAAA,YAAY,CAAG,EAAA,kBAAA,CACtB,MAAO,CAAA,CAAC,CACR,CAAA,WAAA,EAAa,CAAA,EAAG,kBAAmB,CAAA,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAChD;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"auth.cjs.js","sources":["../../src/helpers/auth.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 type {\n AuthService,\n HttpAuthService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport express from 'express';\n\nimport { bulkImportPermission } from '@red-hat-developer-hub/backstage-plugin-bulk-import-common';\n\nimport { auditLogAuthError } from './auditLogUtils';\n\n/**\n * This will resolve to { result: AuthorizeResult.ALLOW } if the permission framework is disabled\n */\nexport async function permissionCheck(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n permissions: PermissionsService,\n httpAuth: HttpAuthService,\n req: express.Request,\n) {\n const decision = (\n await permissions.authorize(\n [\n {\n permission: bulkImportPermission,\n resourceRef: bulkImportPermission.resourceType,\n },\n ],\n {\n credentials: await httpAuth.credentials(req),\n },\n )\n )[0];\n\n if (decision.result === AuthorizeResult.DENY) {\n const err = new NotAllowedError('Unauthorized');\n auditLogAuthError(auditLogger, openApiOperationId, req, err);\n throw err;\n }\n}\n\nexport async function getTokenForPlugin(\n auth: AuthService,\n targetPluginId: string,\n): Promise<string> {\n const resp = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId,\n });\n return resp.token;\n}\n"],"names":["bulkImportPermission","AuthorizeResult","NotAllowedError","auditLogAuthError"],"mappings":";;;;;;;AAkCA,eAAsB,eACpB,CAAA,WAAA,EACA,kBACA,EAAA,WAAA,EACA,UACA,GACA,EAAA;AACA,EAAM,MAAA,QAAA,GAAA,CACJ,MAAM,WAAY,CAAA,SAAA;AAAA,IAChB;AAAA,MACE;AAAA,QACE,UAAY,EAAAA,oDAAA;AAAA,QACZ,aAAaA,oDAAqB,CAAA,YAAA;AAAA,OACpC;AAAA,KACF;AAAA,IACA;AAAA,MACE,WAAa,EAAA,MAAM,QAAS,CAAA,WAAA,CAAY,GAAG,CAAA;AAAA,KAC7C;AAAA,KAEF,CAAC,CAAA,CAAA;AAEH,EAAI,IAAA,QAAA,CAAS,MAAW,KAAAC,sCAAA,CAAgB,IAAM,EAAA;AAC5C,IAAM,MAAA,GAAA,GAAM,IAAIC,sBAAA,CAAgB,cAAc,CAAA,CAAA;AAC9C,IAAkBC,+BAAA,CAAA,WAAA,EAAa,kBAAoB,EAAA,GAAA,EAAK,GAAG,CAAA,CAAA;AAC3D,IAAM,MAAA,GAAA,CAAA;AAAA,GACR;AACF,CAAA;AAEsB,eAAA,iBAAA,CACpB,MACA,cACiB,EAAA;AACjB,EAAM,MAAA,IAAA,GAAO,MAAM,IAAA,CAAK,qBAAsB,CAAA;AAAA,IAC5C,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,IAChD,cAAA;AAAA,GACD,CAAA,CAAA;AACD,EAAA,OAAO,IAAK,CAAA,KAAA,CAAA;AACd;;;;;"}
1
+ {"version":3,"file":"auth.cjs.js","sources":["../../src/helpers/auth.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 type {\n AuthService,\n HttpAuthService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport express from 'express';\n\nimport { bulkImportPermission } from '@red-hat-developer-hub/backstage-plugin-bulk-import-common';\n\nimport { auditLogAuthError } from './auditLogUtils';\n\n/**\n * This will resolve to { result: AuthorizeResult.ALLOW } if the permission framework is disabled\n */\nexport async function permissionCheck(\n auditLogger: AuditLogger,\n openApiOperationId: string | undefined,\n permissions: PermissionsService,\n httpAuth: HttpAuthService,\n req: express.Request,\n) {\n const decision = (\n await permissions.authorize(\n [\n {\n permission: bulkImportPermission,\n resourceRef: bulkImportPermission.resourceType,\n },\n ],\n {\n credentials: await httpAuth.credentials(req),\n },\n )\n )[0];\n\n if (decision.result === AuthorizeResult.DENY) {\n const err = new NotAllowedError('Unauthorized');\n auditLogAuthError(auditLogger, openApiOperationId, req, err);\n throw err;\n }\n}\n\nexport async function getTokenForPlugin(\n auth: AuthService,\n targetPluginId: string,\n): Promise<string> {\n const resp = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId,\n });\n return resp.token;\n}\n"],"names":["bulkImportPermission","AuthorizeResult","NotAllowedError","auditLogAuthError"],"mappings":";;;;;;;AAkCA,eAAsB,eACpB,CAAA,WAAA,EACA,kBACA,EAAA,WAAA,EACA,UACA,GACA,EAAA;AACA,EAAM,MAAA,QAAA,GAAA,CACJ,MAAM,WAAY,CAAA,SAAA;AAAA,IAChB;AAAA,MACE;AAAA,QACE,UAAY,EAAAA,oDAAA;AAAA,QACZ,aAAaA,oDAAqB,CAAA;AAAA;AACpC,KACF;AAAA,IACA;AAAA,MACE,WAAa,EAAA,MAAM,QAAS,CAAA,WAAA,CAAY,GAAG;AAAA;AAC7C,KAEF,CAAC,CAAA;AAEH,EAAI,IAAA,QAAA,CAAS,MAAW,KAAAC,sCAAA,CAAgB,IAAM,EAAA;AAC5C,IAAM,MAAA,GAAA,GAAM,IAAIC,sBAAA,CAAgB,cAAc,CAAA;AAC9C,IAAkBC,+BAAA,CAAA,WAAA,EAAa,kBAAoB,EAAA,GAAA,EAAK,GAAG,CAAA;AAC3D,IAAM,MAAA,GAAA;AAAA;AAEV;AAEsB,eAAA,iBAAA,CACpB,MACA,cACiB,EAAA;AACjB,EAAM,MAAA,IAAA,GAAO,MAAM,IAAA,CAAK,qBAAsB,CAAA;AAAA,IAC5C,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,IAChD;AAAA,GACD,CAAA;AACD,EAAA,OAAO,IAAK,CAAA,KAAA;AACd;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"loggingUtils.cjs.js","sources":["../../src/helpers/loggingUtils.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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport { isError } from '@backstage/errors';\n\n/**\n * Logs the error specified by ensuring no sensitive internal data is disclosed.\n * @param logger the logger\n * @param logMsg the log message\n * @param error the error\n */\nexport function logErrorIfNeeded(\n logger: LoggerService,\n logMsg: string,\n error: any,\n) {\n if (isError(error)) {\n // Ensure that we don't log any sensitive internal data:\n logger.error(logMsg, {\n // Default Error properties:\n name: error.name,\n message: error.message,\n stack: error.stack,\n // Additional status code if available:\n status: (error.response as { status?: string })?.status,\n });\n }\n}\n"],"names":["isError"],"mappings":";;;;AAyBgB,SAAA,gBAAA,CACd,MACA,EAAA,MAAA,EACA,KACA,EAAA;AACA,EAAI,IAAAA,cAAA,CAAQ,KAAK,CAAG,EAAA;AAElB,IAAA,MAAA,CAAO,MAAM,MAAQ,EAAA;AAAA;AAAA,MAEnB,MAAM,KAAM,CAAA,IAAA;AAAA,MACZ,SAAS,KAAM,CAAA,OAAA;AAAA,MACf,OAAO,KAAM,CAAA,KAAA;AAAA;AAAA,MAEb,MAAA,EAAS,MAAM,QAAkC,EAAA,MAAA;AAAA,KAClD,CAAA,CAAA;AAAA,GACH;AACF;;;;"}
1
+ {"version":3,"file":"loggingUtils.cjs.js","sources":["../../src/helpers/loggingUtils.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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport { isError } from '@backstage/errors';\n\n/**\n * Logs the error specified by ensuring no sensitive internal data is disclosed.\n * @param logger the logger\n * @param logMsg the log message\n * @param error the error\n */\nexport function logErrorIfNeeded(\n logger: LoggerService,\n logMsg: string,\n error: any,\n) {\n if (isError(error)) {\n // Ensure that we don't log any sensitive internal data:\n logger.error(logMsg, {\n // Default Error properties:\n name: error.name,\n message: error.message,\n stack: error.stack,\n // Additional status code if available:\n status: (error.response as { status?: string })?.status,\n });\n }\n}\n"],"names":["isError"],"mappings":";;;;AAyBgB,SAAA,gBAAA,CACd,MACA,EAAA,MAAA,EACA,KACA,EAAA;AACA,EAAI,IAAAA,cAAA,CAAQ,KAAK,CAAG,EAAA;AAElB,IAAA,MAAA,CAAO,MAAM,MAAQ,EAAA;AAAA;AAAA,MAEnB,MAAM,KAAM,CAAA,IAAA;AAAA,MACZ,SAAS,KAAM,CAAA,OAAA;AAAA,MACf,OAAO,KAAM,CAAA,KAAA;AAAA;AAAA,MAEb,MAAA,EAAS,MAAM,QAAkC,EAAA;AAAA,KAClD,CAAA;AAAA;AAEL;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.cjs.js","sources":["../../src/helpers/pagination.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\n/**\n * Returns a paginated chunk of the specified array\n * @param array the array to paginate\n * @param page the page number\n * @param size the maximum number of elements in each page\n */\nexport function paginateArray<T>(\n array: T[] | undefined,\n page: number,\n size: number,\n): { result: T[]; totalCount: number } {\n if (page <= 0) {\n throw new Error(`page must be >0. Got page=${page}`);\n }\n if (size < 0) {\n throw new Error(`size must be >=0. Got size=${size}`);\n }\n\n const startIndex = (page - 1) * size;\n const endIndex = startIndex + size;\n\n return {\n result: array?.slice(startIndex, endIndex) ?? [],\n totalCount: array?.length ?? 0,\n };\n}\n"],"names":[],"mappings":";;AAsBgB,SAAA,aAAA,CACd,KACA,EAAA,IAAA,EACA,IACqC,EAAA;AACrC,EAAA,IAAI,QAAQ,CAAG,EAAA;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAA6B,0BAAA,EAAA,IAAI,CAAE,CAAA,CAAA,CAAA;AAAA,GACrD;AACA,EAAA,IAAI,OAAO,CAAG,EAAA;AACZ,IAAA,MAAM,IAAI,KAAA,CAAM,CAA8B,2BAAA,EAAA,IAAI,CAAE,CAAA,CAAA,CAAA;AAAA,GACtD;AAEA,EAAM,MAAA,UAAA,GAAA,CAAc,OAAO,CAAK,IAAA,IAAA,CAAA;AAChC,EAAA,MAAM,WAAW,UAAa,GAAA,IAAA,CAAA;AAE9B,EAAO,OAAA;AAAA,IACL,QAAQ,KAAO,EAAA,KAAA,CAAM,UAAY,EAAA,QAAQ,KAAK,EAAC;AAAA,IAC/C,UAAA,EAAY,OAAO,MAAU,IAAA,CAAA;AAAA,GAC/B,CAAA;AACF;;;;"}
1
+ {"version":3,"file":"pagination.cjs.js","sources":["../../src/helpers/pagination.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\n/**\n * Returns a paginated chunk of the specified array\n * @param array the array to paginate\n * @param page the page number\n * @param size the maximum number of elements in each page\n */\nexport function paginateArray<T>(\n array: T[] | undefined,\n page: number,\n size: number,\n): { result: T[]; totalCount: number } {\n if (page <= 0) {\n throw new Error(`page must be >0. Got page=${page}`);\n }\n if (size < 0) {\n throw new Error(`size must be >=0. Got size=${size}`);\n }\n\n const startIndex = (page - 1) * size;\n const endIndex = startIndex + size;\n\n return {\n result: array?.slice(startIndex, endIndex) ?? [],\n totalCount: array?.length ?? 0,\n };\n}\n"],"names":[],"mappings":";;AAsBgB,SAAA,aAAA,CACd,KACA,EAAA,IAAA,EACA,IACqC,EAAA;AACrC,EAAA,IAAI,QAAQ,CAAG,EAAA;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAA6B,0BAAA,EAAA,IAAI,CAAE,CAAA,CAAA;AAAA;AAErD,EAAA,IAAI,OAAO,CAAG,EAAA;AACZ,IAAA,MAAM,IAAI,KAAA,CAAM,CAA8B,2BAAA,EAAA,IAAI,CAAE,CAAA,CAAA;AAAA;AAGtD,EAAM,MAAA,UAAA,GAAA,CAAc,OAAO,CAAK,IAAA,IAAA;AAChC,EAAA,MAAM,WAAW,UAAa,GAAA,IAAA;AAE9B,EAAO,OAAA;AAAA,IACL,QAAQ,KAAO,EAAA,KAAA,CAAM,UAAY,EAAA,QAAQ,KAAK,EAAC;AAAA,IAC/C,UAAA,EAAY,OAAO,MAAU,IAAA;AAAA,GAC/B;AACF;;;;"}
package/dist/index.d.ts CHANGED
@@ -7,9 +7,14 @@ import express from 'express';
7
7
 
8
8
  /**
9
9
  * The bulk-import backend plugin.
10
+ * @public
10
11
  */
11
12
  declare const bulkImportPlugin: _backstage_backend_plugin_api.BackendFeature;
12
13
 
14
+ /**
15
+ * Router Options
16
+ * @public
17
+ */
13
18
  interface RouterOptions {
14
19
  logger: LoggerService;
15
20
  permissions: PermissionEvaluator;
@@ -20,6 +25,10 @@ interface RouterOptions {
20
25
  auth: AuthService;
21
26
  catalogApi: CatalogApi;
22
27
  }
28
+ /**
29
+ * Router
30
+ * @public
31
+ */
23
32
  declare function createRouter(options: RouterOptions): Promise<express.Router>;
24
33
 
25
34
  export { type RouterOptions, createRouter, bulkImportPlugin as default };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.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 {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';\n\nimport { createRouter } from './service/router';\n\n/**\n * The bulk-import backend plugin.\n */\nexport const bulkImportPlugin = createBackendPlugin({\n pluginId: 'bulk-import',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n http: coreServices.httpRouter,\n cache: coreServices.cache,\n discovery: coreServices.discovery,\n permissions: coreServices.permissions,\n httpAuth: coreServices.httpAuth,\n auth: coreServices.auth,\n catalogApi: catalogServiceRef,\n },\n async init({\n config,\n logger,\n http,\n cache,\n discovery,\n permissions,\n httpAuth,\n auth,\n catalogApi,\n }) {\n const router = await createRouter({\n config,\n cache,\n discovery,\n permissions,\n logger,\n httpAuth,\n auth,\n catalogApi,\n });\n http.use(router);\n http.addAuthPolicy({\n path: '/ping',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","catalogServiceRef","router","createRouter"],"mappings":";;;;;;AA2BO,MAAM,mBAAmBA,oCAAoB,CAAA;AAAA,EAClD,QAAU,EAAA,aAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,MAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,MAAMA,6BAAa,CAAA,UAAA;AAAA,QACnB,OAAOA,6BAAa,CAAA,KAAA;AAAA,QACpB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,aAAaA,6BAAa,CAAA,WAAA;AAAA,QAC1B,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAY,EAAAC,uBAAA;AAAA,OACd;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,MAAA;AAAA,QACA,MAAA;AAAA,QACA,IAAA;AAAA,QACA,KAAA;AAAA,QACA,SAAA;AAAA,QACA,WAAA;AAAA,QACA,QAAA;AAAA,QACA,IAAA;AAAA,QACA,UAAA;AAAA,OACC,EAAA;AACD,QAAM,MAAAC,QAAA,GAAS,MAAMC,mBAAa,CAAA;AAAA,UAChC,MAAA;AAAA,UACA,KAAA;AAAA,UACA,SAAA;AAAA,UACA,WAAA;AAAA,UACA,MAAA;AAAA,UACA,QAAA;AAAA,UACA,IAAA;AAAA,UACA,UAAA;AAAA,SACD,CAAA,CAAA;AACD,QAAA,IAAA,CAAK,IAAID,QAAM,CAAA,CAAA;AACf,QAAA,IAAA,CAAK,aAAc,CAAA;AAAA,UACjB,IAAM,EAAA,OAAA;AAAA,UACN,KAAO,EAAA,iBAAA;AAAA,SACR,CAAA,CAAA;AAAA,OACH;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.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 {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';\n\nimport { createRouter } from './service/router';\n\n/**\n * The bulk-import backend plugin.\n * @public\n */\nexport const bulkImportPlugin = createBackendPlugin({\n pluginId: 'bulk-import',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n http: coreServices.httpRouter,\n cache: coreServices.cache,\n discovery: coreServices.discovery,\n permissions: coreServices.permissions,\n httpAuth: coreServices.httpAuth,\n auth: coreServices.auth,\n catalogApi: catalogServiceRef,\n },\n async init({\n config,\n logger,\n http,\n cache,\n discovery,\n permissions,\n httpAuth,\n auth,\n catalogApi,\n }) {\n const router = await createRouter({\n config,\n cache,\n discovery,\n permissions,\n logger,\n httpAuth,\n auth,\n catalogApi,\n });\n http.use(router);\n http.addAuthPolicy({\n path: '/ping',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","catalogServiceRef","router","createRouter"],"mappings":";;;;;;AA4BO,MAAM,mBAAmBA,oCAAoB,CAAA;AAAA,EAClD,QAAU,EAAA,aAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,MAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,MAAMA,6BAAa,CAAA,UAAA;AAAA,QACnB,OAAOA,6BAAa,CAAA,KAAA;AAAA,QACpB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,aAAaA,6BAAa,CAAA,WAAA;AAAA,QAC1B,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAY,EAAAC;AAAA,OACd;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,MAAA;AAAA,QACA,MAAA;AAAA,QACA,IAAA;AAAA,QACA,KAAA;AAAA,QACA,SAAA;AAAA,QACA,WAAA;AAAA,QACA,QAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,OACC,EAAA;AACD,QAAM,MAAAC,QAAA,GAAS,MAAMC,mBAAa,CAAA;AAAA,UAChC,MAAA;AAAA,UACA,KAAA;AAAA,UACA,SAAA;AAAA,UACA,WAAA;AAAA,UACA,MAAA;AAAA,UACA,QAAA;AAAA,UACA,IAAA;AAAA,UACA;AAAA,SACD,CAAA;AACD,QAAA,IAAA,CAAK,IAAID,QAAM,CAAA;AACf,QAAA,IAAA,CAAK,aAAc,CAAA;AAAA,UACjB,IAAM,EAAA,OAAA;AAAA,UACN,KAAO,EAAA;AAAA,SACR,CAAA;AAAA;AACH,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.cjs.js","sources":["../../../src/service/handlers/handlers.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\nexport const DefaultPageNumber = 1;\nexport const DefaultPageSize = 20;\n\nexport interface HandlerResponse<ResponseBody = any> {\n statusCode: number;\n responseBody?: ResponseBody;\n}\n"],"names":[],"mappings":";;AAgBO,MAAM,iBAAoB,GAAA,EAAA;AAC1B,MAAM,eAAkB,GAAA;;;;;"}
1
+ {"version":3,"file":"handlers.cjs.js","sources":["../../../src/service/handlers/handlers.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\nexport const DefaultPageNumber = 1;\nexport const DefaultPageSize = 20;\n\nexport interface HandlerResponse<ResponseBody = any> {\n statusCode: number;\n responseBody?: ResponseBody;\n}\n"],"names":[],"mappings":";;AAgBO,MAAM,iBAAoB,GAAA;AAC1B,MAAM,eAAkB,GAAA;;;;;"}
@@ -40,27 +40,29 @@ async function findAllImports(deps, requestHeaders, queryParams) {
40
40
  search,
41
41
  pageNumber,
42
42
  pageSize
43
- )).targetUrls;
43
+ )).uniqueCatalogUrlLocations;
44
44
  const defaultBranchByRepoUrl = await resolveReposDefaultBranches(
45
45
  deps.logger,
46
46
  deps.githubApiService,
47
- allLocations,
47
+ allLocations.keys(),
48
48
  catalogFilename
49
49
  );
50
50
  const importCandidates = findImportCandidates(
51
- allLocations,
51
+ allLocations.keys(),
52
52
  defaultBranchByRepoUrl,
53
53
  catalogFilename
54
54
  );
55
55
  const importsReachableFromGHIntegrations = await deps.githubApiService.filterLocationsAccessibleFromIntegrations(
56
56
  importCandidates
57
57
  );
58
+ const repoUrlToLocation = /* @__PURE__ */ new Map();
58
59
  const importStatusPromises = [];
59
60
  for (const loc of importsReachableFromGHIntegrations) {
60
61
  const repoUrl = repoUrlFromLocation(loc);
61
62
  if (!repoUrl) {
62
63
  continue;
63
64
  }
65
+ repoUrlToLocation.set(repoUrl, loc);
64
66
  importStatusPromises.push(
65
67
  findImportStatusByRepo(
66
68
  deps,
@@ -71,7 +73,14 @@ async function findAllImports(deps, requestHeaders, queryParams) {
71
73
  );
72
74
  }
73
75
  const result = await Promise.all(importStatusPromises);
74
- const imports = result.filter((res) => res.responseBody).map((res) => res.responseBody);
76
+ const imports = result.filter((res) => res.responseBody).map((res) => res.responseBody).map((res) => {
77
+ const key = res?.repository?.url;
78
+ const location = key ? repoUrlToLocation.get(key) : void 0;
79
+ return {
80
+ ...res,
81
+ source: location ? allLocations.get(location)?.source : void 0
82
+ };
83
+ });
75
84
  sortImports(imports);
76
85
  const paginated = pagination.paginateArray(imports, pageNumber, pageSize);
77
86
  if (apiVersion === "v1") {
@@ -445,7 +454,7 @@ async function findImportStatusByRepo(deps, repoUrl, defaultBranch, includeCatal
445
454
  }
446
455
  );
447
456
  if (!openImportPr.prUrl) {
448
- const catalogLocations = (await deps.catalogHttpClient.listCatalogUrlLocations()).targetUrls;
457
+ const catalogLocations = (await deps.catalogHttpClient.listCatalogUrlLocations()).uniqueCatalogUrlLocations.keys();
449
458
  const catalogUrl = catalogUtils.getCatalogUrl(deps.config, repoUrl, defaultBranch);
450
459
  let exists = false;
451
460
  for (const loc of catalogLocations) {
@@ -1 +1 @@
1
- {"version":3,"file":"bulkImports.cjs.js","sources":["../../../../src/service/handlers/import/bulkImports.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 type { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Config } from '@backstage/config';\n\nimport gitUrlParse from 'git-url-parse';\n\nimport { CatalogHttpClient } from '../../../catalog/catalogHttpClient';\nimport type { CatalogInfoGenerator } from '../../../catalog/catalogInfoGenerator';\nimport {\n getCatalogFilename,\n getCatalogUrl,\n} from '../../../catalog/catalogUtils';\nimport type { Components, Paths } from '../../../generated/openapi';\nimport type { GithubApiService } from '../../../github';\nimport { logErrorIfNeeded, paginateArray } from '../../../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n type HandlerResponse,\n} from '../handlers';\n\ntype CreateImportDryRunStatus =\n | 'CATALOG_ENTITY_CONFLICT'\n | 'CATALOG_INFO_FILE_EXISTS_IN_REPO'\n | 'CODEOWNERS_FILE_NOT_FOUND_IN_REPO'\n | 'REPO_EMPTY';\n\ntype FindAllImportsResponse =\n | Components.Schemas.Import[]\n | Components.Schemas.ImportJobListV2;\n\nfunction sortImports(imports: Components.Schemas.Import[]) {\n imports.sort((a, b) => {\n if (a.repository?.name === undefined && b.repository?.name === undefined) {\n return 0;\n }\n if (a.repository?.name === undefined) {\n return -1;\n }\n if (b.repository?.name === undefined) {\n return 1;\n }\n return a.repository.name.localeCompare(b.repository.name);\n });\n}\n\nexport async function findAllImports(\n deps: {\n logger: LoggerService;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n requestHeaders?: {\n apiVersion?: Paths.FindAllImports.Parameters.ApiVersion;\n },\n queryParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<HandlerResponse<FindAllImportsResponse>> {\n const apiVersion = requestHeaders?.apiVersion ?? 'v1';\n const search = queryParams?.search;\n const pageNumber = queryParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = queryParams?.pageSize ?? DefaultPageSize;\n\n deps.logger.debug(\n `Getting all bulk import jobs (apiVersion=${apiVersion}, search=${search}, page=${pageNumber}, size=${pageSize})..`,\n );\n\n const catalogFilename = getCatalogFilename(deps.config);\n\n const allLocations = (\n await deps.catalogHttpClient.listCatalogUrlLocations(\n search,\n pageNumber,\n pageSize,\n )\n ).targetUrls;\n\n // resolve default branches for each unique repo URL from GH,\n // because we cannot easily determine that from the location target URL.\n // It can be 'main' or something more convoluted like 'our/awesome/main'.\n const defaultBranchByRepoUrl = await resolveReposDefaultBranches(\n deps.logger,\n deps.githubApiService,\n allLocations,\n catalogFilename,\n );\n\n // filter out locations that do not match what we are expecting, i.e.:\n // an URL to a catalog-info YAML file at the root of the repo\n const importCandidates = findImportCandidates(\n allLocations,\n defaultBranchByRepoUrl,\n catalogFilename,\n );\n\n // Keep only repos that are accessible from the configured GH integrations\n const importsReachableFromGHIntegrations =\n await deps.githubApiService.filterLocationsAccessibleFromIntegrations(\n importCandidates,\n );\n\n // now fetch the import statuses in different promises\n const importStatusPromises: Promise<\n HandlerResponse<Components.Schemas.Import>\n >[] = [];\n for (const loc of importsReachableFromGHIntegrations) {\n const repoUrl = repoUrlFromLocation(loc);\n if (!repoUrl) {\n continue;\n }\n\n importStatusPromises.push(\n findImportStatusByRepo(\n deps,\n repoUrl,\n defaultBranchByRepoUrl.get(repoUrl),\n false,\n ),\n );\n }\n\n const result = await Promise.all(importStatusPromises);\n const imports = result\n .filter(res => res.responseBody)\n .map(res => res.responseBody!);\n\n // sorting the output to make it deterministic and easy to navigate in the UI\n sortImports(imports);\n\n const paginated = paginateArray(imports, pageNumber, pageSize);\n if (apiVersion === 'v1') {\n return {\n statusCode: 200,\n responseBody: paginated.result,\n };\n }\n return {\n statusCode: 200,\n responseBody: {\n imports: paginated.result,\n totalCount: paginated.totalCount,\n page: pageNumber,\n size: pageSize,\n },\n };\n}\n\nasync function resolveReposDefaultBranches(\n logger: LoggerService,\n githubApiService: GithubApiService,\n allLocations: string[],\n catalogFilename: string,\n) {\n const defaultBranchByRepoUrlPromises: Promise<{\n repoUrl: string;\n defaultBranch?: string;\n }>[] = [];\n for (const loc of allLocations) {\n // loc has the following format: https://github.com/<org>/<repo>/blob/<default-branch>/catalog-info.yaml\n // but it can have a more convoluted format like 'https://github.com/janus-idp/backstage-plugins/blob/main/plugins/scaffolder-annotator-action/examples/templates/01-scaffolder-template.yaml'\n // if registered manually from the 'Register existing component' feature in Backstage.\n if (!loc.endsWith(catalogFilename)) {\n logger.debug(\n `Ignored location ${loc} because it does not point to a file named ${catalogFilename}`,\n );\n continue;\n }\n const repoUrl = repoUrlFromLocation(loc);\n if (!repoUrl) {\n continue;\n }\n defaultBranchByRepoUrlPromises.push(\n githubApiService\n .getRepositoryFromIntegrations(repoUrl)\n .then(resp => {\n return { repoUrl, defaultBranch: resp?.repository?.default_branch };\n })\n .catch((err: any) => {\n logErrorIfNeeded(\n logger,\n `Ignored repo ${repoUrl} due to an error while fetching details from GitHub`,\n err,\n );\n return {\n repoUrl,\n defaultBranch: undefined,\n };\n }),\n );\n }\n const defaultBranchesResponses = await Promise.all(\n defaultBranchByRepoUrlPromises,\n );\n return new Map(\n defaultBranchesResponses\n .flat()\n .filter(r => r.defaultBranch)\n .map(r => [r.repoUrl, r.defaultBranch!]),\n );\n}\n\nfunction repoUrlFromLocation(loc: string) {\n const split = loc.split('/blob/');\n if (split.length < 2) {\n return undefined;\n }\n return split[0];\n}\n\nfunction findImportCandidates(\n allLocations: string[],\n defaultBranchByRepoUrl: Map<string, string>,\n catalogFilename: string,\n) {\n const filteredLocations: string[] = [];\n for (const loc of allLocations) {\n const repoUrl = repoUrlFromLocation(loc);\n if (!repoUrl) {\n continue;\n }\n\n const defaultBranch = defaultBranchByRepoUrl.get(repoUrl);\n if (!defaultBranch) {\n continue;\n }\n if (loc !== `${repoUrl}/blob/${defaultBranch}/${catalogFilename}`) {\n // Because users can use the \"Register existing component\" workflow to register a Location\n // using any file path in the repo, we consider a repository as an Import Location only\n // if it is at the root of the repository, because that is what the import PR ultimately does.\n continue;\n }\n filteredLocations.push(loc);\n }\n return filteredLocations;\n}\n\nasync function createPR(\n githubApiService: GithubApiService,\n logger: LoggerService,\n req: Components.Schemas.ImportRequest,\n gitUrl: gitUrlParse.GitUrl,\n catalogInfoGenerator: CatalogInfoGenerator,\n config: Config,\n) {\n const appTitle =\n config.getOptionalString('app.title') ?? 'Red Hat Developer Hub';\n const appBaseUrl = config.getString('app.baseUrl');\n const catalogFileName = getCatalogFilename(config);\n return await githubApiService.submitPrToRepo(logger, {\n repoUrl: req.repository.url,\n gitUrl: gitUrl,\n defaultBranch: req.repository.defaultBranch,\n catalogInfoContent:\n req.catalogInfoContent ??\n (await catalogInfoGenerator.generateDefaultCatalogInfoContent(\n req.repository.url,\n )),\n prTitle: req.github?.pullRequest?.title ?? `Add ${catalogFileName}`,\n prBody:\n req.github?.pullRequest?.body ??\n `\nThis pull request adds a **Backstage entity metadata file** to this repository so that the component can be added to a Backstage application.\n\nAfter this pull request is merged, the component will become available in the [${appTitle} software catalog](${appBaseUrl}).\n\nFor more information, read an [overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/).\n`,\n });\n}\n\nasync function handleAddedReposFromCreateImportJobs(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n importRequests: Components.Schemas.ImportRequest[],\n) {\n const result: Components.Schemas.Import[] = [];\n\n for (const req of importRequests) {\n // Check if repo is already imported\n const repoCatalogUrl = getCatalogUrl(\n deps.config,\n req.repository.url,\n req.repository.defaultBranch,\n );\n const hasLocation = await deps.catalogHttpClient.verifyLocationExistence(\n repoCatalogUrl,\n );\n if (!hasLocation) {\n continue;\n }\n const hasCatalogInfoFileInRepo = await deps.githubApiService.hasFileInRepo({\n repoUrl: req.repository.url,\n defaultBranch: req.repository.defaultBranch,\n fileName: getCatalogFilename(deps.config),\n });\n if (!hasCatalogInfoFileInRepo) {\n continue;\n }\n\n const ghRepo = await deps.githubApiService.getRepositoryFromIntegrations(\n req.repository.url,\n );\n\n // Force a refresh of the Location, so that the entities from the catalog-info.yaml can show up quickly (not guaranteed however).\n await deps.catalogHttpClient.refreshLocationByRepoUrl(\n req.repository.url,\n req.repository.defaultBranch,\n );\n\n const gitUrl = gitUrlParse(req.repository.url);\n result.push({\n status: 'ADDED',\n lastUpdate: ghRepo?.repository?.updated_at ?? undefined,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n });\n }\n return result;\n}\n\nasync function handlePrCreationRequest(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n req: Components.Schemas.ImportRequest,\n gitUrl: gitUrlParse.GitUrl,\n): Promise<Components.Schemas.Import> {\n const repoCatalogUrl = getCatalogUrl(\n deps.config,\n req.repository.url,\n req.repository.defaultBranch,\n );\n const prToRepo = await createPR(\n deps.githubApiService,\n deps.logger,\n req,\n gitUrl,\n deps.catalogInfoGenerator,\n deps.config,\n );\n if (prToRepo.errors && prToRepo.errors.length > 0) {\n return {\n errors: prToRepo.errors,\n status: 'PR_ERROR',\n repository: req.repository,\n };\n }\n if (prToRepo.prUrl) {\n deps.logger.debug(`Created new PR from request: ${prToRepo.prUrl}`);\n }\n\n // Create Location\n await deps.catalogHttpClient.possiblyCreateLocation(repoCatalogUrl);\n\n if (prToRepo.hasChanges === false) {\n deps.logger.debug(\n `No bulk import PR created on ${req.repository.url} since its default branch (${req.repository.defaultBranch}) already contains a catalog-info file`,\n );\n\n // Force a refresh of the Location, so that the entities from the catalog-info.yaml can show up quickly (not guaranteed however).\n await deps.catalogHttpClient.refreshLocationByRepoUrl(\n req.repository.url,\n req.repository.defaultBranch,\n );\n return {\n status: 'ADDED',\n lastUpdate: prToRepo.lastUpdate,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n };\n }\n\n return {\n errors: prToRepo.errors,\n status: 'WAIT_PR_APPROVAL',\n lastUpdate: prToRepo.lastUpdate,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n github: {\n pullRequest: {\n url: prToRepo.prUrl,\n number: prToRepo.prNumber,\n },\n },\n };\n}\n\nexport async function createImportJobs(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n reqParams: {\n importRequests: Paths.CreateImportJobs.RequestBody;\n dryRun?: boolean;\n },\n): Promise<\n HandlerResponse<Components.Schemas.Import[] | { errors: string[] }>\n> {\n const dryRun = reqParams.dryRun ?? false;\n const importRequests = reqParams.importRequests;\n deps.logger.debug(\n `Handling request to import ${importRequests?.length} repo(s) (dryRun=${dryRun})..`,\n );\n\n if (importRequests.length === 0) {\n deps.logger.debug('Missing import requests from request body');\n return {\n statusCode: 400,\n responseBody: [],\n };\n }\n\n if (dryRun) {\n return {\n statusCode: 202,\n responseBody: await dryRunCreateImportJobs(deps, importRequests),\n };\n }\n\n const result: Components.Schemas.Import[] = [];\n\n const addedRepos = await handleAddedReposFromCreateImportJobs(\n deps,\n importRequests,\n );\n result.push(...addedRepos);\n const addedReposMap = new Map(\n addedRepos.map(res => [res.repository?.url, res]),\n );\n const remainingRequests = importRequests.filter(\n req => !addedReposMap.has(req.repository.url),\n );\n\n for (const req of remainingRequests) {\n const gitUrl = gitUrlParse(req.repository.url);\n\n // Create PR\n try {\n result.push(await handlePrCreationRequest(deps, req, gitUrl));\n } catch (error: any) {\n result.push({\n errors: [error.message],\n status: 'PR_ERROR',\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n } as Components.Schemas.Import);\n }\n }\n\n sortImports(result);\n\n return {\n statusCode: 202,\n responseBody: result,\n };\n}\n\nasync function dryRunCreateImportJobs(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n importRequests: Paths.CreateImportJobs.RequestBody,\n) {\n const result: Components.Schemas.Import[] = [];\n for (const req of importRequests) {\n const gitUrl = gitUrlParse(req.repository.url);\n\n const dryRunChecks = await performDryRunChecks(deps, req);\n if (dryRunChecks.errors?.length > 0) {\n deps.logger.warn(\n `Errors while performing dry-run checks: ${dryRunChecks.errors}`,\n );\n }\n result.push({\n errors: dryRunChecks.dryRunStatuses,\n catalogEntityName: req.catalogEntityName,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n });\n }\n return result;\n}\n\nasync function performDryRunChecks(\n deps: {\n logger: LoggerService;\n auth: AuthService;\n catalogApi: CatalogApi;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n req: Components.Schemas.ImportRequest,\n): Promise<{ dryRunStatuses: CreateImportDryRunStatus[]; errors: string[] }> {\n const checkCatalog = async (\n catalogEntityName: string,\n ): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const hasEntity = await deps.catalogHttpClient.hasEntityInCatalog(\n catalogEntityName,\n );\n if (hasEntity) {\n return { dryRunStatuses: ['CATALOG_ENTITY_CONFLICT'] };\n }\n return {};\n };\n\n const checkEmptyRepo = async (): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const empty = await deps.githubApiService.isRepoEmpty({\n repoUrl: req.repository.url,\n });\n if (empty) {\n return {\n dryRunStatuses: ['REPO_EMPTY'],\n };\n }\n return {};\n };\n\n const checkCatalogInfoPresenceInRepo = async (): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const exists = await deps.githubApiService.hasFileInRepo({\n repoUrl: req.repository.url,\n defaultBranch: req.repository.defaultBranch,\n fileName: getCatalogFilename(deps.config),\n });\n if (exists) {\n return {\n dryRunStatuses: ['CATALOG_INFO_FILE_EXISTS_IN_REPO'],\n };\n }\n return {};\n };\n\n const checkCodeOwnersFileInRepo = async (): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const exists = await deps.githubApiService.hasFileInRepo({\n repoUrl: req.repository.url,\n defaultBranch: req.repository.defaultBranch,\n fileName: '.github/CODEOWNERS',\n });\n if (!exists) {\n return {\n dryRunStatuses: ['CODEOWNERS_FILE_NOT_FOUND_IN_REPO'],\n };\n }\n return {};\n };\n\n const dryRunStatuses: CreateImportDryRunStatus[] = [];\n const errors: string[] = [];\n const allChecksFn = [checkEmptyRepo(), checkCatalogInfoPresenceInRepo()];\n if (req.catalogEntityName?.trim()) {\n allChecksFn.push(checkCatalog(req.catalogEntityName));\n }\n if (req.codeOwnersFileAsEntityOwner) {\n allChecksFn.push(checkCodeOwnersFileInRepo());\n }\n const allChecks = await Promise.all(allChecksFn);\n allChecks.flat().forEach(res => {\n if (res.dryRunStatuses) {\n dryRunStatuses.push(...res.dryRunStatuses);\n }\n if (res.errors) {\n errors.push(...res.errors);\n }\n });\n\n dryRunStatuses.sort((a, b) => a.localeCompare(b));\n\n return {\n dryRunStatuses,\n errors,\n };\n}\n\nexport async function findImportStatusByRepo(\n deps: {\n logger: LoggerService;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n repoUrl: string,\n defaultBranch?: string,\n includeCatalogInfoContent?: boolean,\n): Promise<HandlerResponse<Components.Schemas.Import>> {\n deps.logger.debug(`Getting bulk import job status for ${repoUrl}..`);\n\n const gitUrl = gitUrlParse(repoUrl);\n\n const errors: string[] = [];\n const result = {\n id: repoUrl,\n repository: {\n url: repoUrl,\n name: gitUrl.name,\n organization: gitUrl.organization,\n id: `${gitUrl.organization}/${gitUrl.name}`,\n defaultBranch,\n },\n approvalTool: 'GIT',\n status: null,\n } as Components.Schemas.Import;\n try {\n // Check to see if there are any PR\n const openImportPr = await deps.githubApiService.findImportOpenPr(\n deps.logger,\n {\n repoUrl: repoUrl,\n includeCatalogInfoContent,\n },\n );\n if (!openImportPr.prUrl) {\n const catalogLocations = (\n await deps.catalogHttpClient.listCatalogUrlLocations()\n ).targetUrls;\n const catalogUrl = getCatalogUrl(deps.config, repoUrl, defaultBranch);\n let exists = false;\n for (const loc of catalogLocations) {\n if (loc === catalogUrl) {\n exists = true;\n break;\n }\n }\n if (\n exists &&\n (await deps.githubApiService.hasFileInRepo({\n repoUrl,\n defaultBranch,\n fileName: getCatalogFilename(deps.config),\n }))\n ) {\n result.status = 'ADDED';\n // Force a refresh of the Location, so that the entities from the catalog-info.yaml can show up quickly (not guaranteed however).\n await deps.catalogHttpClient.refreshLocationByRepoUrl(\n repoUrl,\n defaultBranch,\n );\n }\n // No import PR => let's determine last update from the repository\n const ghRepo = await deps.githubApiService.getRepositoryFromIntegrations(\n repoUrl,\n );\n result.lastUpdate = ghRepo.repository?.updated_at ?? undefined;\n return {\n statusCode: 200,\n responseBody: result,\n };\n }\n result.status = 'WAIT_PR_APPROVAL';\n result.github = {\n pullRequest: {\n number: openImportPr.prNum,\n url: openImportPr.prUrl,\n title: openImportPr.prTitle,\n body: openImportPr.prBody,\n catalogInfoContent: openImportPr.prCatalogInfoContent,\n },\n };\n result.lastUpdate = openImportPr.lastUpdate;\n } catch (error: any) {\n errors.push(error.message);\n result.errors = errors;\n if (error.message?.includes('Not Found')) {\n return {\n statusCode: 404,\n responseBody: result,\n };\n }\n result.status = 'PR_ERROR';\n }\n\n return {\n statusCode: 200,\n responseBody: result,\n };\n}\n\nexport async function deleteImportByRepo(\n deps: {\n logger: LoggerService;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n repoUrl: string,\n defaultBranch?: string,\n): Promise<HandlerResponse<void>> {\n deps.logger.debug(`Deleting bulk import job status for ${repoUrl}..`);\n\n // Check to see if there are any PR\n const openImportPr = await deps.githubApiService.findImportOpenPr(\n deps.logger,\n {\n repoUrl: repoUrl,\n },\n );\n const gitUrl = gitUrlParse(repoUrl);\n if (openImportPr.prUrl) {\n // Close PR\n const appTitle =\n deps.config.getOptionalString('app.title') ?? 'Red Hat Developer Hub';\n const appBaseUrl = deps.config.getString('app.baseUrl');\n await deps.githubApiService.closeImportPR(deps.logger, {\n repoUrl,\n gitUrl,\n comment: `Closing PR upon request for bulk import deletion. This request was created from [${appTitle}](${appBaseUrl}).`,\n });\n }\n // Also delete the import branch, so that it is not outdated if we try later to import the repo again\n await deps.githubApiService.deleteImportBranch({\n repoUrl,\n gitUrl,\n });\n // Remove Location from catalog\n const catalogUrl = getCatalogUrl(deps.config, repoUrl, defaultBranch);\n const findLocationFrom = (list: { id?: string; target: string }[]) => {\n for (const loc of list) {\n if (loc.target === catalogUrl) {\n return loc.id;\n }\n }\n return undefined;\n };\n\n const locationId = findLocationFrom(\n (\n await deps.catalogHttpClient.listCatalogUrlLocationsByIdFromLocationsEndpoint()\n ).locations,\n );\n if (locationId) {\n await deps.catalogHttpClient.deleteCatalogLocationById(locationId);\n }\n\n return {\n statusCode: 204,\n responseBody: undefined,\n };\n}\n"],"names":["DefaultPageNumber","DefaultPageSize","getCatalogFilename","paginateArray","logErrorIfNeeded","getCatalogUrl","gitUrlParse"],"mappings":"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"}
1
+ {"version":3,"file":"bulkImports.cjs.js","sources":["../../../../src/service/handlers/import/bulkImports.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 type { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Config } from '@backstage/config';\n\nimport gitUrlParse from 'git-url-parse';\n\nimport { CatalogHttpClient } from '../../../catalog/catalogHttpClient';\nimport type { CatalogInfoGenerator } from '../../../catalog/catalogInfoGenerator';\nimport {\n getCatalogFilename,\n getCatalogUrl,\n} from '../../../catalog/catalogUtils';\nimport type { Components, Paths } from '../../../generated/openapi';\nimport type { GithubApiService } from '../../../github';\nimport { logErrorIfNeeded, paginateArray } from '../../../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n type HandlerResponse,\n} from '../handlers';\n\ntype CreateImportDryRunStatus =\n | 'CATALOG_ENTITY_CONFLICT'\n | 'CATALOG_INFO_FILE_EXISTS_IN_REPO'\n | 'CODEOWNERS_FILE_NOT_FOUND_IN_REPO'\n | 'REPO_EMPTY';\n\ntype FindAllImportsResponse =\n | Components.Schemas.Import[]\n | Components.Schemas.ImportJobListV2;\n\nfunction sortImports(imports: Components.Schemas.Import[]) {\n imports.sort((a, b) => {\n if (a.repository?.name === undefined && b.repository?.name === undefined) {\n return 0;\n }\n if (a.repository?.name === undefined) {\n return -1;\n }\n if (b.repository?.name === undefined) {\n return 1;\n }\n return a.repository.name.localeCompare(b.repository.name);\n });\n}\n\nexport async function findAllImports(\n deps: {\n logger: LoggerService;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n requestHeaders?: {\n apiVersion?: Paths.FindAllImports.Parameters.ApiVersion;\n },\n queryParams?: {\n search?: string;\n pageNumber?: number;\n pageSize?: number;\n },\n): Promise<HandlerResponse<FindAllImportsResponse>> {\n const apiVersion = requestHeaders?.apiVersion ?? 'v1';\n const search = queryParams?.search;\n const pageNumber = queryParams?.pageNumber ?? DefaultPageNumber;\n const pageSize = queryParams?.pageSize ?? DefaultPageSize;\n\n deps.logger.debug(\n `Getting all bulk import jobs (apiVersion=${apiVersion}, search=${search}, page=${pageNumber}, size=${pageSize})..`,\n );\n\n const catalogFilename = getCatalogFilename(deps.config);\n\n const allLocations = (\n await deps.catalogHttpClient.listCatalogUrlLocations(\n search,\n pageNumber,\n pageSize,\n )\n ).uniqueCatalogUrlLocations;\n\n // resolve default branches for each unique repo URL from GH,\n // because we cannot easily determine that from the location target URL.\n // It can be 'main' or something more convoluted like 'our/awesome/main'.\n const defaultBranchByRepoUrl = await resolveReposDefaultBranches(\n deps.logger,\n deps.githubApiService,\n allLocations.keys(),\n catalogFilename,\n );\n\n // filter out locations that do not match what we are expecting, i.e.:\n // an URL to a catalog-info YAML file at the root of the repo\n const importCandidates = findImportCandidates(\n allLocations.keys(),\n defaultBranchByRepoUrl,\n catalogFilename,\n );\n\n // Keep only repos that are accessible from the configured GH integrations\n const importsReachableFromGHIntegrations =\n await deps.githubApiService.filterLocationsAccessibleFromIntegrations(\n importCandidates,\n );\n\n const repoUrlToLocation = new Map<string, string>();\n\n // now fetch the import statuses in different promises\n const importStatusPromises: Promise<\n HandlerResponse<Components.Schemas.Import>\n >[] = [];\n for (const loc of importsReachableFromGHIntegrations) {\n const repoUrl = repoUrlFromLocation(loc);\n if (!repoUrl) {\n continue;\n }\n repoUrlToLocation.set(repoUrl, loc);\n\n importStatusPromises.push(\n findImportStatusByRepo(\n deps,\n repoUrl,\n defaultBranchByRepoUrl.get(repoUrl),\n false,\n ),\n );\n }\n\n const result = await Promise.all(importStatusPromises);\n const imports = result\n .filter(res => res.responseBody)\n .map(res => res.responseBody!)\n .map(res => {\n const key = res?.repository?.url;\n const location = key ? repoUrlToLocation.get(key) : undefined;\n return {\n ...res,\n source: location ? allLocations.get(location)?.source : undefined,\n };\n });\n\n // sorting the output to make it deterministic and easy to navigate in the UI\n sortImports(imports);\n\n const paginated = paginateArray(imports, pageNumber, pageSize);\n if (apiVersion === 'v1') {\n return {\n statusCode: 200,\n responseBody: paginated.result,\n };\n }\n return {\n statusCode: 200,\n responseBody: {\n imports: paginated.result,\n totalCount: paginated.totalCount,\n page: pageNumber,\n size: pageSize,\n },\n };\n}\n\nasync function resolveReposDefaultBranches(\n logger: LoggerService,\n githubApiService: GithubApiService,\n allLocations: Iterable<string>,\n catalogFilename: string,\n) {\n const defaultBranchByRepoUrlPromises: Promise<{\n repoUrl: string;\n defaultBranch?: string;\n }>[] = [];\n for (const loc of allLocations) {\n // loc has the following format: https://github.com/<org>/<repo>/blob/<default-branch>/catalog-info.yaml\n // but it can have a more convoluted format like 'https://github.com/janus-idp/backstage-plugins/blob/main/plugins/scaffolder-annotator-action/examples/templates/01-scaffolder-template.yaml'\n // if registered manually from the 'Register existing component' feature in Backstage.\n if (!loc.endsWith(catalogFilename)) {\n logger.debug(\n `Ignored location ${loc} because it does not point to a file named ${catalogFilename}`,\n );\n continue;\n }\n const repoUrl = repoUrlFromLocation(loc);\n if (!repoUrl) {\n continue;\n }\n defaultBranchByRepoUrlPromises.push(\n githubApiService\n .getRepositoryFromIntegrations(repoUrl)\n .then(resp => {\n return { repoUrl, defaultBranch: resp?.repository?.default_branch };\n })\n .catch((err: any) => {\n logErrorIfNeeded(\n logger,\n `Ignored repo ${repoUrl} due to an error while fetching details from GitHub`,\n err,\n );\n return {\n repoUrl,\n defaultBranch: undefined,\n };\n }),\n );\n }\n const defaultBranchesResponses = await Promise.all(\n defaultBranchByRepoUrlPromises,\n );\n return new Map(\n defaultBranchesResponses\n .flat()\n .filter(r => r.defaultBranch)\n .map(r => [r.repoUrl, r.defaultBranch!]),\n );\n}\n\nfunction repoUrlFromLocation(loc: string) {\n const split = loc.split('/blob/');\n if (split.length < 2) {\n return undefined;\n }\n return split[0];\n}\n\nfunction findImportCandidates(\n allLocations: Iterable<string>,\n defaultBranchByRepoUrl: Map<string, string>,\n catalogFilename: string,\n) {\n const filteredLocations: string[] = [];\n for (const loc of allLocations) {\n const repoUrl = repoUrlFromLocation(loc);\n if (!repoUrl) {\n continue;\n }\n\n const defaultBranch = defaultBranchByRepoUrl.get(repoUrl);\n if (!defaultBranch) {\n continue;\n }\n if (loc !== `${repoUrl}/blob/${defaultBranch}/${catalogFilename}`) {\n // Because users can use the \"Register existing component\" workflow to register a Location\n // using any file path in the repo, we consider a repository as an Import Location only\n // if it is at the root of the repository, because that is what the import PR ultimately does.\n continue;\n }\n filteredLocations.push(loc);\n }\n return filteredLocations;\n}\n\nasync function createPR(\n githubApiService: GithubApiService,\n logger: LoggerService,\n req: Components.Schemas.ImportRequest,\n gitUrl: gitUrlParse.GitUrl,\n catalogInfoGenerator: CatalogInfoGenerator,\n config: Config,\n) {\n const appTitle =\n config.getOptionalString('app.title') ?? 'Red Hat Developer Hub';\n const appBaseUrl = config.getString('app.baseUrl');\n const catalogFileName = getCatalogFilename(config);\n return await githubApiService.submitPrToRepo(logger, {\n repoUrl: req.repository.url,\n gitUrl: gitUrl,\n defaultBranch: req.repository.defaultBranch,\n catalogInfoContent:\n req.catalogInfoContent ??\n (await catalogInfoGenerator.generateDefaultCatalogInfoContent(\n req.repository.url,\n )),\n prTitle: req.github?.pullRequest?.title ?? `Add ${catalogFileName}`,\n prBody:\n req.github?.pullRequest?.body ??\n `\nThis pull request adds a **Backstage entity metadata file** to this repository so that the component can be added to a Backstage application.\n\nAfter this pull request is merged, the component will become available in the [${appTitle} software catalog](${appBaseUrl}).\n\nFor more information, read an [overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/).\n`,\n });\n}\n\nasync function handleAddedReposFromCreateImportJobs(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n importRequests: Components.Schemas.ImportRequest[],\n) {\n const result: Components.Schemas.Import[] = [];\n\n for (const req of importRequests) {\n // Check if repo is already imported\n const repoCatalogUrl = getCatalogUrl(\n deps.config,\n req.repository.url,\n req.repository.defaultBranch,\n );\n const hasLocation = await deps.catalogHttpClient.verifyLocationExistence(\n repoCatalogUrl,\n );\n if (!hasLocation) {\n continue;\n }\n const hasCatalogInfoFileInRepo = await deps.githubApiService.hasFileInRepo({\n repoUrl: req.repository.url,\n defaultBranch: req.repository.defaultBranch,\n fileName: getCatalogFilename(deps.config),\n });\n if (!hasCatalogInfoFileInRepo) {\n continue;\n }\n\n const ghRepo = await deps.githubApiService.getRepositoryFromIntegrations(\n req.repository.url,\n );\n\n // Force a refresh of the Location, so that the entities from the catalog-info.yaml can show up quickly (not guaranteed however).\n await deps.catalogHttpClient.refreshLocationByRepoUrl(\n req.repository.url,\n req.repository.defaultBranch,\n );\n\n const gitUrl = gitUrlParse(req.repository.url);\n result.push({\n status: 'ADDED',\n lastUpdate: ghRepo?.repository?.updated_at ?? undefined,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n });\n }\n return result;\n}\n\nasync function handlePrCreationRequest(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n req: Components.Schemas.ImportRequest,\n gitUrl: gitUrlParse.GitUrl,\n): Promise<Components.Schemas.Import> {\n const repoCatalogUrl = getCatalogUrl(\n deps.config,\n req.repository.url,\n req.repository.defaultBranch,\n );\n const prToRepo = await createPR(\n deps.githubApiService,\n deps.logger,\n req,\n gitUrl,\n deps.catalogInfoGenerator,\n deps.config,\n );\n if (prToRepo.errors && prToRepo.errors.length > 0) {\n return {\n errors: prToRepo.errors,\n status: 'PR_ERROR',\n repository: req.repository,\n };\n }\n if (prToRepo.prUrl) {\n deps.logger.debug(`Created new PR from request: ${prToRepo.prUrl}`);\n }\n\n // Create Location\n await deps.catalogHttpClient.possiblyCreateLocation(repoCatalogUrl);\n\n if (prToRepo.hasChanges === false) {\n deps.logger.debug(\n `No bulk import PR created on ${req.repository.url} since its default branch (${req.repository.defaultBranch}) already contains a catalog-info file`,\n );\n\n // Force a refresh of the Location, so that the entities from the catalog-info.yaml can show up quickly (not guaranteed however).\n await deps.catalogHttpClient.refreshLocationByRepoUrl(\n req.repository.url,\n req.repository.defaultBranch,\n );\n return {\n status: 'ADDED',\n lastUpdate: prToRepo.lastUpdate,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n };\n }\n\n return {\n errors: prToRepo.errors,\n status: 'WAIT_PR_APPROVAL',\n lastUpdate: prToRepo.lastUpdate,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n github: {\n pullRequest: {\n url: prToRepo.prUrl,\n number: prToRepo.prNumber,\n },\n },\n };\n}\n\nexport async function createImportJobs(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n reqParams: {\n importRequests: Paths.CreateImportJobs.RequestBody;\n dryRun?: boolean;\n },\n): Promise<\n HandlerResponse<Components.Schemas.Import[] | { errors: string[] }>\n> {\n const dryRun = reqParams.dryRun ?? false;\n const importRequests = reqParams.importRequests;\n deps.logger.debug(\n `Handling request to import ${importRequests?.length} repo(s) (dryRun=${dryRun})..`,\n );\n\n if (importRequests.length === 0) {\n deps.logger.debug('Missing import requests from request body');\n return {\n statusCode: 400,\n responseBody: [],\n };\n }\n\n if (dryRun) {\n return {\n statusCode: 202,\n responseBody: await dryRunCreateImportJobs(deps, importRequests),\n };\n }\n\n const result: Components.Schemas.Import[] = [];\n\n const addedRepos = await handleAddedReposFromCreateImportJobs(\n deps,\n importRequests,\n );\n result.push(...addedRepos);\n const addedReposMap = new Map(\n addedRepos.map(res => [res.repository?.url, res]),\n );\n const remainingRequests = importRequests.filter(\n req => !addedReposMap.has(req.repository.url),\n );\n\n for (const req of remainingRequests) {\n const gitUrl = gitUrlParse(req.repository.url);\n\n // Create PR\n try {\n result.push(await handlePrCreationRequest(deps, req, gitUrl));\n } catch (error: any) {\n result.push({\n errors: [error.message],\n status: 'PR_ERROR',\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n } as Components.Schemas.Import);\n }\n }\n\n sortImports(result);\n\n return {\n statusCode: 202,\n responseBody: result,\n };\n}\n\nasync function dryRunCreateImportJobs(\n deps: {\n logger: LoggerService;\n config: Config;\n auth: AuthService;\n catalogApi: CatalogApi;\n githubApiService: GithubApiService;\n catalogInfoGenerator: CatalogInfoGenerator;\n catalogHttpClient: CatalogHttpClient;\n },\n importRequests: Paths.CreateImportJobs.RequestBody,\n) {\n const result: Components.Schemas.Import[] = [];\n for (const req of importRequests) {\n const gitUrl = gitUrlParse(req.repository.url);\n\n const dryRunChecks = await performDryRunChecks(deps, req);\n if (dryRunChecks.errors?.length > 0) {\n deps.logger.warn(\n `Errors while performing dry-run checks: ${dryRunChecks.errors}`,\n );\n }\n result.push({\n errors: dryRunChecks.dryRunStatuses,\n catalogEntityName: req.catalogEntityName,\n repository: {\n url: req.repository.url,\n name: gitUrl.name,\n organization: gitUrl.organization,\n },\n });\n }\n return result;\n}\n\nasync function performDryRunChecks(\n deps: {\n logger: LoggerService;\n auth: AuthService;\n catalogApi: CatalogApi;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n req: Components.Schemas.ImportRequest,\n): Promise<{ dryRunStatuses: CreateImportDryRunStatus[]; errors: string[] }> {\n const checkCatalog = async (\n catalogEntityName: string,\n ): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const hasEntity = await deps.catalogHttpClient.hasEntityInCatalog(\n catalogEntityName,\n );\n if (hasEntity) {\n return { dryRunStatuses: ['CATALOG_ENTITY_CONFLICT'] };\n }\n return {};\n };\n\n const checkEmptyRepo = async (): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const empty = await deps.githubApiService.isRepoEmpty({\n repoUrl: req.repository.url,\n });\n if (empty) {\n return {\n dryRunStatuses: ['REPO_EMPTY'],\n };\n }\n return {};\n };\n\n const checkCatalogInfoPresenceInRepo = async (): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const exists = await deps.githubApiService.hasFileInRepo({\n repoUrl: req.repository.url,\n defaultBranch: req.repository.defaultBranch,\n fileName: getCatalogFilename(deps.config),\n });\n if (exists) {\n return {\n dryRunStatuses: ['CATALOG_INFO_FILE_EXISTS_IN_REPO'],\n };\n }\n return {};\n };\n\n const checkCodeOwnersFileInRepo = async (): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const exists = await deps.githubApiService.hasFileInRepo({\n repoUrl: req.repository.url,\n defaultBranch: req.repository.defaultBranch,\n fileName: '.github/CODEOWNERS',\n });\n if (!exists) {\n return {\n dryRunStatuses: ['CODEOWNERS_FILE_NOT_FOUND_IN_REPO'],\n };\n }\n return {};\n };\n\n const dryRunStatuses: CreateImportDryRunStatus[] = [];\n const errors: string[] = [];\n const allChecksFn = [checkEmptyRepo(), checkCatalogInfoPresenceInRepo()];\n if (req.catalogEntityName?.trim()) {\n allChecksFn.push(checkCatalog(req.catalogEntityName));\n }\n if (req.codeOwnersFileAsEntityOwner) {\n allChecksFn.push(checkCodeOwnersFileInRepo());\n }\n const allChecks = await Promise.all(allChecksFn);\n allChecks.flat().forEach(res => {\n if (res.dryRunStatuses) {\n dryRunStatuses.push(...res.dryRunStatuses);\n }\n if (res.errors) {\n errors.push(...res.errors);\n }\n });\n\n dryRunStatuses.sort((a, b) => a.localeCompare(b));\n\n return {\n dryRunStatuses,\n errors,\n };\n}\n\nexport async function findImportStatusByRepo(\n deps: {\n logger: LoggerService;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n repoUrl: string,\n defaultBranch?: string,\n includeCatalogInfoContent?: boolean,\n): Promise<HandlerResponse<Components.Schemas.Import>> {\n deps.logger.debug(`Getting bulk import job status for ${repoUrl}..`);\n\n const gitUrl = gitUrlParse(repoUrl);\n\n const errors: string[] = [];\n const result = {\n id: repoUrl,\n repository: {\n url: repoUrl,\n name: gitUrl.name,\n organization: gitUrl.organization,\n id: `${gitUrl.organization}/${gitUrl.name}`,\n defaultBranch,\n },\n approvalTool: 'GIT',\n status: null,\n } as Components.Schemas.Import;\n try {\n // Check to see if there are any PR\n const openImportPr = await deps.githubApiService.findImportOpenPr(\n deps.logger,\n {\n repoUrl: repoUrl,\n includeCatalogInfoContent,\n },\n );\n if (!openImportPr.prUrl) {\n const catalogLocations = (\n await deps.catalogHttpClient.listCatalogUrlLocations()\n ).uniqueCatalogUrlLocations.keys();\n\n const catalogUrl = getCatalogUrl(deps.config, repoUrl, defaultBranch);\n let exists = false;\n for (const loc of catalogLocations) {\n if (loc === catalogUrl) {\n exists = true;\n break;\n }\n }\n if (\n exists &&\n (await deps.githubApiService.hasFileInRepo({\n repoUrl,\n defaultBranch,\n fileName: getCatalogFilename(deps.config),\n }))\n ) {\n result.status = 'ADDED';\n // Force a refresh of the Location, so that the entities from the catalog-info.yaml can show up quickly (not guaranteed however).\n await deps.catalogHttpClient.refreshLocationByRepoUrl(\n repoUrl,\n defaultBranch,\n );\n }\n // No import PR => let's determine last update from the repository\n const ghRepo = await deps.githubApiService.getRepositoryFromIntegrations(\n repoUrl,\n );\n result.lastUpdate = ghRepo.repository?.updated_at ?? undefined;\n return {\n statusCode: 200,\n responseBody: result,\n };\n }\n result.status = 'WAIT_PR_APPROVAL';\n result.github = {\n pullRequest: {\n number: openImportPr.prNum,\n url: openImportPr.prUrl,\n title: openImportPr.prTitle,\n body: openImportPr.prBody,\n catalogInfoContent: openImportPr.prCatalogInfoContent,\n },\n };\n result.lastUpdate = openImportPr.lastUpdate;\n } catch (error: any) {\n errors.push(error.message);\n result.errors = errors;\n if (error.message?.includes('Not Found')) {\n return {\n statusCode: 404,\n responseBody: result,\n };\n }\n result.status = 'PR_ERROR';\n }\n\n return {\n statusCode: 200,\n responseBody: result,\n };\n}\n\nexport async function deleteImportByRepo(\n deps: {\n logger: LoggerService;\n config: Config;\n githubApiService: GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n },\n repoUrl: string,\n defaultBranch?: string,\n): Promise<HandlerResponse<void>> {\n deps.logger.debug(`Deleting bulk import job status for ${repoUrl}..`);\n\n // Check to see if there are any PR\n const openImportPr = await deps.githubApiService.findImportOpenPr(\n deps.logger,\n {\n repoUrl: repoUrl,\n },\n );\n const gitUrl = gitUrlParse(repoUrl);\n if (openImportPr.prUrl) {\n // Close PR\n const appTitle =\n deps.config.getOptionalString('app.title') ?? 'Red Hat Developer Hub';\n const appBaseUrl = deps.config.getString('app.baseUrl');\n await deps.githubApiService.closeImportPR(deps.logger, {\n repoUrl,\n gitUrl,\n comment: `Closing PR upon request for bulk import deletion. This request was created from [${appTitle}](${appBaseUrl}).`,\n });\n }\n // Also delete the import branch, so that it is not outdated if we try later to import the repo again\n await deps.githubApiService.deleteImportBranch({\n repoUrl,\n gitUrl,\n });\n // Remove Location from catalog\n const catalogUrl = getCatalogUrl(deps.config, repoUrl, defaultBranch);\n const findLocationFrom = (list: { id?: string; target: string }[]) => {\n for (const loc of list) {\n if (loc.target === catalogUrl) {\n return loc.id;\n }\n }\n return undefined;\n };\n\n const locationId = findLocationFrom(\n (\n await deps.catalogHttpClient.listCatalogUrlLocationsByIdFromLocationsEndpoint()\n ).locations,\n );\n if (locationId) {\n await deps.catalogHttpClient.deleteCatalogLocationById(locationId);\n }\n\n return {\n statusCode: 204,\n responseBody: undefined,\n };\n}\n"],"names":["DefaultPageNumber","DefaultPageSize","getCatalogFilename","paginateArray","logErrorIfNeeded","getCatalogUrl","gitUrlParse"],"mappings":"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"}