@red-hat-developer-hub/backstage-plugin-bulk-import-backend 6.10.1 → 6.11.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ### Dependencies
2
2
 
3
+ ## 6.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 88ac11d: Implement the execution of import orchestrator workflows with the help of the bulk-import plugin.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [88ac11d]
12
+ - @red-hat-developer-hub/backstage-plugin-bulk-import-common@6.11.0
13
+
3
14
  ## 6.10.1
4
15
 
5
16
  ### Patch Changes
package/README.md CHANGED
@@ -103,10 +103,11 @@ bulkImport:
103
103
  importAPI: 'scaffolder'
104
104
  ```
105
105
 
106
- importAPI: This field defines the import workflow. It currently supports two options:
106
+ importAPI: This field defines the import workflow. It currently supports three options:
107
107
 
108
108
  - open-pull-requests: This is the default import workflow, which includes the logic for creating pull requests for every selected repository.
109
109
  - scaffolder: This workflow uses an import scenario defined in the scaffolder template. The import steps depend on the template's content, allowing for various scenarios. These can include importing existing catalog entities from a repository, creating pull requests, calling webhooks, and more. This method offers greater flexibility.
110
+ - orchestrator: This workflow uses the Orchestrator plugin to execute workflows for bulk import operations. This mode provides advanced workflow orchestration capabilities and integrates with the Backstage Orchestrator plugin.
110
111
 
111
112
  > Important Note
112
113
  > The scaffolder template must be generic and not specific to a single repository to be successfully executed for every repository in the bulk list.
@@ -176,6 +177,119 @@ parameters:
176
177
  title: Git provider host
177
178
  ```
178
179
 
180
+ #### Orchestrator Workflow Execution
181
+
182
+ The Bulk Import plugin supports using the Orchestrator plugin to execute workflows for bulk import operations. This mode leverages the Orchestrator's workflow engine to provide advanced orchestration capabilities for importing repositories.
183
+
184
+ ##### Plugin Configuration
185
+
186
+ To use the orchestrator mode, configure the Bulk Import plugin as follows:
187
+
188
+ ```yaml
189
+ bulkImport:
190
+ orchestratorWorkflow: your-workflow-id
191
+ importAPI: 'orchestrator'
192
+ ```
193
+
194
+ **Configuration Parameters:**
195
+
196
+ - **`orchestratorWorkflow`** (required): The ID of the orchestrator workflow to execute for each repository. This workflow must be registered in the Orchestrator plugin.
197
+ - **`importAPI`**: Set to `'orchestrator'` to enable orchestrator workflow execution mode.
198
+
199
+ > **Important Notes:**
200
+ >
201
+ > - The Orchestrator plugin must be installed and configured in your Backstage instance.
202
+ > - The specified workflow must be available in the Orchestrator plugin.
203
+ > - The workflow must be generic and able to handle different repositories, as it will be executed for each repository in the bulk import list.
204
+
205
+ ##### Orchestrator Workflow Input Parameters
206
+
207
+ When executing an orchestrator workflow, the Bulk Import plugin provides the following input data to the workflow:
208
+
209
+ - **`owner`** – Repository owner (organization or user name).
210
+ **Example:** For `https://github.com/redhat-developer/rhdh-plugins`, `owner` will be `redhat-developer`.
211
+
212
+ - **`repo`** – Repository name.
213
+ **Example:** For `https://github.com/redhat-developer/rhdh-plugins`, `repo` will be `rhdh-plugins`.
214
+
215
+ - **`baseBranch`** – Default branch of the Git repository (e.g., `main`, `master`).
216
+
217
+ - **`targetBranch`** – Target branch for the import operation. By default, this is set to `bulk-import-orchestrator`.
218
+
219
+ Additionally, the plugin automatically provides authentication tokens for the Git provider:
220
+
221
+ - **`authTokens`** – Array of authentication tokens for the Git provider:
222
+ - For GitHub repositories (`approvalTool: 'GIT'`): `{ token: <github-token>, provider: 'github' }`
223
+ - For GitLab repositories (`approvalTool: 'GITLAB'`): `{ token: <gitlab-token>, provider: 'gitlab' }`
224
+
225
+ The tokens are obtained from the configured GitHub/GitLab integrations in your Backstage instance.
226
+
227
+ ##### How Orchestrator Mode Works
228
+
229
+ 1. **Workflow Execution**: When you submit a bulk import request with `importAPI: 'orchestrator'`, the plugin:
230
+ - Iterates through each repository in the request
231
+ - Retrieves the appropriate Git provider credentials (GitHub or GitLab)
232
+ - Executes the specified orchestrator workflow for each repository
233
+ - Passes the repository information and authentication tokens as input data
234
+
235
+ 2. **Workflow Tracking**: The plugin:
236
+ - Stores workflow instance IDs in the database
237
+ - Associates each workflow instance with its repository
238
+ - Tracks workflow execution status by querying the Orchestrator API
239
+
240
+ 3. **Status Monitoring**: You can check the status of orchestrator workflows:
241
+ - The workflow status is mapped from Orchestrator states to Bulk Import statuses (e.g., `WORKFLOW_ACTIVE`, `WORKFLOW_COMPLETED`, `WORKFLOW_ERROR`)
242
+ - Status information includes the workflow instance ID, which can be used to view detailed workflow execution in the Orchestrator UI
243
+
244
+ ##### API Endpoints for Orchestrator Mode
245
+
246
+ When using `importAPI: 'orchestrator'`, the following API endpoints are used:
247
+
248
+ - **`POST /api/bulk-import/orchestrator-workflows`** – Create orchestrator workflow import jobs
249
+ - **`GET /api/bulk-import/orchestrator-workflows`** – List all orchestrator workflow import jobs
250
+ - **`GET /api/bulk-import/orchestrator-import/by-repo`** – Get orchestrator workflow import status for a specific repository
251
+ - **`DELETE /api/bulk-import/orchestrator-import/by-repo`** – Delete orchestrator workflow records for a specific repository
252
+
253
+ ##### Example Orchestrator Workflow
254
+
255
+ Your orchestrator workflow should accept the input parameters described above. Here's an example of how the workflow input data structure looks:
256
+
257
+ ```json
258
+ {
259
+ "inputData": {
260
+ "owner": "redhat-developer",
261
+ "repo": "rhdh-plugins",
262
+ "baseBranch": "main",
263
+ "targetBranch": "bulk-import-orchestrator"
264
+ },
265
+ "authTokens": [
266
+ {
267
+ "token": "<github-token>",
268
+ "provider": "github"
269
+ }
270
+ ]
271
+ }
272
+ ```
273
+
274
+ The workflow can then use these parameters to perform operations such as:
275
+
276
+ - Creating catalog entities
277
+ - Generating pull requests
278
+ - Calling external APIs
279
+ - Performing custom import logic
280
+
281
+ ##### Workflow Status Mapping
282
+
283
+ The plugin maps Orchestrator workflow states to Bulk Import status values:
284
+
285
+ - Orchestrator `ACTIVE` → `WORKFLOW_ACTIVE`
286
+ - Orchestrator `COMPLETED` → `WORKFLOW_COMPLETED`
287
+ - Orchestrator `ERROR` → `WORKFLOW_ERROR`
288
+ - Orchestrator `ABORTED` → `WORKFLOW_ABORTED`
289
+ - Other states are prefixed with `WORKFLOW_` and uppercased
290
+
291
+ If workflow status cannot be retrieved, the status is set to `WORKFLOW_FETCH_FAILED`.
292
+
179
293
  ### Audit Logging
