@red-hat-developer-hub/backstage-plugin-bulk-import-backend 6.2.0 → 6.4.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 (56) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +28 -0
  3. package/config.d.ts +9 -1
  4. package/dist/catalog/catalogHttpClient.cjs.js +3 -0
  5. package/dist/catalog/catalogHttpClient.cjs.js.map +1 -1
  6. package/dist/catalog/catalogInfoGenerator.cjs.js +1 -0
  7. package/dist/catalog/catalogInfoGenerator.cjs.js.map +1 -1
  8. package/dist/database/migration.cjs.js +20 -0
  9. package/dist/database/migration.cjs.js.map +1 -0
  10. package/dist/database/repositoryDao.cjs.js +117 -0
  11. package/dist/database/repositoryDao.cjs.js.map +1 -0
  12. package/dist/generated/openapidocument.cjs.js +366 -38
  13. package/dist/generated/openapidocument.cjs.js.map +1 -1
  14. package/dist/github/githubApiService.cjs.js +222 -95
  15. package/dist/github/githubApiService.cjs.js.map +1 -1
  16. package/dist/github/utils/orgUtils.cjs.js +2 -0
  17. package/dist/github/utils/orgUtils.cjs.js.map +1 -1
  18. package/dist/github/utils/prUtils.cjs.js +3 -0
  19. package/dist/github/utils/prUtils.cjs.js.map +1 -1
  20. package/dist/github/utils/repoUtils.cjs.js +1 -38
  21. package/dist/github/utils/repoUtils.cjs.js.map +1 -1
  22. package/dist/github/utils/utils.cjs.js +29 -18
  23. package/dist/github/utils/utils.cjs.js.map +1 -1
  24. package/dist/gitlab/GitlabAppManager.cjs.js +54 -0
  25. package/dist/gitlab/GitlabAppManager.cjs.js.map +1 -0
  26. package/dist/gitlab/gitlabApiService.cjs.js +660 -0
  27. package/dist/gitlab/gitlabApiService.cjs.js.map +1 -0
  28. package/dist/gitlab/utils/glUtils.cjs.js +15 -0
  29. package/dist/gitlab/utils/glUtils.cjs.js.map +1 -0
  30. package/dist/gitlab/utils/orgUtils.cjs.js +72 -0
  31. package/dist/gitlab/utils/orgUtils.cjs.js.map +1 -0
  32. package/dist/gitlab/utils/prUtils.cjs.js +73 -0
  33. package/dist/gitlab/utils/prUtils.cjs.js.map +1 -0
  34. package/dist/gitlab/utils/repoUtils.cjs.js +200 -0
  35. package/dist/gitlab/utils/repoUtils.cjs.js.map +1 -0
  36. package/dist/gitlab/utils/utils.cjs.js +131 -0
  37. package/dist/gitlab/utils/utils.cjs.js.map +1 -0
  38. package/dist/helpers/utils.cjs.js +58 -0
  39. package/dist/helpers/utils.cjs.js.map +1 -1
  40. package/dist/index.d.ts +2 -1
  41. package/dist/plugin.cjs.js +6 -3
  42. package/dist/plugin.cjs.js.map +1 -1
  43. package/dist/service/handlers/import/bulkImports.cjs.js +216 -34
  44. package/dist/service/handlers/import/bulkImports.cjs.js.map +1 -1
  45. package/dist/service/handlers/import/execute-template.cjs.js +264 -0
  46. package/dist/service/handlers/import/execute-template.cjs.js.map +1 -0
  47. package/dist/service/handlers/import/importStatus.cjs.js +5 -8
  48. package/dist/service/handlers/import/importStatus.cjs.js.map +1 -1
  49. package/dist/service/handlers/organization/organizations.cjs.js +2 -2
  50. package/dist/service/handlers/organization/organizations.cjs.js.map +1 -1
  51. package/dist/service/handlers/repository/repositories.cjs.js +5 -2
  52. package/dist/service/handlers/repository/repositories.cjs.js.map +1 -1
  53. package/dist/service/router.cjs.js +199 -34
  54. package/dist/service/router.cjs.js.map +1 -1
  55. package/migrations/20250808122501_migrations.js +53 -0
  56. package/package.json +17 -11
@@ -45,6 +45,7 @@ async function findAllImports(deps, requestHeaders, queryParams) {
45
45
  )).uniqueCatalogUrlLocations;