180
294
 
181
295
  Audit logging is backed by the [`@backstage/backend-plugin-api`](https://www.npmjs.com/package/@backstage/backend-plugin-api) package.
package/config.d.ts CHANGED
@@ -17,9 +17,14 @@
17
17
  export interface Config {
18
18
  bulkImport?: {
19
19
  /**
20
- * The name of the scaffolder template to execute for importing a repository.
20
+ * The name of the scaffolder template or entity reference to execute for importing a repository.
21
21
  * @visibility backend
22
22
  */
23
23
  importTemplate?: string;
24
+ /**
25
+ * The orchestrator workflow id to execute for importing a repository.
26
+ * @visibility backend
27
+ */
28
+ orchestratorWorkflow?: string;
24
29
  };
25
30
  }
@@ -25,41 +25,42 @@ async function paginateQuery(queryBuilder, page = 1, size = 10, search) {
25
25
  };
26
26
  }
27
27
  class RepositoryDao {
28
- constructor(knex, logger) {
28
+ constructor(knex, logger, tableName) {
29
29
  this.knex = knex;
30
30
  this.logger = logger;
31
+ this.tableName = tableName;
31
32
  }
32
33
  async findRepositories(page = 1, size = 10, search) {
33
34
  this.logger.debug(
34
35
  `Fetching repositories page=${page}, size=${size}, search=${search}`
35
36
  );
36
- const query = this.knex("repositories").select("id", "url", "approvalTool");
37
+ const query = this.knex(this.tableName).select("id", "url", "approvalTool");
37
38
  const searchParam = { column: "url", term: search };
38
39
  return paginateQuery(query, page, size, searchParam);
39
40
  }
40
- async insertRepository(repoUrl, taskId, approvalTool) {
41
+ async insertRepository(repoUrl, taskOrWorkflowId, approvalTool) {
41
42
  this.logger.debug(
42
- `Saving task ${taskId} for repo ${repoUrl} to database..`
43
+ `Saving repository ${repoUrl} for task/workflow ${taskOrWorkflowId} to database..`
43
44
  );
44
- const repository = await this.knex("repositories").where({ url: repoUrl }).first();
45
+ const repository = await this.knex(this.tableName).where({ url: repoUrl }).first();
45
46
  let repositoryId;
46
47
  if (repository) {
47
48
  repositoryId = repository.id;
48
49
  } else {
49
- const [newRepository] = await this.knex("repositories").insert({ url: repoUrl, approvalTool }).returning("id");
50
+ const [newRepository] = await this.knex(this.tableName).insert({ url: repoUrl, approvalTool }).returning("id");
50
51
  repositoryId = newRepository.id;
51
52
  }
52
53
  return repositoryId;
53
54
  }
54
55
  async findRepositoryByUrl(url) {
55
56
  this.logger.debug(`Fetching repository from database by url ${url}...`);
56
- return await this.knex("repositories").where({ url }).first();
57
+ return await this.knex(this.tableName).where({ url }).first();
57
58
  }
58
59
  async deleteRepository(url) {
59
60
  this.logger.debug(`Deleting repository from database by url ${url}...`);
60
- const repository = await this.knex("repositories").where({ url }).first();
61
+ const repository = await this.knex(this.tableName).where({ url }).first();
61
62
  if (repository) {
62
- await this.knex("repositories").where({ id: repository.id }).del();
63
+ await this.knex(this.tableName).where({ id: repository.id }).del();
63
64
  }
64
65
  }
65
66
  }
@@ -109,7 +110,37 @@ class TaskLocationsDao {
109
110
  );
110
111
  }
111
112
  }
113
+ class OrchestratorWorkflowDao {
114
+ constructor(knex) {
115
+ this.knex = knex;
116
+ }
117
+ async insertWorkflow(instanceId, repositoryId) {
118
+ const [newWorkflow] = await this.knex("orchestrator_workflows").insert({
119
+ instance_id: instanceId,
120
+ repositoryId
121
+ }).returning("id");
122
+ return newWorkflow.id;
123
+ }
124
+ async lastExecutedWorkflowByRepoId(repositoryId) {
125
+ const result = await this.knex("orchestrator_workflows").select({
126
+ id: "id",
127
+ instanceId: "instance_id",
128
+ repositoryId: "repositoryId",
129
+ createdAt: "created_at"
130
+ }).where({ repositoryId }).orderBy("created_at", "desc").first();
131
+ return result;
132
+ }
133
+ async findWorkflowsByRepositoryId(repositoryId) {
134
+ return await this.knex("orchestrator_workflows").where({ repositoryId }).select({
135
+ id: "id",
136
+ instanceId: "instance_id",
137
+ repositoryId: "repositoryId",
138
+ createdAt: "created_at"
139
+ });
140
+ }
141
+ }
112
142
 
143
+ exports.OrchestratorWorkflowDao = OrchestratorWorkflowDao;
113
144
  exports.RepositoryDao = RepositoryDao;
114
145
  exports.ScaffolderTaskDao = ScaffolderTaskDao;
115
146
  exports.TaskLocationsDao = TaskLocationsDao;
@@ -1 +1 @@
1
- {"version":3,"file":"repositoryDao.cjs.js","sources":["../../src/database/repositoryDao.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 { LoggerService } from '@backstage/backend-plugin-api';\n\nimport { Knex } from 'knex';\n\nimport { Components } from '../generated/openapi';\n\nexport interface Repository {\n id: number;\n url: string;\n approvalTool: Components.Schemas.ApprovalTool;\n}\n\nexport interface ScaffolderTask {\n taskId: string;\n scaffolderOptions: any;\n repositoryId: number;\n executedAt?: Date;\n}\n\nexport interface PaginatedResult<T> {\n data: T[];\n page: number;\n size: number;\n total: number;\n totalPages: number;\n}\n\nexport async function paginateQuery<T>(\n queryBuilder: Knex.QueryBuilder<any, T[]>,\n page: number = 1,\n size: number = 10,\n search?: { column: string; term?: string },\n): Promise<PaginatedResult<T>> {\n const offset = (page - 1) * size;\n\n let baseQuery = queryBuilder.clone();\n\n if (search?.term) {\n if (queryBuilder.client.dialect === 'postgres') {\n baseQuery = baseQuery.whereILike(search.column, `%${search.term}%`);\n } else {\n baseQuery = baseQuery.whereLike(search.column, `%${search.term}%`);\n }\n }\n\n const countQuery = baseQuery\n .clone()\n .clearSelect()\n .clearOrder()\n .count<{ count: string }[]>('* as count');\n\n const [rows, [{ count }]] = await Promise.all([\n baseQuery.clone().limit(size).offset(offset),\n countQuery,\n ]);\n\n const total = Number(count);\n\n return {\n data: rows as T[],\n page,\n size,\n total,\n totalPages: Math.ceil(total / size),\n };\n}\n\n// @internal\nexport class RepositoryDao {\n constructor(\n private readonly knex: Knex<any, any[]>,\n private readonly logger: LoggerService,\n ) {}\n\n async findRepositories(\n page: number = 1,\n size: number = 10,\n search?: string,\n ): Promise<PaginatedResult<Repository>> {\n this.logger.debug(\n `Fetching repositories page=${page}, size=${size}, search=${search}`,\n );\n const query = this.knex('repositories').select('id', 'url', 'approvalTool');\n const searchParam = { column: 'url', term: search };\n return paginateQuery<Repository>(query, page, size, searchParam);\n }\n\n async insertRepository(\n repoUrl: string,\n taskId: string,\n approvalTool: string,\n ): Promise<number> {\n this.logger.debug(\n `Saving task ${taskId} for repo ${repoUrl} to database..`,\n );\n const repository = await this.knex('repositories')\n .where({ url: repoUrl })\n .first();\n\n let repositoryId;\n if (repository) {\n repositoryId = repository.id;\n } else {\n const [newRepository] = await this.knex('repositories')\n .insert({ url: repoUrl, approvalTool: approvalTool })\n .returning('id');\n repositoryId = newRepository.id;\n }\n return repositoryId;\n }\n\n async findRepositoryByUrl(url: string): Promise<Repository | undefined> {\n this.logger.debug(`Fetching repository from database by url ${url}...`);\n return await this.knex('repositories').where({ url: url }).first();\n }\n\n async deleteRepository(url: string): Promise<void> {\n this.logger.debug(`Deleting repository from database by url ${url}...`);\n const repository = await this.knex('repositories')\n .where({ url: url })\n .first();\n\n if (repository) {\n await this.knex('repositories').where({ id: repository.id }).del();\n }\n }\n}\n\n// @internal\nexport class ScaffolderTaskDao {\n constructor(private readonly knex: Knex<any, any[]>) {}\n\n async findAllTasks(): Promise<ScaffolderTask[]> {\n return await this.knex('scaffolder_tasks').select<ScaffolderTask[]>(\n 'taskId',\n 'repositoryId',\n 'scaffolderOptions',\n );\n }\n\n async insertTask(task: ScaffolderTask): Promise<string> {\n const result = await this.knex('scaffolder_tasks')\n .insert<ScaffolderTask>({\n taskId: task.taskId,\n repositoryId: task.repositoryId,\n scaffolderOptions: JSON.stringify(task.scaffolderOptions),\n executedAt: task.executedAt,\n })\n .returning<{ taskId: string }[]>('taskId');\n\n return result[0].taskId;\n }\n\n async findTasksByRepositoryId(\n repositoryId: number,\n ): Promise<ScaffolderTask[]> {\n return await this.knex('scaffolder_tasks')\n .where({ repositoryId })\n .select<\n ScaffolderTask[]\n >('taskId', 'repositoryId', 'scaffolderOptions', 'executedAt');\n }\n\n async lastExecutedTaskByRepoId(\n repositoryId: number,\n ): Promise<ScaffolderTask | undefined> {\n return await this.knex('scaffolder_tasks')\n .where({ repositoryId })\n .orderBy('executedAt', 'desc')\n .first<ScaffolderTask>();\n }\n}\n\nexport interface TaskLocation {\n id: number;\n taskId: string;\n location: string;\n type: string;\n}\n\n// @internal\nexport class TaskLocationsDao {\n constructor(private readonly knex: Knex<any, any[]>) {}\n\n async addTaskLocation(\n taskId: string,\n location: string,\n type: string = 'component',\n ): Promise<void> {\n await this.knex('task_locations').insert({ taskId, location, type });\n }\n\n async findLocationsByTaskId(taskId: string): Promise<TaskLocation[]> {\n return await this.knex('task_locations')\n .where({ taskId })\n .select<TaskLocation[]>('id', 'taskId', 'location', 'type');\n }\n\n async findAllLocations(): Promise<TaskLocation[]> {\n return await this.knex('task_locations').select<TaskLocation[]>(\n 'id',\n 'taskId',\n 'location',\n 'type',\n );\n }\n}\n"],"names":[],"mappings":";;AA2CA,eAAsB,cACpB,YACA,EAAA,IAAA,GAAe,CACf,EAAA,IAAA,GAAe,IACf,MAC6B,EAAA;AAC7B,EAAM,MAAA,MAAA,GAAA,CAAU,OAAO,CAAK,IAAA,IAAA;AAE5B,EAAI,IAAA,SAAA,GAAY,aAAa,KAAM,EAAA;AAEnC,EAAA,IAAI,QAAQ,IAAM,EAAA;AAChB,IAAI,IAAA,YAAA,CAAa,MAAO,CAAA,OAAA,KAAY,UAAY,EAAA;AAC9C,MAAA,SAAA,GAAY,UAAU,UAAW,CAAA,MAAA,CAAO,QAAQ,CAAI,CAAA,EAAA,MAAA,CAAO,IAAI,CAAG,CAAA,CAAA,CAAA;AAAA,KAC7D,MAAA;AACL,MAAA,SAAA,GAAY,UAAU,SAAU,CAAA,MAAA,CAAO,QAAQ,CAAI,CAAA,EAAA,MAAA,CAAO,IAAI,CAAG,CAAA,CAAA,CAAA;AAAA;AACnE;AAGF,EAAM,MAAA,UAAA,GAAa,UAChB,KAAM,EAAA,CACN,aACA,CAAA,UAAA,EACA,CAAA,KAAA,CAA2B,YAAY,CAAA;AAE1C,EAAM,MAAA,CAAC,IAAM,EAAA,CAAC,EAAE,KAAA,EAAO,CAAC,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAI,CAAA;AAAA,IAC5C,UAAU,KAAM,EAAA,CAAE,MAAM,IAAI,CAAA,CAAE,OAAO,MAAM,CAAA;AAAA,IAC3C;AAAA,GACD,CAAA;AAED,EAAM,MAAA,KAAA,GAAQ,OAAO,KAAK,CAAA;AAE1B,EAAO,OAAA;AAAA,IACL,IAAM,EAAA,IAAA;AAAA,IACN,IAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAY,EAAA,IAAA,CAAK,IAAK,CAAA,KAAA,GAAQ,IAAI;AAAA,GACpC;AACF;AAGO,MAAM,aAAc,CAAA;AAAA,EACzB,WAAA,CACmB,MACA,MACjB,EAAA;AAFiB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA;AAChB,EAEH,MAAM,gBACJ,CAAA,IAAA,GAAe,CACf,EAAA,IAAA,GAAe,IACf,MACsC,EAAA;AACtC,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,MACV,CAA8B,2BAAA,EAAA,IAAI,CAAU,OAAA,EAAA,IAAI,YAAY,MAAM,CAAA;AAAA,KACpE;AACA,IAAM,MAAA,KAAA,GAAQ,KAAK,IAAK,CAAA,cAAc,EAAE,MAAO,CAAA,IAAA,EAAM,OAAO,cAAc,CAAA;AAC1E,IAAA,MAAM,WAAc,GAAA,EAAE,MAAQ,EAAA,KAAA,EAAO,MAAM,MAAO,EAAA;AAClD,IAAA,OAAO,aAA0B,CAAA,KAAA,EAAO,IAAM,EAAA,IAAA,EAAM,WAAW,CAAA;AAAA;AACjE,EAEA,MAAM,gBAAA,CACJ,OACA,EAAA,MAAA,EACA,YACiB,EAAA;AACjB,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,MACV,CAAA,YAAA,EAAe,MAAM,CAAA,UAAA,EAAa,OAAO,CAAA,cAAA;AAAA,KAC3C;AACA,IAAA,MAAM,UAAa,GAAA,MAAM,IAAK,CAAA,IAAA,CAAK,cAAc,CAAA,CAC9C,KAAM,CAAA,EAAE,GAAK,EAAA,OAAA,EAAS,CAAA,CACtB,KAAM,EAAA;AAET,IAAI,IAAA,YAAA;AACJ,IAAA,IAAI,UAAY,EAAA;AACd,MAAA,YAAA,GAAe,UAAW,CAAA,EAAA;AAAA,KACrB,MAAA;AACL,MAAA,MAAM,CAAC,aAAa,CAAA,GAAI,MAAM,IAAA,CAAK,KAAK,cAAc,CAAA,CACnD,MAAO,CAAA,EAAE,KAAK,OAAS,EAAA,YAAA,EAA4B,CAAA,CACnD,UAAU,IAAI,CAAA;AACjB,MAAA,YAAA,GAAe,aAAc,CAAA,EAAA;AAAA;AAE/B,IAAO,OAAA,YAAA;AAAA;AACT,EAEA,MAAM,oBAAoB,GAA8C,EAAA;AACtE,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA4C,yCAAA,EAAA,GAAG,CAAK,GAAA,CAAA,CAAA;AACtE,IAAO,OAAA,MAAM,IAAK,CAAA,IAAA,CAAK,cAAc,CAAA,CAAE,MAAM,EAAE,GAAA,EAAU,CAAA,CAAE,KAAM,EAAA;AAAA;AACnE,EAEA,MAAM,iBAAiB,GAA4B,EAAA;AACjD,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA4C,yCAAA,EAAA,GAAG,CAAK,GAAA,CAAA,CAAA;AACtE,IAAM,MAAA,UAAA,GAAa,MAAM,IAAA,CAAK,IAAK,CAAA,cAAc,CAC9C,CAAA,KAAA,CAAM,EAAE,GAAA,EAAU,CAAA,CAClB,KAAM,EAAA;AAET,IAAA,IAAI,UAAY,EAAA;AACd,MAAM,MAAA,IAAA,CAAK,IAAK,CAAA,cAAc,CAAE,CAAA,KAAA,CAAM,EAAE,EAAA,EAAI,UAAW,CAAA,EAAA,EAAI,CAAA,CAAE,GAAI,EAAA;AAAA;AACnE;AAEJ;AAGO,MAAM,iBAAkB,CAAA;AAAA,EAC7B,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAEtD,MAAM,YAA0C,GAAA;AAC9C,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,CAAE,CAAA,MAAA;AAAA,MACzC,QAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA,KACF;AAAA;AACF,EAEA,MAAM,WAAW,IAAuC,EAAA;AACtD,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,EAC9C,MAAuB,CAAA;AAAA,MACtB,QAAQ,IAAK,CAAA,MAAA;AAAA,MACb,cAAc,IAAK,CAAA,YAAA;AAAA,MACnB,iBAAmB,EAAA,IAAA,CAAK,SAAU,CAAA,IAAA,CAAK,iBAAiB,CAAA;AAAA,MACxD,YAAY,IAAK,CAAA;AAAA,KAClB,CACA,CAAA,SAAA,CAAgC,QAAQ,CAAA;AAE3C,IAAO,OAAA,MAAA,CAAO,CAAC,CAAE,CAAA,MAAA;AAAA;AACnB,EAEA,MAAM,wBACJ,YAC2B,EAAA;AAC3B,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,EACtC,KAAM,CAAA,EAAE,YAAa,EAAC,CACtB,CAAA,MAAA,CAEC,QAAU,EAAA,cAAA,EAAgB,qBAAqB,YAAY,CAAA;AAAA;AACjE,EAEA,MAAM,yBACJ,YACqC,EAAA;AACrC,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,EACtC,KAAM,CAAA,EAAE,YAAa,EAAC,CACtB,CAAA,OAAA,CAAQ,YAAc,EAAA,MAAM,EAC5B,KAAsB,EAAA;AAAA;AAE7B;AAUO,MAAM,gBAAiB,CAAA;AAAA,EAC5B,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAEtD,MAAM,eAAA,CACJ,MACA,EAAA,QAAA,EACA,OAAe,WACA,EAAA;AACf,IAAM,MAAA,IAAA,CAAK,KAAK,gBAAgB,CAAA,CAAE,OAAO,EAAE,MAAA,EAAQ,QAAU,EAAA,IAAA,EAAM,CAAA;AAAA;AACrE,EAEA,MAAM,sBAAsB,MAAyC,EAAA;AACnE,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,gBAAgB,EACpC,KAAM,CAAA,EAAE,MAAO,EAAC,CAChB,CAAA,MAAA,CAAuB,IAAM,EAAA,QAAA,EAAU,YAAY,MAAM,CAAA;AAAA;AAC9D,EAEA,MAAM,gBAA4C,GAAA;AAChD,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,gBAAgB,CAAE,CAAA,MAAA;AAAA,MACvC,IAAA;AAAA,MACA,QAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AAAA;AAEJ;;;;;;;"}
1
+ {"version":3,"file":"repositoryDao.cjs.js","sources":["../../src/database/repositoryDao.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 { LoggerService } from '@backstage/backend-plugin-api';\n\nimport { Knex } from 'knex';\n\nimport { Components } from '../generated/openapi';\n\nexport interface Repository {\n id: number;\n url: string;\n approvalTool: Components.Schemas.ApprovalTool;\n}\n\nexport interface ScaffolderTask {\n taskId: string;\n scaffolderOptions: any;\n repositoryId: number;\n executedAt?: Date;\n}\n\nexport interface PaginatedResult<T> {\n data: T[];\n page: number;\n size: number;\n total: number;\n totalPages: number;\n}\n\nexport async function paginateQuery<T>(\n queryBuilder: Knex.QueryBuilder<any, T[]>,\n page: number = 1,\n size: number = 10,\n search?: { column: string; term?: string },\n): Promise<PaginatedResult<T>> {\n const offset = (page - 1) * size;\n\n let baseQuery = queryBuilder.clone();\n\n if (search?.term) {\n if (queryBuilder.client.dialect === 'postgres') {\n baseQuery = baseQuery.whereILike(search.column, `%${search.term}%`);\n } else {\n baseQuery = baseQuery.whereLike(search.column, `%${search.term}%`);\n }\n }\n\n const countQuery = baseQuery\n .clone()\n .clearSelect()\n .clearOrder()\n .count<{ count: string }[]>('* as count');\n\n const [rows, [{ count }]] = await Promise.all([\n baseQuery.clone().limit(size).offset(offset),\n countQuery,\n ]);\n\n const total = Number(count);\n\n return {\n data: rows as T[],\n page,\n size,\n total,\n totalPages: Math.ceil(total / size),\n };\n}\n\nexport type RepositoryName = 'repositories' | 'orchestrator_repositories';\n\n// @internal\nexport class RepositoryDao<R extends RepositoryName> {\n constructor(\n private readonly knex: Knex<any, any[]>,\n private readonly logger: LoggerService,\n private readonly tableName: R,\n ) {}\n\n async findRepositories(\n page: number = 1,\n size: number = 10,\n search?: string,\n ): Promise<PaginatedResult<Repository>> {\n this.logger.debug(\n `Fetching repositories page=${page}, size=${size}, search=${search}`,\n );\n const query = this.knex(this.tableName).select('id', 'url', 'approvalTool');\n const searchParam = { column: 'url', term: search };\n return paginateQuery<Repository>(query, page, size, searchParam);\n }\n\n async insertRepository(\n repoUrl: string,\n taskOrWorkflowId: string,\n approvalTool: string,\n ): Promise<number> {\n this.logger.debug(\n `Saving repository ${repoUrl} for task/workflow ${taskOrWorkflowId} to database..`,\n );\n const repository = await this.knex(this.tableName)\n .where({ url: repoUrl })\n .first();\n\n let repositoryId;\n if (repository) {\n repositoryId = repository.id;\n } else {\n const [newRepository] = await this.knex(this.tableName)\n .insert({ url: repoUrl, approvalTool: approvalTool })\n .returning('id');\n repositoryId = newRepository.id;\n }\n return repositoryId;\n }\n\n async findRepositoryByUrl(url: string): Promise<Repository | undefined> {\n this.logger.debug(`Fetching repository from database by url ${url}...`);\n return await this.knex(this.tableName).where({ url: url }).first();\n }\n\n async deleteRepository(url: string): Promise<void> {\n this.logger.debug(`Deleting repository from database by url ${url}...`);\n const repository = await this.knex(this.tableName)\n .where({ url: url })\n .first();\n\n if (repository) {\n await this.knex(this.tableName).where({ id: repository.id }).del();\n }\n }\n}\n\n// @internal\nexport class ScaffolderTaskDao {\n constructor(private readonly knex: Knex<any, any[]>) {}\n\n async findAllTasks(): Promise<ScaffolderTask[]> {\n return await this.knex('scaffolder_tasks').select<ScaffolderTask[]>(\n 'taskId',\n 'repositoryId',\n 'scaffolderOptions',\n );\n }\n\n async insertTask(task: ScaffolderTask): Promise<string> {\n const result = await this.knex('scaffolder_tasks')\n .insert<ScaffolderTask>({\n taskId: task.taskId,\n repositoryId: task.repositoryId,\n scaffolderOptions: JSON.stringify(task.scaffolderOptions),\n executedAt: task.executedAt,\n })\n .returning<{ taskId: string }[]>('taskId');\n\n return result[0].taskId;\n }\n\n async findTasksByRepositoryId(\n repositoryId: number,\n ): Promise<ScaffolderTask[]> {\n return await this.knex('scaffolder_tasks')\n .where({ repositoryId })\n .select<\n ScaffolderTask[]\n >('taskId', 'repositoryId', 'scaffolderOptions', 'executedAt');\n }\n\n async lastExecutedTaskByRepoId(\n repositoryId: number,\n ): Promise<ScaffolderTask | undefined> {\n return await this.knex('scaffolder_tasks')\n .where({ repositoryId })\n .orderBy('executedAt', 'desc')\n .first<ScaffolderTask>();\n }\n}\n\nexport interface TaskLocation {\n id: number;\n taskId: string;\n location: string;\n type: string;\n}\n\n// @internal\nexport class TaskLocationsDao {\n constructor(private readonly knex: Knex<any, any[]>) {}\n\n async addTaskLocation(\n taskId: string,\n location: string,\n type: string = 'component',\n ): Promise<void> {\n await this.knex('task_locations').insert({ taskId, location, type });\n }\n\n async findLocationsByTaskId(taskId: string): Promise<TaskLocation[]> {\n return await this.knex('task_locations')\n .where({ taskId })\n .select<TaskLocation[]>('id', 'taskId', 'location', 'type');\n }\n\n async findAllLocations(): Promise<TaskLocation[]> {\n return await this.knex('task_locations').select<TaskLocation[]>(\n 'id',\n 'taskId',\n 'location',\n 'type',\n );\n }\n}\n\nexport interface OrchestratorWorkflow {\n id: number;\n instanceId: string;\n repositoryId: number;\n createdAt: Date;\n status?: string;\n}\n\n// @internal\nexport class OrchestratorWorkflowDao {\n constructor(private readonly knex: Knex<any, any[]>) {}\n\n async insertWorkflow(\n instanceId: string,\n repositoryId: number,\n ): Promise<number> {\n const [newWorkflow] = await this.knex('orchestrator_workflows')\n .insert({\n instance_id: instanceId,\n repositoryId: repositoryId,\n })\n .returning('id');\n return newWorkflow.id;\n }\n\n async lastExecutedWorkflowByRepoId(\n repositoryId: number,\n ): Promise<OrchestratorWorkflow | undefined> {\n const result = await this.knex('orchestrator_workflows')\n .select({\n id: 'id',\n instanceId: 'instance_id',\n repositoryId: 'repositoryId',\n createdAt: 'created_at',\n })\n .where({ repositoryId: repositoryId })\n .orderBy('created_at', 'desc')\n .first();\n return result;\n }\n\n async findWorkflowsByRepositoryId(\n repositoryId: number,\n ): Promise<OrchestratorWorkflow[]> {\n return await this.knex('orchestrator_workflows')\n .where({ repositoryId: repositoryId })\n .select({\n id: 'id',\n instanceId: 'instance_id',\n repositoryId: 'repositoryId',\n createdAt: 'created_at',\n });\n }\n}\n"],"names":[],"mappings":";;AA2CA,eAAsB,cACpB,YACA,EAAA,IAAA,GAAe,CACf,EAAA,IAAA,GAAe,IACf,MAC6B,EAAA;AAC7B,EAAM,MAAA,MAAA,GAAA,CAAU,OAAO,CAAK,IAAA,IAAA;AAE5B,EAAI,IAAA,SAAA,GAAY,aAAa,KAAM,EAAA;AAEnC,EAAA,IAAI,QAAQ,IAAM,EAAA;AAChB,IAAI,IAAA,YAAA,CAAa,MAAO,CAAA,OAAA,KAAY,UAAY,EAAA;AAC9C,MAAA,SAAA,GAAY,UAAU,UAAW,CAAA,MAAA,CAAO,QAAQ,CAAI,CAAA,EAAA,MAAA,CAAO,IAAI,CAAG,CAAA,CAAA,CAAA;AAAA,KAC7D,MAAA;AACL,MAAA,SAAA,GAAY,UAAU,SAAU,CAAA,MAAA,CAAO,QAAQ,CAAI,CAAA,EAAA,MAAA,CAAO,IAAI,CAAG,CAAA,CAAA,CAAA;AAAA;AACnE;AAGF,EAAM,MAAA,UAAA,GAAa,UAChB,KAAM,EAAA,CACN,aACA,CAAA,UAAA,EACA,CAAA,KAAA,CAA2B,YAAY,CAAA;AAE1C,EAAM,MAAA,CAAC,IAAM,EAAA,CAAC,EAAE,KAAA,EAAO,CAAC,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAI,CAAA;AAAA,IAC5C,UAAU,KAAM,EAAA,CAAE,MAAM,IAAI,CAAA,CAAE,OAAO,MAAM,CAAA;AAAA,IAC3C;AAAA,GACD,CAAA;AAED,EAAM,MAAA,KAAA,GAAQ,OAAO,KAAK,CAAA;AAE1B,EAAO,OAAA;AAAA,IACL,IAAM,EAAA,IAAA;AAAA,IACN,IAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAY,EAAA,IAAA,CAAK,IAAK,CAAA,KAAA,GAAQ,IAAI;AAAA,GACpC;AACF;AAKO,MAAM,aAAwC,CAAA;AAAA,EACnD,WAAA,CACmB,IACA,EAAA,MAAA,EACA,SACjB,EAAA;AAHiB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AAAA;AAChB,EAEH,MAAM,gBACJ,CAAA,IAAA,GAAe,CACf,EAAA,IAAA,GAAe,IACf,MACsC,EAAA;AACtC,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,MACV,CAA8B,2BAAA,EAAA,IAAI,CAAU,OAAA,EAAA,IAAI,YAAY,MAAM,CAAA;AAAA,KACpE;AACA,IAAM,MAAA,KAAA,GAAQ,KAAK,IAAK,CAAA,IAAA,CAAK,SAAS,CAAE,CAAA,MAAA,CAAO,IAAM,EAAA,KAAA,EAAO,cAAc,CAAA;AAC1E,IAAA,MAAM,WAAc,GAAA,EAAE,MAAQ,EAAA,KAAA,EAAO,MAAM,MAAO,EAAA;AAClD,IAAA,OAAO,aAA0B,CAAA,KAAA,EAAO,IAAM,EAAA,IAAA,EAAM,WAAW,CAAA;AAAA;AACjE,EAEA,MAAM,gBAAA,CACJ,OACA,EAAA,gBAAA,EACA,YACiB,EAAA;AACjB,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,MACV,CAAA,kBAAA,EAAqB,OAAO,CAAA,mBAAA,EAAsB,gBAAgB,CAAA,cAAA;AAAA,KACpE;AACA,IAAA,MAAM,UAAa,GAAA,MAAM,IAAK,CAAA,IAAA,CAAK,IAAK,CAAA,SAAS,CAC9C,CAAA,KAAA,CAAM,EAAE,GAAA,EAAK,OAAQ,EAAC,EACtB,KAAM,EAAA;AAET,IAAI,IAAA,YAAA;AACJ,IAAA,IAAI,UAAY,EAAA;AACd,MAAA,YAAA,GAAe,UAAW,CAAA,EAAA;AAAA,KACrB,MAAA;AACL,MAAA,MAAM,CAAC,aAAa,CAAA,GAAI,MAAM,IAAA,CAAK,KAAK,IAAK,CAAA,SAAS,CACnD,CAAA,MAAA,CAAO,EAAE,GAAK,EAAA,OAAA,EAAS,cAA4B,CAAA,CACnD,UAAU,IAAI,CAAA;AACjB,MAAA,YAAA,GAAe,aAAc,CAAA,EAAA;AAAA;AAE/B,IAAO,OAAA,YAAA;AAAA;AACT,EAEA,MAAM,oBAAoB,GAA8C,EAAA;AACtE,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA4C,yCAAA,EAAA,GAAG,CAAK,GAAA,CAAA,CAAA;AACtE,IAAO,OAAA,MAAM,IAAK,CAAA,IAAA,CAAK,IAAK,CAAA,SAAS,CAAE,CAAA,KAAA,CAAM,EAAE,GAAA,EAAU,CAAA,CAAE,KAAM,EAAA;AAAA;AACnE,EAEA,MAAM,iBAAiB,GAA4B,EAAA;AACjD,IAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA4C,yCAAA,EAAA,GAAG,CAAK,GAAA,CAAA,CAAA;AACtE,IAAA,MAAM,UAAa,GAAA,MAAM,IAAK,CAAA,IAAA,CAAK,IAAK,CAAA,SAAS,CAC9C,CAAA,KAAA,CAAM,EAAE,GAAA,EAAU,CAAA,CAClB,KAAM,EAAA;AAET,IAAA,IAAI,UAAY,EAAA;AACd,MAAA,MAAM,IAAK,CAAA,IAAA,CAAK,IAAK,CAAA,SAAS,CAAE,CAAA,KAAA,CAAM,EAAE,EAAA,EAAI,UAAW,CAAA,EAAA,EAAI,CAAA,CAAE,GAAI,EAAA;AAAA;AACnE;AAEJ;AAGO,MAAM,iBAAkB,CAAA;AAAA,EAC7B,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAEtD,MAAM,YAA0C,GAAA;AAC9C,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,CAAE,CAAA,MAAA;AAAA,MACzC,QAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA,KACF;AAAA;AACF,EAEA,MAAM,WAAW,IAAuC,EAAA;AACtD,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,EAC9C,MAAuB,CAAA;AAAA,MACtB,QAAQ,IAAK,CAAA,MAAA;AAAA,MACb,cAAc,IAAK,CAAA,YAAA;AAAA,MACnB,iBAAmB,EAAA,IAAA,CAAK,SAAU,CAAA,IAAA,CAAK,iBAAiB,CAAA;AAAA,MACxD,YAAY,IAAK,CAAA;AAAA,KAClB,CACA,CAAA,SAAA,CAAgC,QAAQ,CAAA;AAE3C,IAAO,OAAA,MAAA,CAAO,CAAC,CAAE,CAAA,MAAA;AAAA;AACnB,EAEA,MAAM,wBACJ,YAC2B,EAAA;AAC3B,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,EACtC,KAAM,CAAA,EAAE,YAAa,EAAC,CACtB,CAAA,MAAA,CAEC,QAAU,EAAA,cAAA,EAAgB,qBAAqB,YAAY,CAAA;AAAA;AACjE,EAEA,MAAM,yBACJ,YACqC,EAAA;AACrC,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,kBAAkB,EACtC,KAAM,CAAA,EAAE,YAAa,EAAC,CACtB,CAAA,OAAA,CAAQ,YAAc,EAAA,MAAM,EAC5B,KAAsB,EAAA;AAAA;AAE7B;AAUO,MAAM,gBAAiB,CAAA;AAAA,EAC5B,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAEtD,MAAM,eAAA,CACJ,MACA,EAAA,QAAA,EACA,OAAe,WACA,EAAA;AACf,IAAM,MAAA,IAAA,CAAK,KAAK,gBAAgB,CAAA,CAAE,OAAO,EAAE,MAAA,EAAQ,QAAU,EAAA,IAAA,EAAM,CAAA;AAAA;AACrE,EAEA,MAAM,sBAAsB,MAAyC,EAAA;AACnE,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,gBAAgB,EACpC,KAAM,CAAA,EAAE,MAAO,EAAC,CAChB,CAAA,MAAA,CAAuB,IAAM,EAAA,QAAA,EAAU,YAAY,MAAM,CAAA;AAAA;AAC9D,EAEA,MAAM,gBAA4C,GAAA;AAChD,IAAA,OAAO,MAAM,IAAA,CAAK,IAAK,CAAA,gBAAgB,CAAE,CAAA,MAAA;AAAA,MACvC,IAAA;AAAA,MACA,QAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AAAA;AAEJ;AAWO,MAAM,uBAAwB,CAAA;AAAA,EACnC,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAEtD,MAAM,cACJ,CAAA,UAAA,EACA,YACiB,EAAA;AACjB,IAAM,MAAA,CAAC,WAAW,CAAI,GAAA,MAAM,KAAK,IAAK,CAAA,wBAAwB,EAC3D,MAAO,CAAA;AAAA,MACN,WAAa,EAAA,UAAA;AAAA,MACb;AAAA,KACD,CACA,CAAA,SAAA,CAAU,IAAI,CAAA;AACjB,IAAA,OAAO,WAAY,CAAA,EAAA;AAAA;AACrB,EAEA,MAAM,6BACJ,YAC2C,EAAA;AAC3C,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,IAAK,CAAA,wBAAwB,EACpD,MAAO,CAAA;AAAA,MACN,EAAI,EAAA,IAAA;AAAA,MACJ,UAAY,EAAA,aAAA;AAAA,MACZ,YAAc,EAAA,cAAA;AAAA,MACd,SAAW,EAAA;AAAA,KACZ,CACA,CAAA,KAAA,CAAM,EAAE,YAAA,EAA4B,CAAA,CACpC,OAAQ,CAAA,YAAA,EAAc,MAAM,CAAA,CAC5B,KAAM,EAAA;AACT,IAAO,OAAA,MAAA;AAAA;AACT,EAEA,MAAM,4BACJ,YACiC,EAAA;AACjC,IAAO,OAAA,MAAM,IAAK,CAAA,IAAA,CAAK,wBAAwB,CAAA,CAC5C,MAAM,EAAE,YAAA,EAA4B,CAAA,CACpC,MAAO,CAAA;AAAA,MACN,EAAI,EAAA,IAAA;AAAA,MACJ,UAAY,EAAA,aAAA;AAAA,MACZ,YAAc,EAAA,cAAA;AAAA,MACd,SAAW,EAAA;AAAA,KACZ,CAAA;AAAA;AAEP;;;;;;;;"}
@@ -562,6 +562,135 @@ const OPENAPI = `
562
562
  }
563
563
  }
564
564
  },
565
+ "/orchestrator-workflows": {
566
+ "get": {
567
+ "operationId": "findAllOrchestratorWorkflowImports",
568
+ "summary": "Fetch Import Jobs",
569
+ "security": [
570
+ {
571
+ "BearerAuth": []
572
+ }
573
+ ],
574
+ "tags": [
575
+ "Import"
576
+ ],
577
+ "parameters": [
578
+ {
579
+ "$ref": "#/components/parameters/apiVersionHeaderParam"
580
+ },
581
+ {
582
+ "$ref": "#/components/parameters/pagePerIntegrationQueryParamDeprecated"
583
+ },
584
+ {
585
+ "$ref": "#/components/parameters/sizePerIntegrationQueryParamDeprecated"
586
+ },
587
+ {
588
+ "$ref": "#/components/parameters/pageQueryParam"
589
+ },
590
+ {
591
+ "$ref": "#/components/parameters/sizeQueryParam"
592
+ },
593
+ {
594
+ "$ref": "#/components/parameters/sortOrderQueryParam"
595
+ },
596
+ {
597
+ "$ref": "#/components/parameters/sortColumnQueryParam"
598
+ },
599
+ {
600
+ "$ref": "#/components/parameters/searchQueryParam"
601
+ }
602
+ ],
603
+ "responses": {
604
+ "200": {
605
+ "description": "Import Job list was fetched successfully with no errors",
606
+ "content": {
607
+ "application/json": {
608
+ "schema": {
609
+ "oneOf": [
610
+ {
611
+ "type": "array",
612
+ "items": {
613
+ "$ref": "#/components/schemas/SourceImport"
614
+ }
615
+ },
616
+ {
617
+ "$ref": "#/components/schemas/ImportJobListV2"
618
+ }
619
+ ]
620
+ },
621
+ "examples": {
622
+ "twoImports": {
623
+ "$ref": "#/components/examples/twoImports"
624
+ },
625
+ "multipleImportJobsV2": {
626
+ "$ref": "#/components/examples/multipleImportJobsV2"
627
+ }
628
+ }
629
+ }
630
+ }
631
+ },
632
+ "500": {
633
+ "description": "Generic error when there are errors and no Import Job is returned",
634
+ "content": {
635
+ "application/json": {
636
+ "schema": {
637
+ "oneOf": [
638
+ {
639
+ "type": "string",
640
+ "description": "Generic error"
641
+ },
642
+ {
643
+ "$ref": "#/components/schemas/ImportJobListV2"
644
+ }
645
+ ]
646
+ },
647
+ "examples": {
648
+ "repositoryListErrors": {
649
+ "$ref": "#/components/examples/importJobListErrors"
650
+ }
651
+ }
652
+ }
653
+ }
654
+ }
655
+ }
656
+ },
657
+ "post": {
658
+ "operationId": "createOrchestratorWorkflowJobs",
659
+ "summary": "Execute an orchestrator workflow",
660
+ "security": [
661
+ {
662
+ "BearerAuth": []
663
+ }
664
+ ],
665
+ "tags": [
666
+ "Import"
667
+ ],
668
+ "requestBody": {
669
+ "description": "List of Import jobs to create",
670
+ "required": true,
671
+ "content": {
672
+ "application/json": {
673
+ "schema": {
674
+ "type": "array",
675
+ "items": {
676
+ "$ref": "#/components/schemas/ImportRequest"
677
+ }
678
+ },
679
+ "examples": {
680
+ "multipleImportRequests": {
681
+ "$ref": "#/components/examples/multipleImportRequests"
682
+ }
683
+ }
684
+ }
685
+ }
686
+ },
687
+ "responses": {
688
+ "200": {
689
+ "description": "The orchestrator workflow was triggered successfully."
690
+ }
691
+ }
692
+ }
693
+ },
565
694
  "/task-import/by-repo": {
566
695
  "get": {
567
696
  "operationId": "findTaskImportStatusByRepo",
@@ -651,6 +780,86 @@ const OPENAPI = `
651
780
  }