46
46
  const defaultBranchByRepoUrl = await resolveReposDefaultBranches(
47
47
  deps.logger,
48
+ deps.gitlabApiService,
48
49
  deps.githubApiService,
49
50
  allLocations.keys(),
50
51
  catalogFilename
@@ -55,19 +56,45 @@ async function findAllImports(deps, requestHeaders, queryParams) {
55
56
  catalogFilename
56
57
  );
57
58
  const importsReachableFromGHIntegrations = await deps.githubApiService.filterLocationsAccessibleFromIntegrations(
58
- importCandidates
59
+ importCandidates.filter((val) => {
60
+ return utils.parseGitURLForApprovalTool(val) === "GIT";
61
+ })
62
+ );
63
+ const importsReachableFromGLIntegrations = await deps.gitlabApiService.filterLocationsAccessibleFromIntegrations(
64
+ importCandidates.filter((val) => {
65
+ return utils.parseGitURLForApprovalTool(val) === "GITLAB";
66
+ })
67
+ );
68
+ const mergedReachbleFromIntegrations = importsReachableFromGHIntegrations.map((val) => {
69
+ return {
70
+ loc: val,
71
+ approvalTool: "GIT"
72
+ };
73
+ }).concat(
74
+ importsReachableFromGLIntegrations.map((val) => {
75
+ return {
76
+ loc: val,
77
+ approvalTool: "GITLAB"
78
+ };
79
+ })
59
80
  );
60
81
  const repoUrlToLocation = /* @__PURE__ */ new Map();
61
82
  const importStatusPromises = [];
62
- for (const loc of importsReachableFromGHIntegrations) {
63
- const repoUrl = repoUrlFromLocation(loc);
83
+ for (const imports2 of mergedReachbleFromIntegrations) {
84
+ const repoUrl = repoUrlFromLocation(imports2.loc);
64
85
  if (!repoUrl) {
65
86
  continue;
66
87
  }
67
- repoUrlToLocation.set(repoUrl, loc);
88
+ repoUrlToLocation.set(repoUrl, imports2.loc);
68
89
  importStatusPromises.push(
69
90
  findImportStatusByRepo(
70
- deps,
91
+ {
92
+ logger: deps.logger,
93
+ config: deps.config,
94
+ gitApiService: imports2.approvalTool === "GITLAB" ? deps.gitlabApiService : deps.githubApiService,
95
+ catalogHttpClient: deps.catalogHttpClient,
96
+ approvalTool: imports2.approvalTool
97
+ },
71
98
  repoUrl,
72
99
  defaultBranchByRepoUrl.get(repoUrl),
73
100
  false
@@ -101,7 +128,7 @@ async function findAllImports(deps, requestHeaders, queryParams) {
101
128
  }
102
129
  };
103
130
  }
104
- async function resolveReposDefaultBranches(logger, githubApiService, allLocations, catalogFilename) {
131
+ async function resolveReposDefaultBranches(logger, gitlabApiService, githubApiService, allLocations, catalogFilename) {
105
132
  const defaultBranchByRepoUrlPromises = [];
106
133
  for (const loc of allLocations) {
107
134
  if (!loc.endsWith(catalogFilename)) {
@@ -115,7 +142,7 @@ async function resolveReposDefaultBranches(logger, githubApiService, allLocation
115
142
  continue;
116
143
  }
117
144
  defaultBranchByRepoUrlPromises.push(
118
- githubApiService.getRepositoryFromIntegrations(repoUrl).then((resp) => {
145
+ (utils.parseGitURLForApprovalTool(repoUrl) === "GITLAB" ? gitlabApiService : githubApiService).getRepositoryFromIntegrations(repoUrl).then((resp) => {
119
146
  return { repoUrl, defaultBranch: resp?.repository?.default_branch };
120
147
  }).catch((err) => {
121
148
  loggingUtils.logErrorIfNeeded(
@@ -162,19 +189,19 @@ function findImportCandidates(allLocations, defaultBranchByRepoUrl, catalogFilen
162
189
  }
163
190
  return filteredLocations;
164
191
  }
165
- async function createPR(githubApiService, logger, req, gitUrl, catalogInfoGenerator, config) {
192
+ async function createPR(gitApiService, logger, req, gitUrl, catalogInfoGenerator, config) {
166
193
  const appTitle = config.getOptionalString("app.title") ?? "Red Hat Developer Hub";
167
194
  const appBaseUrl = config.getString("app.baseUrl");
168
195
  const catalogFileName = catalogUtils.getCatalogFilename(config);
169
- return await githubApiService.submitPrToRepo(logger, {
196
+ return await gitApiService.submitPrToRepo(logger, {
170
197
  repoUrl: req.repository.url,
171
198
  gitUrl,
172
199
  defaultBranch: req.repository.defaultBranch,
173
200
  catalogInfoContent: req.catalogInfoContent ?? await catalogInfoGenerator.generateDefaultCatalogInfoContent(
174
201
  req.repository.url
175
202
  ),
176
- prTitle: req.github?.pullRequest?.title ?? `Add ${catalogFileName}`,
177
- prBody: req.github?.pullRequest?.body ?? `
203
+ prTitle: req[req.approvalTool === "GITLAB" ? "gitlab" : "github"]?.pullRequest?.title ?? `Add ${catalogFileName}`,
204
+ prBody: req[req.approvalTool === "GITLAB" ? "gitlab" : "github"]?.pullRequest?.body ?? `
178
205
  This pull request adds a **Backstage entity metadata file** to this repository so that the component can be added to a Backstage application.
179
206
 
180
207
  After this pull request is merged, the component will become available in the [${appTitle} software catalog](${appBaseUrl}).
@@ -195,7 +222,8 @@ async function handleAddedReposFromCreateImportJobs(deps, importRequests) {
195
222
  if (!hasLocation) {
196
223
  continue;
197
224
  }
198
- const hasCatalogInfoFileInRepo = await deps.githubApiService.hasFileInRepo({
225
+ const gitApiService = req.approvalTool === "GITLAB" ? deps.gitlabApiService : deps.githubApiService;
226
+ const hasCatalogInfoFileInRepo = await gitApiService.hasFileInRepo({
199
227
  repoUrl: req.repository.url,
200
228
  defaultBranch: req.repository.defaultBranch,
201
229
  fileName: catalogUtils.getCatalogFilename(deps.config)
@@ -203,7 +231,7 @@ async function handleAddedReposFromCreateImportJobs(deps, importRequests) {
203
231
  if (!hasCatalogInfoFileInRepo) {
204
232
  continue;
205
233
  }
206
- const ghRepo = await deps.githubApiService.getRepositoryFromIntegrations(
234
+ const ghRepo = await gitApiService.getRepositoryFromIntegrations(
207
235
  req.repository.url
208
236
  );
209
237
  await deps.catalogHttpClient.refreshLocationByRepoUrl(
@@ -229,8 +257,9 @@ async function handlePrCreationRequest(deps, req, gitUrl) {
229
257
  req.repository.url,
230
258
  req.repository.defaultBranch
231
259
  );
260
+ const gitApiService = req.approvalTool === "GITLAB" ? deps.gitlabApiService : deps.githubApiService;
232
261
  const prToRepo = await createPR(
233
- deps.githubApiService,
262
+ gitApiService,
234
263
  deps.logger,
235
264
  req,
236
265
  gitUrl,
@@ -267,6 +296,7 @@ async function handlePrCreationRequest(deps, req, gitUrl) {
267
296
  };
268
297
  }
269
298
  return {
299
+ approvalTool: req.approvalTool ?? "GIT",
270
300
  errors: prToRepo.errors,
271
301
  status: "WAIT_PR_APPROVAL",
272
302
  lastUpdate: prToRepo.lastUpdate,
@@ -275,7 +305,7 @@ async function handlePrCreationRequest(deps, req, gitUrl) {
275
305
  name: gitUrl.name,
276
306
  organization: gitUrl.organization
277
307
  },
278
- github: {
308
+ [req.approvalTool === "GITLAB" ? "gitlab" : "github"]: {
279
309
  pullRequest: {
280
310
  url: prToRepo.prUrl,
281
311
  number: prToRepo.prNumber
@@ -366,8 +396,9 @@ async function performDryRunChecks(deps, req) {
366
396
  }
367
397
  return {};
368
398
  };
399
+ const gitApiService = req.approvalTool === "GITLAB" ? deps.gitlabApiService : deps.githubApiService;
369
400
  const checkEmptyRepo = async () => {
370
- const empty = await deps.githubApiService.isRepoEmpty({
401
+ const empty = await gitApiService.isRepoEmpty({
371
402
  repoUrl: req.repository.url
372
403
  });
373
404
  if (empty) {
@@ -378,7 +409,7 @@ async function performDryRunChecks(deps, req) {
378
409
  return {};
379
410
  };
380
411
  const checkCatalogInfoPresenceInRepo = async () => {
381
- const exists = await deps.githubApiService.hasFileInRepo({
412
+ const exists = await gitApiService.hasFileInRepo({
382
413
  repoUrl: req.repository.url,
383
414
  defaultBranch: req.repository.defaultBranch,
384
415
  fileName: catalogUtils.getCatalogFilename(deps.config)
@@ -391,10 +422,11 @@ async function performDryRunChecks(deps, req) {
391
422
  return {};
392
423
  };
393
424
  const checkCodeOwnersFileInRepo = async () => {
394
- const exists = await deps.githubApiService.hasFileInRepo({
425
+ const gitDirLocation = req.approvalTool === "GITLAB" ? ".gitlab" : ".github";
426
+ const exists = await gitApiService.hasFileInRepo({
395
427
  repoUrl: req.repository.url,
396
428
  defaultBranch: req.repository.defaultBranch,
397
- fileName: ".github/CODEOWNERS"
429
+ fileName: `${gitDirLocation}/CODEOWNERS`
398
430
  });
399
431
  if (!exists) {
400
432
  return {
@@ -440,18 +472,18 @@ async function findImportStatusByRepo(deps, repoUrl, defaultBranch, includeCatal
440
472
  id: `${gitUrl.organization}/${gitUrl.name}`,
441
473
  defaultBranch
442
474
  },
443
- approvalTool: "GIT",
475
+ approvalTool: deps.approvalTool,
444
476
  status: null
445
477
  };
446
478
  try {
447
- const openImportPr = await deps.githubApiService.findImportOpenPr(
479
+ const openImportPr = await deps.gitApiService.findImportOpenPr(
448
480
  deps.logger,
449
481
  {
450
482
  repoUrl,
451
483
  includeCatalogInfoContent
452
484
  }
453
485
  );
454
- if (!openImportPr.prUrl) {
486
+ if (!openImportPr?.prUrl) {
455
487
  const catalogLocations = (await deps.catalogHttpClient.listCatalogUrlLocations()).uniqueCatalogUrlLocations.keys();
456
488
  const catalogUrl = catalogUtils.getCatalogUrl(deps.config, repoUrl, defaultBranch);
457
489
  let exists = false;
@@ -461,7 +493,7 @@ async function findImportStatusByRepo(deps, repoUrl, defaultBranch, includeCatal
461
493
  break;
462
494
  }
463
495
  }
464
- if (exists && await deps.githubApiService.hasFileInRepo({
496
+ if (exists && await deps.gitApiService.hasFileInRepo({
465
497
  repoUrl,
466
498
  defaultBranch,
467
499
  fileName: catalogUtils.getCatalogFilename(deps.config)
@@ -472,7 +504,7 @@ async function findImportStatusByRepo(deps, repoUrl, defaultBranch, includeCatal
472
504
  defaultBranch
473
505
  );
474
506
  }
475
- const ghRepo = await deps.githubApiService.getRepositoryFromIntegrations(repoUrl);
507
+ const ghRepo = await deps.gitApiService.getRepositoryFromIntegrations(repoUrl);
476
508
  result.lastUpdate = ghRepo.repository?.updated_at ?? void 0;
477
509
  return {
478
510
  statusCode: 200,
@@ -480,7 +512,7 @@ async function findImportStatusByRepo(deps, repoUrl, defaultBranch, includeCatal
480
512
  };
481
513
  }
482
514
  result.status = "WAIT_PR_APPROVAL";
483
- result.github = {
515
+ result[deps.approvalTool === "GITLAB" ? "gitlab" : "github"] = {
484
516
  pullRequest: {
485
517
  number: openImportPr.prNum,
486
518
  url: openImportPr.prUrl,
@@ -506,25 +538,154 @@ async function findImportStatusByRepo(deps, repoUrl, defaultBranch, includeCatal
506
538
  responseBody: result
507
539
  };
508
540
  }
541
+ async function findTaskImportStatusByRepo(deps, repoUrl, skipTasks) {
542
+ deps.logger.debug(`Getting bulk import job status for ${repoUrl}..`);
543
+ const gitUrl = gitUrlParse__default.default(repoUrl);
544
+ const errors = [];
545
+ const result = {
546
+ id: repoUrl,
547
+ repository: {
548
+ url: repoUrl,
549
+ name: gitUrl.name,
550
+ organization: gitUrl.organization,
551
+ id: `${gitUrl.organization}/${gitUrl.name}`
552
+ },
553
+ approvalTool: "GIT"
554
+ };
555
+ try {
556
+ const repository = await deps.repositoryDao.findRepositoryByUrl(repoUrl);
557
+ if (repository?.id) {
558
+ const task = await deps.taskDao.lastExecutedTaskByRepoId(repository?.id);
559
+ if (!task) {
560
+ throw new Error(
561
+ `Unable to find scaffolder task for repository: ${repository?.id}`
562
+ );
563
+ }
564
+ const scaffolderUrl = await deps.discovery.getBaseUrl("scaffolder");
565
+ const { token } = await deps.auth.getPluginRequestToken({
566
+ onBehalfOf: await deps.auth.getOwnServiceCredentials(),
567
+ targetPluginId: "scaffolder"
568
+ });
569
+ const response = await fetch(`${scaffolderUrl}/v2/tasks/${task.taskId}`, {
570
+ headers: {
571
+ Authorization: `Bearer ${token}`
572
+ }
573
+ });
574
+ if (!skipTasks) {
575
+ const tasks = await deps.taskDao.findTasksByRepositoryId(repository.id);
576
+ const tasksWithLocations = await Promise.all(
577
+ tasks.map(async (taskItem) => {
578
+ const locations = await deps.taskLocationsDao.findLocationsByTaskId(
579
+ taskItem.taskId
580
+ );
581
+ const taskLocations = locations.map((location) => location.location);
582
+ return {
583
+ ...taskItem,
584
+ locations: taskLocations
585
+ };
586
+ })
587
+ );
588
+ result.tasks = tasksWithLocations;
589
+ }
590
+ result.task = { taskId: task.taskId };
591
+ let data;
592
+ if (response.ok) {
593
+ data = await response.json();
594
+ result.lastUpdate = data.lastHeartbeatAt;
595
+ const approvalTool = repository.approvalTool;
596
+ const pullRequest = await parsePullOrMergeRequestInfo(
597
+ data.state?.checkpoints,
598
+ deps.gitlabApiService,
599
+ deps.githubApiService,
600
+ approvalTool,
601
+ deps.logger,
602
+ repoUrl
603
+ );
604
+ if (pullRequest && approvalTool === "GITLAB") {
605
+ result.gitlab = { pullRequest };
606
+ }
607
+ if (pullRequest && approvalTool === "GIT") {
608
+ result.github = { pullRequest };
609
+ }
610
+ result.status = `TASK_${data.status?.toLocaleUpperCase()}`;
611
+ } else {
612
+ const errMsg = await response.text() ?? `Failed to fetch task ${task.taskId}. Response status: ${response.status}`;
613
+ throw new Error(errMsg);
614
+ }
615
+ }
616
+ } catch (error) {
617
+ errors.push(error.message);
618
+ result.errors = errors;
619
+ result.status = "TASK_FETCH_FAILED";
620
+ if (error.message?.includes("Not Found")) {
621
+ return {
622
+ statusCode: 404,
623
+ responseBody: result
624
+ };
625
+ }
626
+ }
627
+ return {
628
+ statusCode: 200,
629
+ responseBody: result
630
+ };
631
+ }
632
+ async function parsePullOrMergeRequestInfo(checkpoints, githubApiService, gitlabApiService, approvalTool, logger, repoUrl) {
633
+ if (approvalTool !== "GITLAB" && approvalTool !== "GIT") {
634
+ return undefined;
635
+ }
636
+ const gitApiService = approvalTool === "GITLAB" ? gitlabApiService : githubApiService;
637
+ for (const key in checkpoints) {
638
+ if (!checkpoints.hasOwnProperty(key)) {
639
+ continue;
640
+ }
641
+ try {
642
+ if (key.startsWith("v1.task.checkpoint.publish.create.pr") || key.startsWith("v1.task.checkpoint.publish.create.mr")) {
643
+ const url = checkpoints[key]?.value?.html_url ?? checkpoints[key]?.value?.mrWebUrl;
644
+ if (!url) {
645
+ continue;
646
+ }
647
+ const prNumber = Number.parseInt(url.split("/").pop(), 10);
648
+ const prDetails = await gitApiService.getPullRequest(repoUrl, prNumber);
649
+ let catalogInfoContent;
650
+ if (prDetails.prSha) {
651
+ catalogInfoContent = await gitApiService.getCatalogInfoFile(logger, {
652
+ repoUrl,
653
+ prHeadSha: prDetails.prSha,
654
+ prNumber
655
+ });
656
+ }
657
+ return {
658
+ url,
659
+ number: prNumber,
660
+ title: prDetails.title,
661
+ body: prDetails.body,
662
+ status: prDetails.merged ? "PR_MERGED" : "WAIT_PR_APPROVAL",
663
+ catalogInfoContent
664
+ };
665
+ }
666
+ } catch (err) {
667
+ logger.warn(`Error while processing checkpoint ${key}: ${err}`);
668
+ continue;
669
+ }
670
+ }
671
+ return undefined;
672
+ }
509
673
  async function deleteImportByRepo(deps, repoUrl, defaultBranch) {
510
674
  deps.logger.debug(`Deleting bulk import job status for ${repoUrl}..`);
511
- const openImportPr = await deps.githubApiService.findImportOpenPr(
512
- deps.logger,
513
- {
514
- repoUrl
515
- }
516
- );
675
+ const openImportPr = await deps.gitApiService.findImportOpenPr(deps.logger, {
676
+ repoUrl
677
+ });
517
678
  const gitUrl = gitUrlParse__default.default(repoUrl);
518
- if (openImportPr.prUrl) {
679
+ if (openImportPr?.prUrl) {
519
680
  const appTitle = deps.config.getOptionalString("app.title") ?? "Red Hat Developer Hub";
520
681
  const appBaseUrl = deps.config.getString("app.baseUrl");
521
- await deps.githubApiService.closeImportPR(deps.logger, {
682
+ await deps.gitApiService.closeImportPR(deps.logger, {
522
683
  repoUrl,
523
684
  gitUrl,
524
685
  comment: `Closing PR upon request for bulk import deletion. This request was created from [${appTitle}](${appBaseUrl}).`
525
686
  });
526
687
  }
527
- await deps.githubApiService.deleteImportBranch({
688
+ await deps.gitApiService.deleteImportBranch({
528
689
  repoUrl,
529
690
  gitUrl
530
691
  });
@@ -548,9 +709,30 @@ async function deleteImportByRepo(deps, repoUrl, defaultBranch) {
548
709
  responseBody: undefined
549
710
  };
550
711
  }
712
+ async function deleteTaskImportByRepo(deps, repoUrl) {
713
+ deps.logger.debug(`Deleting repository from database by name ${repoUrl}...`);
714
+ try {
715
+ await deps.dao.deleteRepository(repoUrl);
716
+ return {
717
+ statusCode: 204,
718
+ responseBody: void 0
719
+ };
720
+ } catch (error) {
721
+ deps.logger.error(
722
+ `Failed to delete repository from database by url ${repoUrl}`,
723
+ error
724
+ );
725
+ return {
726
+ statusCode: 500
727
+ };
728
+ }
729
+ }
551
730
 
552
731
  exports.createImportJobs = createImportJobs;
553
732
  exports.deleteImportByRepo = deleteImportByRepo;
733
+ exports.deleteTaskImportByRepo = deleteTaskImportByRepo;
554
734
  exports.findAllImports = findAllImports;
555
735
  exports.findImportStatusByRepo = findImportStatusByRepo;
736
+ exports.findTaskImportStatusByRepo = findTaskImportStatusByRepo;
737
+ exports.sortImports = sortImports;
556
738
  //# sourceMappingURL=bulkImports.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bulkImports.cjs.js","sources":["../../../../src/service/handlers/import/bulkImports.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 {\n getNestedValue,\n logErrorIfNeeded,\n paginateArray,\n} from '../../../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n DefaultSortColumn,\n DefaultSortOrder,\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(\n imports: Components.Schemas.Import[],\n sortColumn: Components.Parameters.SortColumnQueryParam = DefaultSortColumn,\n sortOrder: Components.Parameters.SortOrderQueryParam = DefaultSortOrder,\n) {\n imports.sort((a, b) => {\n const value1 = getNestedValue(a, sortColumn);\n const value2 = getNestedValue(b, sortColumn);\n // Handle cases where values are undefined\n if (value1 === undefined && value2 === undefined) return 0;\n if (value1 === undefined) return sortOrder === 'asc' ? -1 : 1;\n if (value2 === undefined) return sortOrder === 'asc' ? 1 : -1;\n\n if (sortColumn === 'lastUpdate') {\n const date1 = new Date(value1); // Convert string to Date object\n const date2 = new Date(value2); // Convert string to Date object\n // Compare dates\n return sortOrder === 'asc'\n ? date2.getTime() - date1.getTime()\n : date1.getTime() - date2.getTime();\n }\n // Compare values based on sort order\n return sortOrder === 'asc'\n ? value1.localeCompare(value2)\n : value2.localeCompare(value1);\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 sortColumn?: Components.Parameters.SortColumnQueryParam;\n sortOrder?: Components.Parameters.SortOrderQueryParam;\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 const sortColumn = queryParams?.sortColumn ?? DefaultSortColumn;\n const sortOrder = queryParams?.sortOrder ?? DefaultSortOrder;\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\n sortImports(imports, sortColumn, sortOrder);\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 =\n await deps.catalogHttpClient.verifyLocationExistence(repoCatalogUrl);\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 =\n await deps.catalogHttpClient.hasEntityInCatalog(catalogEntityName);\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 =\n await deps.githubApiService.getRepositoryFromIntegrations(repoUrl);\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":["DefaultSortColumn","DefaultSortOrder","getNestedValue","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 Red Hat, Inc.\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 DiscoveryService,\n LoggerService,\n} 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 {\n RepositoryDao,\n ScaffolderTaskDao,\n TaskLocationsDao,\n} from '../../../database/repositoryDao';\nimport type { Components, Paths } from '../../../generated/openapi';\nimport type { GithubApiService } from '../../../github';\nimport { GitlabApiService } from '../../../gitlab';\nimport {\n getNestedValue,\n logErrorIfNeeded,\n paginateArray,\n parseGitURLForApprovalTool,\n} from '../../../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n DefaultSortColumn,\n DefaultSortOrder,\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\nexport function sortImports(\n imports: Components.Schemas.Import[],\n sortColumn: Components.Parameters.SortColumnQueryParam = DefaultSortColumn,\n sortOrder: Components.Parameters.SortOrderQueryParam = DefaultSortOrder,\n) {\n imports.sort((a, b) => {\n const value1 = getNestedValue(a, sortColumn);\n const value2 = getNestedValue(b, sortColumn);\n // Handle cases where values are undefined\n if (value1 === undefined && value2 === undefined) return 0;\n if (value1 === undefined) return sortOrder === 'asc' ? -1 : 1;\n if (value2 === undefined) return sortOrder === 'asc' ? 1 : -1;\n\n if (sortColumn === 'lastUpdate') {\n const date1 = new Date(value1); // Convert string to Date object\n const date2 = new Date(value2); // Convert string to Date object\n // Compare dates\n return sortOrder === 'asc'\n ? date2.getTime() - date1.getTime()\n : date1.getTime() - date2.getTime();\n }\n // Compare values based on sort order\n return sortOrder === 'asc'\n ? value1.localeCompare(value2)\n : value2.localeCompare(value1);\n });\n}\n\nexport async function findAllImports(\n deps: {\n logger: LoggerService;\n config: Config;\n gitlabApiService: GitlabApiService;\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 sortColumn?: Components.Parameters.SortColumnQueryParam;\n sortOrder?: Components.Parameters.SortOrderQueryParam;\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 const sortColumn = queryParams?.sortColumn ?? DefaultSortColumn;\n const sortOrder = queryParams?.sortOrder ?? DefaultSortOrder;\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.gitlabApiService,\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/GL integrations\n const importsReachableFromGHIntegrations =\n await deps.githubApiService.filterLocationsAccessibleFromIntegrations(\n importCandidates.filter(val => {\n return parseGitURLForApprovalTool(val) === 'GIT';\n }),\n );\n\n const importsReachableFromGLIntegrations =\n await deps.gitlabApiService.filterLocationsAccessibleFromIntegrations(\n importCandidates.filter(val => {\n return parseGitURLForApprovalTool(val) === 'GITLAB';\n }),\n );\n\n // Merge the two lists together and map the appropriate approvalTool\n const mergedReachbleFromIntegrations = importsReachableFromGHIntegrations\n .map(val => {\n return {\n loc: val,\n approvalTool: 'GIT',\n };\n })\n .concat(\n importsReachableFromGLIntegrations.map(val => {\n return {\n loc: val,\n approvalTool: 'GITLAB',\n };\n }),\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 imports of mergedReachbleFromIntegrations) {\n const repoUrl = repoUrlFromLocation(imports.loc);\n if (!repoUrl) {\n continue;\n }\n repoUrlToLocation.set(repoUrl, imports.loc);\n\n importStatusPromises.push(\n findImportStatusByRepo(\n {\n logger: deps.logger,\n config: deps.config,\n gitApiService:\n imports.approvalTool === 'GITLAB'\n ? deps.gitlabApiService\n : deps.githubApiService,\n catalogHttpClient: deps.catalogHttpClient,\n approvalTool: imports.approvalTool,\n },\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\n sortImports(imports, sortColumn, sortOrder);\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 gitlabApiService: GitlabApiService,\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 // Have to do this for Both the gitlab/github, possibly a better way?\n // Should probably parse the URL to figure out which to use?\n defaultBranchByRepoUrlPromises.push(\n (parseGitURLForApprovalTool(repoUrl) === 'GITLAB'\n ? gitlabApiService\n : githubApiService\n )\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 gitApiService: GithubApiService | GitlabApiService,\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 gitApiService.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:\n req[req.approvalTool === 'GITLAB' ? 'gitlab' : 'github']?.pullRequest\n ?.title ?? `Add ${catalogFileName}`,\n prBody:\n req[req.approvalTool === 'GITLAB' ? 'gitlab' : 'github']?.pullRequest\n ?.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 gitlabApiService: GitlabApiService;\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 =\n await deps.catalogHttpClient.verifyLocationExistence(repoCatalogUrl);\n if (!hasLocation) {\n continue;\n }\n const gitApiService =\n req.approvalTool === 'GITLAB'\n ? deps.gitlabApiService\n : deps.githubApiService;\n const hasCatalogInfoFileInRepo = await gitApiService.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 gitApiService.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 gitlabApiService: GitlabApiService;\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 gitApiService =\n req.approvalTool === 'GITLAB'\n ? deps.gitlabApiService\n : deps.githubApiService;\n const prToRepo = await createPR(\n gitApiService,\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 approvalTool: req.approvalTool ?? 'GIT',\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 [req.approvalTool === 'GITLAB' ? 'gitlab' : '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 gitlabApiService: GitlabApiService;\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 gitlabApiService: GitlabApiService;\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 gitlabApiService: GitlabApiService;\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 =\n await deps.catalogHttpClient.hasEntityInCatalog(catalogEntityName);\n if (hasEntity) {\n return { dryRunStatuses: ['CATALOG_ENTITY_CONFLICT'] };\n }\n return {};\n };\n\n const gitApiService =\n req.approvalTool === 'GITLAB'\n ? deps.gitlabApiService\n : deps.githubApiService;\n const checkEmptyRepo = async (): Promise<{\n dryRunStatuses?: CreateImportDryRunStatus[];\n errors?: string[];\n }> => {\n const empty = await gitApiService.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 gitApiService.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 gitDirLocation =\n req.approvalTool === 'GITLAB' ? '.gitlab' : '.github';\n const exists = await gitApiService.hasFileInRepo({\n repoUrl: req.repository.url,\n defaultBranch: req.repository.defaultBranch,\n fileName: `${gitDirLocation}/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 gitApiService: GitlabApiService | GithubApiService;\n catalogHttpClient: CatalogHttpClient;\n approvalTool: string | undefined;\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: deps.approvalTool,\n status: null,\n } as Components.Schemas.Import;\n try {\n // Check to see if there are any PR\n const openImportPr = await deps.gitApiService.findImportOpenPr(\n deps.logger,\n {\n repoUrl: repoUrl,\n includeCatalogInfoContent,\n },\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.gitApiService.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 =\n await deps.gitApiService.getRepositoryFromIntegrations(repoUrl);\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[deps.approvalTool === 'GITLAB' ? 'gitlab' : '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\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 findTaskImportStatusByRepo(\n deps: {\n logger: LoggerService;\n config: Config;\n githubApiService: GithubApiService;\n gitlabApiService: GitlabApiService;\n catalogHttpClient: CatalogHttpClient;\n repositoryDao: RepositoryDao;\n taskDao: ScaffolderTaskDao;\n taskLocationsDao: TaskLocationsDao;\n discovery: DiscoveryService;\n auth: AuthService;\n },\n repoUrl: string,\n skipTasks?: 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: Components.Schemas.Import = {\n id: repoUrl,\n repository: {\n url: repoUrl,\n name: gitUrl.name,\n organization: gitUrl.organization,\n id: `${gitUrl.organization}/${gitUrl.name}`,\n },\n approvalTool: 'GIT',\n };\n try {\n const repository = await deps.repositoryDao.findRepositoryByUrl(repoUrl);\n if (repository?.id) {\n const task = await deps.taskDao.lastExecutedTaskByRepoId(repository?.id);\n if (!task) {\n throw new Error(\n `Unable to find scaffolder task for repository: ${repository?.id}`,\n );\n }\n\n const scaffolderUrl = await deps.discovery.getBaseUrl('scaffolder');\n const { token } = await deps.auth.getPluginRequestToken({\n onBehalfOf: await deps.auth.getOwnServiceCredentials(),\n targetPluginId: 'scaffolder',\n });\n\n const response = await fetch(`${scaffolderUrl}/v2/tasks/${task.taskId}`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n if (!skipTasks) {\n const tasks = await deps.taskDao.findTasksByRepositoryId(repository.id);\n const tasksWithLocations = await Promise.all(\n tasks.map(async taskItem => {\n const locations = await deps.taskLocationsDao.findLocationsByTaskId(\n taskItem.taskId,\n );\n const taskLocations = locations.map(location => location.location);\n return {\n ...taskItem,\n locations: taskLocations,\n };\n }),\n );\n result.tasks = tasksWithLocations;\n }\n\n result.task = { taskId: task.taskId };\n let data;\n if (response.ok) {\n data = await response.json();\n result.lastUpdate = data.lastHeartbeatAt;\n\n const approvalTool =\n repository.approvalTool as unknown as Components.Schemas.ApprovalTool;\n const pullRequest = await parsePullOrMergeRequestInfo(\n data.state?.checkpoints,\n deps.gitlabApiService,\n deps.githubApiService,\n approvalTool,\n deps.logger,\n repoUrl,\n );\n if (pullRequest && approvalTool === 'GITLAB') {\n result.gitlab = { pullRequest };\n }\n if (pullRequest && approvalTool === 'GIT') {\n result.github = { pullRequest };\n }\n\n result.status =\n `TASK_${(data.status as string)?.toLocaleUpperCase()}` as Components.Schemas.TaskImportStatus;\n } else {\n const errMsg =\n (await response.text()) ??\n `Failed to fetch task ${task.taskId}. Response status: ${response.status}`;\n throw new Error(errMsg);\n }\n }\n } catch (error: any) {\n errors.push(error.message);\n result.errors = errors;\n result.status = 'TASK_FETCH_FAILED';\n if (error.message?.includes('Not Found')) {\n return {\n statusCode: 404,\n responseBody: result,\n };\n }\n }\n\n return {\n statusCode: 200,\n responseBody: result,\n };\n}\n\nasync function parsePullOrMergeRequestInfo(\n checkpoints: Record<string, any>,\n githubApiService: GitlabApiService,\n gitlabApiService: GithubApiService,\n approvalTool: Components.Schemas.ApprovalTool,\n logger: LoggerService,\n repoUrl: string,\n): Promise<Components.Schemas.PullRequest | undefined> {\n // return errors ?\n if (approvalTool !== 'GITLAB' && approvalTool !== 'GIT') {\n return undefined;\n }\n const gitApiService =\n approvalTool === 'GITLAB' ? gitlabApiService : githubApiService;\n\n for (const key in checkpoints) {\n if (!checkpoints.hasOwnProperty(key)) {\n continue;\n }\n try {\n if (\n key.startsWith('v1.task.checkpoint.publish.create.pr') ||\n key.startsWith('v1.task.checkpoint.publish.create.mr')\n ) {\n const url =\n checkpoints[key]?.value?.html_url ??\n checkpoints[key]?.value?.mrWebUrl;\n if (!url) {\n continue;\n }\n const prNumber = Number.parseInt(url.split('/').pop()!, 10);\n const prDetails = await gitApiService.getPullRequest(repoUrl, prNumber);\n\n let catalogInfoContent: string | undefined;\n if (prDetails.prSha) {\n catalogInfoContent = await gitApiService.getCatalogInfoFile(logger, {\n repoUrl,\n prHeadSha: prDetails.prSha,\n prNumber,\n });\n }\n return {\n url,\n number: prNumber,\n title: prDetails.title,\n body: prDetails.body,\n status: prDetails.merged ? 'PR_MERGED' : 'WAIT_PR_APPROVAL',\n catalogInfoContent,\n };\n }\n } catch (err) {\n logger.warn(`Error while processing checkpoint ${key}: ${err}`);\n continue;\n }\n }\n\n return undefined;\n}\n\nexport async function deleteImportByRepo(\n deps: {\n logger: LoggerService;\n config: Config;\n gitApiService: GithubApiService | GitlabApiService;\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.gitApiService.findImportOpenPr(deps.logger, {\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\n await deps.gitApiService.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.gitApiService.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\nexport async function deleteTaskImportByRepo(\n deps: {\n logger: LoggerService;\n dao: RepositoryDao;\n },\n repoUrl: string,\n): Promise<HandlerResponse<void>> {\n deps.logger.debug(`Deleting repository from database by name ${repoUrl}...`);\n try {\n await deps.dao.deleteRepository(repoUrl);\n\n return {\n statusCode: 204,\n responseBody: undefined,\n };\n } catch (error: any) {\n deps.logger.error(\n `Failed to delete repository from database by url ${repoUrl}`,\n error,\n );\n return {\n statusCode: 500,\n };\n }\n}\n"],"names":["DefaultSortColumn","DefaultSortOrder","getNestedValue","DefaultPageNumber","DefaultPageSize","getCatalogFilename","parseGitURLForApprovalTool","imports","paginateArray","logErrorIfNeeded","getCatalogUrl","gitUrlParse"],"mappings":"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"}