652
781
  }
653
782
  },
783
+ "/orchestrator-import/by-repo": {
784
+ "get": {
785
+ "operationId": "findOrchestratorImportStatusByRepo",
786
+ "summary": "Get Import Status by repository",
787
+ "security": [
788
+ {
789
+ "BearerAuth": []
790
+ }
791
+ ],
792
+ "tags": [
793
+ "Import"
794
+ ],
795
+ "parameters": [
796
+ {
797
+ "in": "query",
798
+ "name": "repo",
799
+ "description": "the full URL to the repo",
800
+ "schema": {
801
+ "type": "string"
802
+ }
803
+ },
804
+ {
805
+ "$ref": "#/components/parameters/approvalToolParam"
806
+ }
807
+ ],
808
+ "responses": {
809
+ "200": {
810
+ "description": "Import Job status was determined successfully with no errors",
811
+ "content": {
812
+ "application/json": {
813
+ "schema": {
814
+ "$ref": "#/components/schemas/Import"
815
+ },
816
+ "examples": {
817
+ "singleImportStatusForRepo": {
818
+ "$ref": "#/components/examples/singleImportStatusForRepo"
819
+ }
820
+ }
821
+ }
822
+ }
823
+ },
824
+ "500": {
825
+ "description": "Generic error"
826
+ }
827
+ }
828
+ },
829
+ "delete": {
830
+ "operationId": "deleteOrchestratorImportByRepo",
831
+ "summary": "Delete stored orchestrator workflow records for a specific repository",
832
+ "security": [
833
+ {
834
+ "BearerAuth": []
835
+ }
836
+ ],
837
+ "tags": [
838
+ "Import"
839
+ ],
840
+ "parameters": [
841
+ {
842
+ "in": "query",
843
+ "name": "repo",
844
+ "description": "the full URL to the repo",
845
+ "schema": {
846
+ "type": "string"
847
+ }
848
+ },
849
+ {
850
+ "$ref": "#/components/parameters/approvalToolParam"
851
+ }
852
+ ],
853
+ "responses": {
854
+ "204": {
855
+ "description": "Repository was deleted successfully with no errors"
856
+ },
857
+ "500": {
858
+ "description": "Generic error"
859
+ }
860
+ }
861
+ }
862
+ },
654
863
  "/import/by-repo": {
655
864
  "get": {
656
865
  "operationId": "findImportStatusByRepo",
@@ -983,6 +1192,9 @@ const OPENAPI = `
983
1192
  },
984
1193
  {
985
1194
  "$ref": "#/components/schemas/TaskImportStatus"
1195
+ },
1196
+ {
1197
+ "$ref": "#/components/schemas/WorkflowImportStatus"
986
1198
  }
987
1199
  ]
988
1200
  },
@@ -1012,7 +1224,6 @@ const OPENAPI = `
1012
1224
  },
1013
1225
  "TaskImportStatus": {
1014
1226
  "type": "string",
1015
- "nullable": true,
1016
1227
  "description": "Import Job status",
1017
1228
  "enum": [
1018
1229
  "TASK_CANCELLED",
@@ -1076,6 +1287,9 @@ const OPENAPI = `
1076
1287
  },
1077
1288
  {
1078
1289
  "$ref": "#/components/schemas/TaskImportStatus"
1290
+ },
1291
+ {
1292
+ "$ref": "#/components/schemas/WorkflowImportStatus"
1079
1293
  }
1080
1294
  ]
1081
1295
  },
@@ -1087,6 +1301,14 @@ const OPENAPI = `
1087
1301
  }
1088
1302
  }
1089
1303
  },
1304
+ "workflow": {
1305
+ "type": "object",
1306
+ "properties": {
1307
+ "workflowId": {
1308
+ "type": "string"
1309
+ }
1310
+ }
1311
+ },
1090
1312
  "tasks": {
1091
1313
  "type": "array",
1092
1314
  "items": {
@@ -1098,6 +1320,17 @@ const OPENAPI = `
1098
1320
  }
1099
1321
  }
1100
1322
  },
1323
+ "workflows": {
1324
+ "type": "array",
1325
+ "items": {
1326
+ "type": "object",
1327
+ "properties": {
1328
+ "workflowId": {
1329
+ "type": "string"
1330
+ }
1331
+ }
1332
+ }
1333
+ },
1101
1334
  "catalogEntityName": {
1102
1335
  "type": "string",
1103
1336
  "description": "Specified entity name in the catalog. Filled only in response for dry-run import requests."
@@ -1281,6 +1514,19 @@ const OPENAPI = `
1281
1514
  }
1282
1515
  }
1283
1516
  }
1517
+ },
1518
+ "WorkflowImportStatus": {
1519
+ "type": "string",
1520
+ "description": "Import Job status",
1521
+ "enum": [
1522
+ "WORKFLOW_ACTIVE",
1523
+ "WORKFLOW_COMPLETED",
1524
+ "WORKFLOW_ABORTED",
1525
+ "WORKFLOW_SUSPENDED",
1526
+ "WORKFLOW_ERROR",
1527
+ "WORKFLOW_PENDING",
1528
+ "WORKFLOW_FETCH_FAILED"
1529
+ ]
1284
1530
  }
1285
1531
  },
1286
1532
  "securitySchemes": {