@openchoreo/backstage-plugin-openchoreo-workflows-backend 1.1.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 ADDED
@@ -0,0 +1,5 @@
1
+ # @openchoreo/backstage-plugin-openchoreo-workflows-backend
2
+
3
+ ## 1.1.0
4
+
5
+ - Initial public release on GitHub Packages, aligned with the OpenChoreo platform release line (`1.1.0`).
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var plugin = require('./plugin.cjs.js');
6
+
7
+
8
+
9
+ exports.default = plugin.openchoreoWorkflowsBackendPlugin;
10
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -0,0 +1,17 @@
1
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
+
3
+ /**
4
+ * OpenChoreo Generic Workflows Backend Plugin
5
+ *
6
+ * This plugin provides backend APIs for org-level generic workflows:
7
+ * - Listing workflow templates
8
+ * - Getting workflow schemas for dynamic form generation
9
+ * - Listing workflow runs
10
+ * - Getting workflow run details
11
+ * - Triggering new workflow runs
12
+ *
13
+ * @public
14
+ */
15
+ declare const openchoreoWorkflowsBackendPlugin: _backstage_backend_plugin_api.BackendFeature;
16
+
17
+ export { openchoreoWorkflowsBackendPlugin as default };
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var router = require('./router.cjs.js');
5
+ var openchoreoAuth = require('@openchoreo/openchoreo-auth');
6
+ var GenericWorkflowService = require('./services/GenericWorkflowService.cjs.js');
7
+
8
+ const openchoreoWorkflowsBackendPlugin = backendPluginApi.createBackendPlugin({
9
+ pluginId: "openchoreo-workflows-backend",
10
+ register(env) {
11
+ env.registerInit({
12
+ deps: {
13
+ httpAuth: backendPluginApi.coreServices.httpAuth,
14
+ httpRouter: backendPluginApi.coreServices.httpRouter,
15
+ logger: backendPluginApi.coreServices.logger,
16
+ config: backendPluginApi.coreServices.rootConfig,
17
+ tokenService: openchoreoAuth.openChoreoTokenServiceRef
18
+ },
19
+ async init({ httpRouter, logger, config, tokenService }) {
20
+ const openchoreoConfig = config.getOptionalConfig("openchoreo");
21
+ if (!openchoreoConfig) {
22
+ logger.info(
23
+ "OpenChoreo Workflows backend plugin disabled - no configuration found"
24
+ );
25
+ return;
26
+ }
27
+ const baseUrl = openchoreoConfig.getString("baseUrl");
28
+ const authEnabled = config.getOptionalBoolean("openchoreo.features.auth.enabled") ?? true;
29
+ logger.info("Initializing OpenChoreo Workflows backend plugin");
30
+ const workflowService = new GenericWorkflowService.GenericWorkflowService(logger, baseUrl);
31
+ httpRouter.use(
32
+ await router.createRouter({
33
+ workflowService,
34
+ tokenService,
35
+ authEnabled
36
+ })
37
+ );
38
+ }
39
+ });
40
+ }
41
+ });
42
+
43
+ exports.openchoreoWorkflowsBackendPlugin = openchoreoWorkflowsBackendPlugin;
44
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './router';\nimport { openChoreoTokenServiceRef } from '@openchoreo/openchoreo-auth';\nimport { GenericWorkflowService } from './services/GenericWorkflowService';\n\n/**\n * OpenChoreo Generic Workflows Backend Plugin\n *\n * This plugin provides backend APIs for org-level generic workflows:\n * - Listing workflow templates\n * - Getting workflow schemas for dynamic form generation\n * - Listing workflow runs\n * - Getting workflow run details\n * - Triggering new workflow runs\n *\n * @public\n */\nexport const openchoreoWorkflowsBackendPlugin = createBackendPlugin({\n pluginId: 'openchoreo-workflows-backend',\n register(env) {\n env.registerInit({\n deps: {\n httpAuth: coreServices.httpAuth,\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n tokenService: openChoreoTokenServiceRef,\n },\n async init({ httpRouter, logger, config, tokenService }) {\n const openchoreoConfig = config.getOptionalConfig('openchoreo');\n\n if (!openchoreoConfig) {\n logger.info(\n 'OpenChoreo Workflows backend plugin disabled - no configuration found',\n );\n return;\n }\n\n const baseUrl = openchoreoConfig.getString('baseUrl');\n\n // Check if auth feature is enabled (defaults to true)\n // When auth is enabled, mutating operations require a valid user token\n const authEnabled =\n config.getOptionalBoolean('openchoreo.features.auth.enabled') ?? true;\n\n logger.info('Initializing OpenChoreo Workflows backend plugin');\n\n // Initialize service\n const workflowService = new GenericWorkflowService(logger, baseUrl);\n\n httpRouter.use(\n await createRouter({\n workflowService,\n tokenService,\n authEnabled,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","openChoreoTokenServiceRef","GenericWorkflowService","createRouter"],"mappings":";;;;;;;AAoBO,MAAM,mCAAmCA,oCAAA,CAAoB;AAAA,EAClE,QAAA,EAAU,8BAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,UAAUC,6BAAA,CAAa,QAAA;AAAA,QACvB,YAAYA,6BAAA,CAAa,UAAA;AAAA,QACzB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,YAAA,EAAcC;AAAA,OAChB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,YAAY,MAAA,EAAQ,MAAA,EAAQ,cAAa,EAAG;AACvD,QAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,iBAAA,CAAkB,YAAY,CAAA;AAE9D,QAAA,IAAI,CAAC,gBAAA,EAAkB;AACrB,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WACF;AACA,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,SAAA,CAAU,SAAS,CAAA;AAIpD,QAAA,MAAM,WAAA,GACJ,MAAA,CAAO,kBAAA,CAAmB,kCAAkC,CAAA,IAAK,IAAA;AAEnE,QAAA,MAAA,CAAO,KAAK,kDAAkD,CAAA;AAG9D,QAAA,MAAM,eAAA,GAAkB,IAAIC,6CAAA,CAAuB,MAAA,EAAQ,OAAO,CAAA;AAElE,QAAA,UAAA,CAAW,GAAA;AAAA,UACT,MAAMC,mBAAA,CAAa;AAAA,YACjB,eAAA;AAAA,YACA,YAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
@@ -0,0 +1,177 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@backstage/errors');
4
+ var express = require('express');
5
+ var Router = require('express-promise-router');
6
+ var openchoreoAuth = require('@openchoreo/openchoreo-auth');
7
+
8
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
9
+
10
+ var express__default = /*#__PURE__*/_interopDefaultCompat(express);
11
+ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
12
+
13
+ async function createRouter({
14
+ workflowService,
15
+ tokenService,
16
+ authEnabled
17
+ }) {
18
+ const router = Router__default.default();
19
+ router.use(express__default.default.json());
20
+ router.use(openchoreoAuth.createUserTokenMiddleware(tokenService));
21
+ const requireAuth = openchoreoAuth.createRequireAuthMiddleware(tokenService, authEnabled);
22
+ router.get("/workflows", async (req, res) => {
23
+ const { namespaceName } = req.query;
24
+ if (!namespaceName) {
25
+ throw new errors.InputError("namespaceName is required query parameter");
26
+ }
27
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
28
+ res.json(
29
+ await workflowService.listWorkflows(namespaceName, userToken)
30
+ );
31
+ });
32
+ router.get("/workflows/:workflowName/schema", async (req, res) => {
33
+ const { workflowName } = req.params;
34
+ const { namespaceName } = req.query;
35
+ if (!namespaceName) {
36
+ throw new errors.InputError("namespaceName is required query parameter");
37
+ }
38
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
39
+ res.json(
40
+ await workflowService.getWorkflowSchema(
41
+ namespaceName,
42
+ workflowName,
43
+ userToken
44
+ )
45
+ );
46
+ });
47
+ router.get(
48
+ "/cluster-workflows/:clusterWorkflowName/schema",
49
+ async (req, res) => {
50
+ const { clusterWorkflowName } = req.params;
51
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
52
+ res.json(
53
+ await workflowService.getClusterWorkflowSchema(
54
+ clusterWorkflowName,
55
+ userToken
56
+ )
57
+ );
58
+ }
59
+ );
60
+ router.get("/namespaces", async (req, res) => {
61
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
62
+ res.json(await workflowService.listNamespaces(userToken));
63
+ });
64
+ router.get("/workflow-runs", async (req, res) => {
65
+ const { namespaceName, workflowName, projectName, componentName } = req.query;
66
+ if (!namespaceName) {
67
+ throw new errors.InputError("namespaceName is required query parameter");
68
+ }
69
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
70
+ res.json(
71
+ await workflowService.listWorkflowRuns(
72
+ namespaceName,
73
+ workflowName,
74
+ userToken,
75
+ projectName,
76
+ componentName
77
+ )
78
+ );
79
+ });
80
+ router.get("/workflow-runs/:runName", async (req, res) => {
81
+ const { runName } = req.params;
82
+ const { namespaceName } = req.query;
83
+ if (!namespaceName) {
84
+ throw new errors.InputError("namespaceName is required query parameter");
85
+ }
86
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
87
+ res.json(
88
+ await workflowService.getWorkflowRun(
89
+ namespaceName,
90
+ runName,
91
+ userToken
92
+ )
93
+ );
94
+ });
95
+ router.get("/workflow-runs/:runName/logs", async (req, res) => {
96
+ const { runName } = req.params;
97
+ const { namespaceName, task } = req.query;
98
+ if (!namespaceName) {
99
+ throw new errors.InputError("namespaceName is required query parameter");
100
+ }
101
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
102
+ const logs = await workflowService.getWorkflowRunLogs(
103
+ namespaceName,
104
+ runName,
105
+ task,
106
+ userToken
107
+ );
108
+ res.json(logs);
109
+ });
110
+ router.get("/workflow-runs/:runName/status", async (req, res) => {
111
+ const { runName } = req.params;
112
+ const { namespaceName } = req.query;
113
+ if (!namespaceName) {
114
+ throw new errors.InputError("namespaceName is required query parameter");
115
+ }
116
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
117
+ res.json(
118
+ await workflowService.getWorkflowRunStatus(
119
+ namespaceName,
120
+ runName,
121
+ userToken
122
+ )
123
+ );
124
+ });
125
+ router.get("/workflow-runs/:runName/events", async (req, res) => {
126
+ const { runName } = req.params;
127
+ const { namespaceName, task } = req.query;
128
+ if (!namespaceName) {
129
+ throw new errors.InputError("namespaceName is required query parameter");
130
+ }
131
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
132
+ res.json(
133
+ await workflowService.getWorkflowRunEvents(
134
+ namespaceName,
135
+ runName,
136
+ task,
137
+ userToken
138
+ )
139
+ );
140
+ });
141
+ router.post("/workflow-runs", requireAuth, async (req, res) => {
142
+ const { namespaceName } = req.query;
143
+ const {
144
+ workflowName,
145
+ workflowKind,
146
+ parameters,
147
+ labels,
148
+ annotations,
149
+ workflowRunName
150
+ } = req.body;
151
+ if (!namespaceName) {
152
+ throw new errors.InputError("namespaceName is required query parameter");
153
+ }
154
+ if (!workflowName) {
155
+ throw new errors.InputError("workflowName is required in request body");
156
+ }
157
+ const userToken = openchoreoAuth.getUserTokenFromRequest(req);
158
+ res.json(
159
+ await workflowService.createWorkflowRun(
160
+ namespaceName,
161
+ {
162
+ workflowName,
163
+ workflowKind,
164
+ parameters,
165
+ labels,
166
+ annotations,
167
+ workflowRunName
168
+ },
169
+ userToken
170
+ )
171
+ );
172
+ });
173
+ return router;
174
+ }
175
+
176
+ exports.createRouter = createRouter;
177
+ //# sourceMappingURL=router.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["import { InputError } from '@backstage/errors';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { GenericWorkflowService } from './services';\nimport {\n OpenChoreoTokenService,\n createUserTokenMiddleware,\n getUserTokenFromRequest,\n createRequireAuthMiddleware,\n} from '@openchoreo/openchoreo-auth';\n\nexport async function createRouter({\n workflowService,\n tokenService,\n authEnabled,\n}: {\n workflowService: GenericWorkflowService;\n tokenService: OpenChoreoTokenService;\n authEnabled: boolean;\n}): Promise<express.Router> {\n const router = Router();\n router.use(express.json());\n\n // Add middleware to extract and cache user's IDP token from request headers\n router.use(createUserTokenMiddleware(tokenService));\n\n // Middleware to require authentication for mutating operations\n const requireAuth = createRequireAuthMiddleware(tokenService, authEnabled);\n\n // GET /workflows - List all workflow templates\n router.get('/workflows', async (req, res) => {\n const { namespaceName } = req.query;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.listWorkflows(namespaceName as string, userToken),\n );\n });\n\n // GET /workflows/:workflowName/schema - Get workflow parameter schema\n router.get('/workflows/:workflowName/schema', async (req, res) => {\n const { workflowName } = req.params;\n const { namespaceName } = req.query;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.getWorkflowSchema(\n namespaceName as string,\n workflowName,\n userToken,\n ),\n );\n });\n\n // GET /cluster-workflows/:clusterWorkflowName/schema - Get cluster workflow parameter schema\n router.get(\n '/cluster-workflows/:clusterWorkflowName/schema',\n async (req, res) => {\n const { clusterWorkflowName } = req.params;\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.getClusterWorkflowSchema(\n clusterWorkflowName,\n userToken,\n ),\n );\n },\n );\n\n // GET /namespaces - List OpenChoreo namespaces (for ClusterWorkflow namespace selector)\n router.get('/namespaces', async (req, res) => {\n const userToken = getUserTokenFromRequest(req);\n res.json(await workflowService.listNamespaces(userToken));\n });\n\n // GET /workflow-runs - List workflow runs (with optional workflow/project/component filter)\n router.get('/workflow-runs', async (req, res) => {\n const { namespaceName, workflowName, projectName, componentName } =\n req.query;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.listWorkflowRuns(\n namespaceName as string,\n workflowName as string | undefined,\n userToken,\n projectName as string | undefined,\n componentName as string | undefined,\n ),\n );\n });\n\n // GET /workflow-runs/:runName - Get workflow run details\n router.get('/workflow-runs/:runName', async (req, res) => {\n const { runName } = req.params;\n const { namespaceName } = req.query;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.getWorkflowRun(\n namespaceName as string,\n runName,\n userToken,\n ),\n );\n });\n\n // GET /workflow-runs/:runName/logs - Get workflow run logs\n router.get('/workflow-runs/:runName/logs', async (req, res) => {\n const { runName } = req.params;\n const { namespaceName, task } = req.query;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n const logs = await workflowService.getWorkflowRunLogs(\n namespaceName as string,\n runName,\n task as string | undefined,\n userToken,\n );\n res.json(logs);\n });\n\n // GET /workflow-runs/:runName/status - Get workflow run status (with steps)\n router.get('/workflow-runs/:runName/status', async (req, res) => {\n const { runName } = req.params;\n const { namespaceName } = req.query;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.getWorkflowRunStatus(\n namespaceName as string,\n runName,\n userToken,\n ),\n );\n });\n\n // GET /workflow-runs/:runName/events - Get workflow run Kubernetes events\n router.get('/workflow-runs/:runName/events', async (req, res) => {\n const { runName } = req.params;\n const { namespaceName, task } = req.query;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.getWorkflowRunEvents(\n namespaceName as string,\n runName,\n task as string | undefined,\n userToken,\n ),\n );\n });\n\n // POST /workflow-runs - Create (trigger) a new workflow run\n router.post('/workflow-runs', requireAuth, async (req, res) => {\n const { namespaceName } = req.query;\n const {\n workflowName,\n workflowKind,\n parameters,\n labels,\n annotations,\n workflowRunName,\n } = req.body;\n\n if (!namespaceName) {\n throw new InputError('namespaceName is required query parameter');\n }\n\n if (!workflowName) {\n throw new InputError('workflowName is required in request body');\n }\n\n const userToken = getUserTokenFromRequest(req);\n\n res.json(\n await workflowService.createWorkflowRun(\n namespaceName as string,\n {\n workflowName,\n workflowKind,\n parameters,\n labels,\n annotations,\n workflowRunName,\n },\n userToken,\n ),\n );\n });\n\n return router;\n}\n"],"names":["Router","express","createUserTokenMiddleware","createRequireAuthMiddleware","InputError","getUserTokenFromRequest"],"mappings":"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"}
@@ -0,0 +1,522 @@
1
+ 'use strict';
2
+
3
+ var openchoreoClientNode = require('@openchoreo/openchoreo-client-node');
4
+ var backstagePluginCommon = require('@openchoreo/backstage-plugin-common');
5
+
6
+ function deriveWorkflowRunStatus(run) {
7
+ const conditions = run.status?.conditions ?? [];
8
+ if (conditions.length === 0) {
9
+ return "Pending";
10
+ }
11
+ if (conditions.some((c) => c.type === "WorkloadUpdated" && c.status === "True")) {
12
+ return "Completed";
13
+ }
14
+ if (conditions.some((c) => c.type === "WorkflowFailed" && c.status === "True")) {
15
+ return "Failed";
16
+ }
17
+ if (conditions.some((c) => c.type === "WorkflowSucceeded" && c.status === "True")) {
18
+ return "Succeeded";
19
+ }
20
+ if (conditions.some((c) => c.type === "WorkflowRunning" && c.status === "True")) {
21
+ return "Running";
22
+ }
23
+ return "Pending";
24
+ }
25
+ function transformWorkflowRun(run) {
26
+ return {
27
+ name: run.metadata?.name ?? "",
28
+ uuid: run.metadata?.uid,
29
+ workflowName: run.spec?.workflow?.name ?? "",
30
+ namespaceName: run.metadata?.namespace ?? "",
31
+ status: deriveWorkflowRunStatus(run),
32
+ parameters: run.spec?.workflow?.parameters,
33
+ createdAt: run.metadata?.creationTimestamp,
34
+ finishedAt: run.status?.completedAt
35
+ };
36
+ }
37
+ const TERMINAL_STATUSES = /* @__PURE__ */ new Set(["Completed", "Failed", "Succeeded"]);
38
+ class GenericWorkflowService {
39
+ logger;
40
+ baseUrl;
41
+ resolver;
42
+ constructor(logger, baseUrl) {
43
+ this.logger = logger;
44
+ this.baseUrl = baseUrl;
45
+ this.resolver = new openchoreoClientNode.ObservabilityUrlResolver({ baseUrl, logger });
46
+ }
47
+ /**
48
+ * List all generic workflow templates for a namespace
49
+ */
50
+ async listWorkflows(namespaceName, token) {
51
+ this.logger.debug(
52
+ `Fetching generic workflows for namespace: ${namespaceName}`
53
+ );
54
+ try {
55
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
56
+ baseUrl: this.baseUrl,
57
+ token,
58
+ logger: this.logger
59
+ });
60
+ const { data, error, response } = await client.GET(
61
+ "/api/v1/namespaces/{namespaceName}/workflows",
62
+ {
63
+ params: {
64
+ path: { namespaceName }
65
+ }
66
+ }
67
+ );
68
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "fetch workflows");
69
+ const items = (data?.items || []).map((wf) => {
70
+ const name = wf.metadata?.name ?? "";
71
+ const isCI = wf.metadata?.labels?.[backstagePluginCommon.CHOREO_LABELS.WORKFLOW_TYPE] === "component";
72
+ return {
73
+ name,
74
+ displayName: wf.metadata?.annotations?.["openchoreo.dev/display-name"] ?? name,
75
+ description: wf.metadata?.annotations?.["openchoreo.dev/description"],
76
+ createdAt: wf.metadata?.creationTimestamp,
77
+ type: isCI ? "CI" : "Generic"
78
+ };
79
+ });
80
+ this.logger.debug(
81
+ `Successfully fetched ${items.length} generic workflows for namespace: ${namespaceName}`
82
+ );
83
+ return {
84
+ items,
85
+ pagination: data?.pagination
86
+ };
87
+ } catch (error) {
88
+ this.logger.error(
89
+ `Failed to fetch generic workflows for namespace ${namespaceName}: ${error}`
90
+ );
91
+ throw error;
92
+ }
93
+ }
94
+ /**
95
+ * Get the JSONSchema for a workflow's parameters
96
+ */
97
+ async getWorkflowSchema(namespaceName, workflowName, token) {
98
+ this.logger.debug(
99
+ `Fetching schema for workflow: ${workflowName} in namespace: ${namespaceName}`
100
+ );
101
+ try {
102
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
103
+ baseUrl: this.baseUrl,
104
+ token,
105
+ logger: this.logger
106
+ });
107
+ const { data, error, response } = await client.GET(
108
+ "/api/v1/namespaces/{namespaceName}/workflows/{workflowName}/schema",
109
+ {
110
+ params: {
111
+ path: { namespaceName, workflowName }
112
+ }
113
+ }
114
+ );
115
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "fetch workflow schema");
116
+ this.logger.debug(
117
+ `Successfully fetched schema for workflow: ${workflowName}`
118
+ );
119
+ return data;
120
+ } catch (error) {
121
+ this.logger.error(
122
+ `Failed to fetch schema for workflow ${workflowName} in namespace ${namespaceName}: ${error}`
123
+ );
124
+ throw error;
125
+ }
126
+ }
127
+ /**
128
+ * Get the JSONSchema for a cluster-scoped workflow's parameters
129
+ */
130
+ async getClusterWorkflowSchema(clusterWorkflowName, token) {
131
+ this.logger.debug(
132
+ `Fetching schema for cluster workflow: ${clusterWorkflowName}`
133
+ );
134
+ try {
135
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
136
+ baseUrl: this.baseUrl,
137
+ token,
138
+ logger: this.logger
139
+ });
140
+ const { data, error, response } = await client.GET(
141
+ "/api/v1/clusterworkflows/{clusterWorkflowName}/schema",
142
+ {
143
+ params: {
144
+ path: { clusterWorkflowName }
145
+ }
146
+ }
147
+ );
148
+ openchoreoClientNode.assertApiResponse(
149
+ { data, error, response },
150
+ "fetch cluster workflow schema"
151
+ );
152
+ this.logger.debug(
153
+ `Successfully fetched schema for cluster workflow: ${clusterWorkflowName}`
154
+ );
155
+ return data;
156
+ } catch (error) {
157
+ this.logger.error(
158
+ `Failed to fetch schema for cluster workflow ${clusterWorkflowName}: ${error}`
159
+ );
160
+ throw error;
161
+ }
162
+ }
163
+ /**
164
+ * List workflow runs for a namespace, optionally filtered by workflow name
165
+ */
166
+ async listWorkflowRuns(namespaceName, workflowName, token, projectName, componentName) {
167
+ this.logger.debug(
168
+ `Fetching workflow runs for namespace: ${namespaceName}${workflowName ? `, workflow: ${workflowName}` : ""}`
169
+ );
170
+ try {
171
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
172
+ baseUrl: this.baseUrl,
173
+ token,
174
+ logger: this.logger
175
+ });
176
+ const { data, error, response } = await client.GET(
177
+ "/api/v1/namespaces/{namespaceName}/workflowruns",
178
+ {
179
+ params: {
180
+ path: { namespaceName }
181
+ }
182
+ }
183
+ );
184
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "fetch workflow runs");
185
+ const rawItems = data?.items || [];
186
+ let filtered = workflowName ? rawItems.filter(
187
+ (run) => run.spec?.workflow?.name === workflowName || run.workflowName === workflowName
188
+ ) : rawItems;
189
+ if (projectName) {
190
+ filtered = filtered.filter(
191
+ (run) => run.metadata?.labels?.[backstagePluginCommon.CHOREO_LABELS.WORKFLOW_PROJECT] === projectName
192
+ );
193
+ }
194
+ if (componentName) {
195
+ filtered = filtered.filter(
196
+ (run) => run.metadata?.labels?.[backstagePluginCommon.CHOREO_LABELS.WORKFLOW_COMPONENT] === componentName
197
+ );
198
+ }
199
+ const items = filtered.map(transformWorkflowRun);
200
+ this.logger.debug(
201
+ `Successfully fetched ${items.length} workflow runs for namespace: ${namespaceName}${workflowName ? `, workflow: ${workflowName}` : ""}`
202
+ );
203
+ return {
204
+ items,
205
+ pagination: data?.pagination
206
+ };
207
+ } catch (error) {
208
+ this.logger.error(
209
+ `Failed to fetch workflow runs for namespace ${namespaceName}: ${error}`
210
+ );
211
+ throw error;
212
+ }
213
+ }
214
+ /**
215
+ * List OpenChoreo namespaces (those labeled openchoreo.dev/control-plane=true).
216
+ * Used to populate the namespace selector on the ClusterWorkflow runs page.
217
+ */
218
+ async listNamespaces(token) {
219
+ this.logger.debug("Fetching OpenChoreo namespaces");
220
+ try {
221
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
222
+ baseUrl: this.baseUrl,
223
+ token,
224
+ logger: this.logger
225
+ });
226
+ const { data, error, response } = await client.GET("/api/v1/namespaces");
227
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "fetch namespaces");
228
+ const names = (data?.items || []).map((ns) => ns.metadata?.name ?? "").filter(Boolean);
229
+ this.logger.debug(`Successfully fetched ${names.length} namespaces`);
230
+ return names;
231
+ } catch (error) {
232
+ this.logger.error(`Failed to fetch namespaces: ${error}`);
233
+ throw error;
234
+ }
235
+ }
236
+ /**
237
+ * Get details of a specific workflow run
238
+ */
239
+ async getWorkflowRun(namespaceName, runName, token) {
240
+ this.logger.debug(
241
+ `Fetching workflow run: ${runName} in namespace: ${namespaceName}`
242
+ );
243
+ try {
244
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
245
+ baseUrl: this.baseUrl,
246
+ token,
247
+ logger: this.logger
248
+ });
249
+ const { data, error, response } = await client.GET(
250
+ "/api/v1/namespaces/{namespaceName}/workflowruns/{runName}",
251
+ {
252
+ params: {
253
+ path: { namespaceName, runName }
254
+ }
255
+ }
256
+ );
257
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "fetch workflow run");
258
+ this.logger.debug(`Successfully fetched workflow run: ${runName}`);
259
+ return transformWorkflowRun(data);
260
+ } catch (error) {
261
+ this.logger.error(
262
+ `Failed to fetch workflow run ${runName} in namespace ${namespaceName}: ${error}`
263
+ );
264
+ throw error;
265
+ }
266
+ }
267
+ /**
268
+ * Create (trigger) a new workflow run
269
+ */
270
+ async createWorkflowRun(namespaceName, request, token) {
271
+ this.logger.info(
272
+ `Creating workflow run for ${request.workflowKind === "ClusterWorkflow" ? "cluster workflow" : "workflow"}: ${request.workflowName} in namespace: ${namespaceName}`
273
+ );
274
+ try {
275
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
276
+ baseUrl: this.baseUrl,
277
+ token,
278
+ logger: this.logger
279
+ });
280
+ const { data, error, response } = await client.POST(
281
+ "/api/v1/namespaces/{namespaceName}/workflowruns",
282
+ {
283
+ params: {
284
+ path: { namespaceName }
285
+ },
286
+ body: {
287
+ metadata: {
288
+ name: request.workflowRunName?.trim() || `${request.workflowName}-${Date.now()}`,
289
+ ...request.labels && { labels: request.labels },
290
+ ...request.annotations && { annotations: request.annotations }
291
+ },
292
+ spec: {
293
+ workflow: {
294
+ kind: request.workflowKind,
295
+ name: request.workflowName,
296
+ parameters: request.parameters
297
+ }
298
+ }
299
+ }
300
+ }
301
+ );
302
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "create workflow run");
303
+ this.logger.debug(
304
+ `Successfully created workflow run: ${data.metadata?.name}`
305
+ );
306
+ return transformWorkflowRun(data);
307
+ } catch (error) {
308
+ this.logger.error(
309
+ `Failed to create workflow run for ${request.workflowKind === "ClusterWorkflow" ? "cluster workflow" : "workflow"} ${request.workflowName} in namespace ${namespaceName}: ${error}`
310
+ );
311
+ throw error;
312
+ }
313
+ }
314
+ /**
315
+ * Get logs for a specific workflow run.
316
+ */
317
+ async getWorkflowRunLogs(namespaceName, runName, task, token) {
318
+ this.logger.debug(
319
+ `Fetching logs for workflow run: ${runName} in namespace: ${namespaceName}${task ? `, task: ${task}` : ""}`
320
+ );
321
+ try {
322
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
323
+ baseUrl: this.baseUrl,
324
+ token,
325
+ logger: this.logger
326
+ });
327
+ const { data, error, response } = await client.GET(
328
+ "/api/v1/namespaces/{namespaceName}/workflowruns/{runName}/logs",
329
+ {
330
+ params: {
331
+ path: { namespaceName, runName },
332
+ query: { ...task ? { task } : {} }
333
+ }
334
+ }
335
+ );
336
+ if (!error && response.ok && Array.isArray(data) && data.length > 0) {
337
+ const logs = data.map((entry) => ({
338
+ timestamp: entry.timestamp ?? "",
339
+ log: entry.log
340
+ }));
341
+ this.logger.debug(
342
+ `Fetched ${logs.length} live log entries for run: ${runName}`
343
+ );
344
+ return { logs, totalCount: logs.length };
345
+ }
346
+ const {
347
+ data: runData,
348
+ error: runError,
349
+ response: runResponse
350
+ } = await client.GET(
351
+ "/api/v1/namespaces/{namespaceName}/workflowruns/{runName}",
352
+ { params: { path: { namespaceName, runName } } }
353
+ );
354
+ if (runError || !runResponse.ok || !runData) {
355
+ return { logs: [], totalCount: 0 };
356
+ }
357
+ const runStatus = deriveWorkflowRunStatus(runData);
358
+ if (!TERMINAL_STATUSES.has(runStatus)) {
359
+ return { logs: [], totalCount: 0 };
360
+ }
361
+ let observerUrl;
362
+ const projectName = runData.metadata?.labels?.[backstagePluginCommon.CHOREO_LABELS.WORKFLOW_PROJECT];
363
+ try {
364
+ if (projectName) {
365
+ ({ observerUrl } = await this.resolver.resolveForBuild(
366
+ namespaceName,
367
+ projectName,
368
+ token
369
+ ));
370
+ } else {
371
+ ({ observerUrl } = await this.resolver.resolveForEnvironment(
372
+ namespaceName,
373
+ "development",
374
+ token
375
+ ));
376
+ }
377
+ } catch (resolveErr) {
378
+ this.logger.debug(
379
+ `Could not resolve observer URL for run ${runName}: ${resolveErr}`
380
+ );
381
+ return { logs: [], totalCount: 0 };
382
+ }
383
+ if (!observerUrl) {
384
+ return { logs: [], totalCount: 0 };
385
+ }
386
+ const obsClient = openchoreoClientNode.createObservabilityClientWithUrl(
387
+ observerUrl,
388
+ token,
389
+ this.logger
390
+ );
391
+ const startTime = new Date(
392
+ Date.now() - 30 * 24 * 60 * 60 * 1e3
393
+ ).toISOString();
394
+ const endTime = (/* @__PURE__ */ new Date()).toISOString();
395
+ this.logger.debug(
396
+ `Querying observer logs for run ${runName}, namespace: ${namespaceName}`
397
+ );
398
+ const {
399
+ data: obsData,
400
+ error: obsError,
401
+ response: obsResponse
402
+ } = await obsClient.POST("/api/v1/logs/query", {
403
+ body: {
404
+ startTime,
405
+ endTime,
406
+ limit: 1e3,
407
+ sortOrder: "asc",
408
+ searchScope: {
409
+ namespace: namespaceName,
410
+ workflowRunName: runName,
411
+ ...task ? { taskName: task } : {}
412
+ }
413
+ }
414
+ });
415
+ if (obsError || !obsResponse.ok) {
416
+ this.logger.error(
417
+ `Failed to fetch observer logs for run ${runName}: ${obsResponse.status} ${obsResponse.statusText}`
418
+ );
419
+ return { logs: [], totalCount: 0 };
420
+ }
421
+ const obsLogs = (obsData?.logs || []).map((entry) => ({
422
+ timestamp: entry.timestamp ?? "",
423
+ log: entry.log
424
+ }));
425
+ this.logger.debug(
426
+ `Observer fetched ${obsLogs.length} log entries for run: ${runName}`
427
+ );
428
+ return {
429
+ logs: obsLogs,
430
+ totalCount: obsData?.total ?? obsLogs.length,
431
+ tookMs: obsData?.tookMs
432
+ };
433
+ } catch (error) {
434
+ this.logger.error(
435
+ `Error fetching logs for workflow run ${runName} in namespace ${namespaceName}:`,
436
+ error
437
+ );
438
+ throw error;
439
+ }
440
+ }
441
+ /**
442
+ * Get status (including steps) for a specific workflow run
443
+ */
444
+ async getWorkflowRunStatus(namespaceName, runName, token) {
445
+ this.logger.debug(
446
+ `Fetching status for workflow run: ${runName} in namespace: ${namespaceName}`
447
+ );
448
+ try {
449
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
450
+ baseUrl: this.baseUrl,
451
+ token,
452
+ logger: this.logger
453
+ });
454
+ const { data, error, response } = await client.GET(
455
+ "/api/v1/namespaces/{namespaceName}/workflowruns/{runName}/status",
456
+ {
457
+ params: {
458
+ path: { namespaceName, runName }
459
+ }
460
+ }
461
+ );
462
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "fetch workflow run status");
463
+ this.logger.debug(
464
+ `Successfully fetched status for workflow run: ${runName}`
465
+ );
466
+ return data;
467
+ } catch (error) {
468
+ this.logger.error(
469
+ `Failed to fetch status for workflow run ${runName} in namespace ${namespaceName}: ${error}`
470
+ );
471
+ throw error;
472
+ }
473
+ }
474
+ /**
475
+ * Get Kubernetes events for a specific workflow run (optionally filtered by task)
476
+ */
477
+ async getWorkflowRunEvents(namespaceName, runName, task, token) {
478
+ this.logger.debug(
479
+ `Fetching events for workflow run: ${runName} in namespace: ${namespaceName}${task ? `, task: ${task}` : ""}`
480
+ );
481
+ try {
482
+ const client = openchoreoClientNode.createOpenChoreoApiClient({
483
+ baseUrl: this.baseUrl,
484
+ token,
485
+ logger: this.logger
486
+ });
487
+ const { data, error, response } = await client.GET(
488
+ "/api/v1/namespaces/{namespaceName}/workflowruns/{runName}/events",
489
+ {
490
+ params: {
491
+ path: { namespaceName, runName },
492
+ query: {
493
+ ...task ? { task } : {}
494
+ }
495
+ }
496
+ }
497
+ );
498
+ openchoreoClientNode.assertApiResponse({ data, error, response }, "fetch workflow run events");
499
+ if (!Array.isArray(data)) {
500
+ return [];
501
+ }
502
+ const entries = data.map((entry) => ({
503
+ timestamp: entry.timestamp,
504
+ type: entry.type,
505
+ reason: entry.reason,
506
+ message: entry.message
507
+ }));
508
+ this.logger.debug(
509
+ `Successfully fetched ${entries.length} events for workflow run: ${runName}`
510
+ );
511
+ return entries;
512
+ } catch (error) {
513
+ this.logger.error(
514
+ `Failed to fetch events for workflow run ${runName} in namespace ${namespaceName}: ${error}`
515
+ );
516
+ throw error;
517
+ }
518
+ }
519
+ }
520
+
521
+ exports.GenericWorkflowService = GenericWorkflowService;
522
+ //# sourceMappingURL=GenericWorkflowService.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenericWorkflowService.cjs.js","sources":["../../src/services/GenericWorkflowService.ts"],"sourcesContent":["import { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n createOpenChoreoApiClient,\n createObservabilityClientWithUrl,\n assertApiResponse,\n ObservabilityUrlResolver,\n} from '@openchoreo/openchoreo-client-node';\nimport { CHOREO_LABELS } from '@openchoreo/backstage-plugin-common';\nimport {\n Workflow,\n WorkflowRun,\n CreateWorkflowRunRequest,\n PaginatedResponse,\n LogsResponse,\n WorkflowRunStatusResponse,\n WorkflowRunEventEntry,\n} from '../types';\n\n/**\n * Derive a display status from a raw K8s-style WorkflowRun object.\n *\n * Checks conditions in priority order, matching the Go-side\n * getComponentWorkflowStatus logic:\n * 1. WorkloadUpdated + True → Completed\n * 2. WorkflowFailed + True → Failed\n * 3. WorkflowSucceeded + True → Succeeded\n * 4. WorkflowRunning + True → Running\n * 5. Default → Pending\n */\nfunction deriveWorkflowRunStatus(run: any): string {\n const conditions = (run.status?.conditions ?? []) as any[];\n\n if (conditions.length === 0) {\n return 'Pending';\n }\n\n if (\n conditions.some(c => c.type === 'WorkloadUpdated' && c.status === 'True')\n ) {\n return 'Completed';\n }\n\n if (\n conditions.some(c => c.type === 'WorkflowFailed' && c.status === 'True')\n ) {\n return 'Failed';\n }\n\n if (\n conditions.some(c => c.type === 'WorkflowSucceeded' && c.status === 'True')\n ) {\n return 'Succeeded';\n }\n\n if (\n conditions.some(c => c.type === 'WorkflowRunning' && c.status === 'True')\n ) {\n return 'Running';\n }\n\n return 'Pending';\n}\n\n/**\n * Transform a raw K8s WorkflowRun object into the local flat WorkflowRun shape.\n */\nfunction transformWorkflowRun(run: any): import('../types').WorkflowRun {\n return {\n name: run.metadata?.name ?? '',\n uuid: run.metadata?.uid,\n workflowName: run.spec?.workflow?.name ?? '',\n namespaceName: run.metadata?.namespace ?? '',\n status: deriveWorkflowRunStatus(run),\n parameters: run.spec?.workflow?.parameters,\n createdAt: run.metadata?.creationTimestamp,\n finishedAt: run.status?.completedAt,\n };\n}\n\n/**\n * Service for managing namespace-level generic workflows\n * Handles workflow templates, runs, and schemas\n *\n * After the Organization CRD removal, the hierarchy is now:\n * Namespace → Project → Component\n */\nconst TERMINAL_STATUSES = new Set(['Completed', 'Failed', 'Succeeded']);\n\nexport class GenericWorkflowService {\n private logger: LoggerService;\n private baseUrl: string;\n private readonly resolver: ObservabilityUrlResolver;\n\n constructor(logger: LoggerService, baseUrl: string) {\n this.logger = logger;\n this.baseUrl = baseUrl;\n this.resolver = new ObservabilityUrlResolver({ baseUrl, logger });\n }\n\n /**\n * List all generic workflow templates for a namespace\n */\n async listWorkflows(\n namespaceName: string,\n token?: string,\n ): Promise<PaginatedResponse<Workflow>> {\n this.logger.debug(\n `Fetching generic workflows for namespace: ${namespaceName}`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflows',\n {\n params: {\n path: { namespaceName },\n },\n },\n );\n\n assertApiResponse({ data, error, response }, 'fetch workflows');\n\n // Map K8s-style Workflow to the local flat Workflow interface\n const items: Workflow[] = ((data as any)?.items || []).map((wf: any) => {\n const name: string = wf.metadata?.name ?? '';\n const isCI =\n wf.metadata?.labels?.[CHOREO_LABELS.WORKFLOW_TYPE] === 'component';\n return {\n name,\n displayName:\n wf.metadata?.annotations?.['openchoreo.dev/display-name'] ?? name,\n description: wf.metadata?.annotations?.['openchoreo.dev/description'],\n createdAt: wf.metadata?.creationTimestamp,\n type: isCI ? 'CI' : 'Generic',\n };\n });\n\n this.logger.debug(\n `Successfully fetched ${items.length} generic workflows for namespace: ${namespaceName}`,\n );\n\n return {\n items,\n pagination: (data as any)?.pagination as\n | { nextCursor?: string }\n | undefined,\n };\n } catch (error) {\n this.logger.error(\n `Failed to fetch generic workflows for namespace ${namespaceName}: ${error}`,\n );\n throw error;\n }\n }\n\n /**\n * Get the JSONSchema for a workflow's parameters\n */\n async getWorkflowSchema(\n namespaceName: string,\n workflowName: string,\n token?: string,\n ): Promise<unknown> {\n this.logger.debug(\n `Fetching schema for workflow: ${workflowName} in namespace: ${namespaceName}`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflows/{workflowName}/schema',\n {\n params: {\n path: { namespaceName, workflowName },\n },\n },\n );\n\n assertApiResponse({ data, error, response }, 'fetch workflow schema');\n\n this.logger.debug(\n `Successfully fetched schema for workflow: ${workflowName}`,\n );\n\n return data;\n } catch (error) {\n this.logger.error(\n `Failed to fetch schema for workflow ${workflowName} in namespace ${namespaceName}: ${error}`,\n );\n throw error;\n }\n }\n\n /**\n * Get the JSONSchema for a cluster-scoped workflow's parameters\n */\n async getClusterWorkflowSchema(\n clusterWorkflowName: string,\n token?: string,\n ): Promise<unknown> {\n this.logger.debug(\n `Fetching schema for cluster workflow: ${clusterWorkflowName}`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/clusterworkflows/{clusterWorkflowName}/schema' as any,\n {\n params: {\n path: { clusterWorkflowName },\n },\n } as any,\n );\n\n assertApiResponse(\n { data, error, response },\n 'fetch cluster workflow schema',\n );\n\n this.logger.debug(\n `Successfully fetched schema for cluster workflow: ${clusterWorkflowName}`,\n );\n\n return data;\n } catch (error) {\n this.logger.error(\n `Failed to fetch schema for cluster workflow ${clusterWorkflowName}: ${error}`,\n );\n throw error;\n }\n }\n\n /**\n * List workflow runs for a namespace, optionally filtered by workflow name\n */\n async listWorkflowRuns(\n namespaceName: string,\n workflowName?: string,\n token?: string,\n projectName?: string,\n componentName?: string,\n ): Promise<PaginatedResponse<WorkflowRun>> {\n this.logger.debug(\n `Fetching workflow runs for namespace: ${namespaceName}${\n workflowName ? `, workflow: ${workflowName}` : ''\n }`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflowruns',\n {\n params: {\n path: { namespaceName },\n },\n },\n );\n\n assertApiResponse({ data, error, response }, 'fetch workflow runs');\n\n const rawItems = ((data as any)?.items || []) as any[];\n\n // Filter by workflowName before transforming (check both flat and K8s fields)\n // TODO: If upstream API supports filtering, pass workflowName as query param instead\n let filtered = workflowName\n ? rawItems.filter(\n run =>\n run.spec?.workflow?.name === workflowName ||\n run.workflowName === workflowName,\n )\n : rawItems;\n\n // Filter by project and component labels if provided\n if (projectName) {\n filtered = filtered.filter(\n run =>\n run.metadata?.labels?.[CHOREO_LABELS.WORKFLOW_PROJECT] ===\n projectName,\n );\n }\n if (componentName) {\n filtered = filtered.filter(\n run =>\n run.metadata?.labels?.[CHOREO_LABELS.WORKFLOW_COMPONENT] ===\n componentName,\n );\n }\n\n const items: WorkflowRun[] = filtered.map(transformWorkflowRun);\n\n this.logger.debug(\n `Successfully fetched ${\n items.length\n } workflow runs for namespace: ${namespaceName}${\n workflowName ? `, workflow: ${workflowName}` : ''\n }`,\n );\n\n return {\n items,\n pagination: (data as any)?.pagination as\n | { nextCursor?: string }\n | undefined,\n };\n } catch (error) {\n this.logger.error(\n `Failed to fetch workflow runs for namespace ${namespaceName}: ${error}`,\n );\n throw error;\n }\n }\n\n /**\n * List OpenChoreo namespaces (those labeled openchoreo.dev/control-plane=true).\n * Used to populate the namespace selector on the ClusterWorkflow runs page.\n */\n async listNamespaces(token?: string): Promise<string[]> {\n this.logger.debug('Fetching OpenChoreo namespaces');\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET('/api/v1/namespaces');\n\n assertApiResponse({ data, error, response }, 'fetch namespaces');\n\n const names: string[] = ((data as any)?.items || [])\n .map((ns: any) => ns.metadata?.name ?? '')\n .filter(Boolean);\n\n this.logger.debug(`Successfully fetched ${names.length} namespaces`);\n\n return names;\n } catch (error) {\n this.logger.error(`Failed to fetch namespaces: ${error}`);\n throw error;\n }\n }\n\n /**\n * Get details of a specific workflow run\n */\n async getWorkflowRun(\n namespaceName: string,\n runName: string,\n token?: string,\n ): Promise<WorkflowRun> {\n this.logger.debug(\n `Fetching workflow run: ${runName} in namespace: ${namespaceName}`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflowruns/{runName}',\n {\n params: {\n path: { namespaceName, runName },\n },\n },\n );\n\n assertApiResponse({ data, error, response }, 'fetch workflow run');\n\n this.logger.debug(`Successfully fetched workflow run: ${runName}`);\n\n return transformWorkflowRun(data);\n } catch (error) {\n this.logger.error(\n `Failed to fetch workflow run ${runName} in namespace ${namespaceName}: ${error}`,\n );\n throw error;\n }\n }\n\n /**\n * Create (trigger) a new workflow run\n */\n async createWorkflowRun(\n namespaceName: string,\n request: CreateWorkflowRunRequest,\n token?: string,\n ): Promise<WorkflowRun> {\n this.logger.info(\n `Creating workflow run for ${\n request.workflowKind === 'ClusterWorkflow'\n ? 'cluster workflow'\n : 'workflow'\n }: ${request.workflowName} in namespace: ${namespaceName}`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n // The WorkflowRunConfig.kind field tells the server whether to look up a\n // namespace-scoped Workflow or a cluster-scoped ClusterWorkflow.\n // Default (omitted / 'Workflow') resolves a namespace-scoped Workflow;\n // 'ClusterWorkflow' resolves a cluster-scoped ClusterWorkflow.\n const { data, error, response } = await client.POST(\n '/api/v1/namespaces/{namespaceName}/workflowruns',\n {\n params: {\n path: { namespaceName },\n },\n body: {\n metadata: {\n name:\n request.workflowRunName?.trim() ||\n `${request.workflowName}-${Date.now()}`,\n ...(request.labels && { labels: request.labels }),\n ...(request.annotations && { annotations: request.annotations }),\n },\n spec: {\n workflow: {\n kind: request.workflowKind,\n name: request.workflowName,\n parameters: request.parameters,\n },\n },\n } as any,\n },\n );\n\n assertApiResponse({ data, error, response }, 'create workflow run');\n\n this.logger.debug(\n `Successfully created workflow run: ${(data as any).metadata?.name}`,\n );\n\n return transformWorkflowRun(data);\n } catch (error) {\n this.logger.error(\n `Failed to create workflow run for ${\n request.workflowKind === 'ClusterWorkflow'\n ? 'cluster workflow'\n : 'workflow'\n } ${request.workflowName} in namespace ${namespaceName}: ${error}`,\n );\n throw error;\n }\n }\n\n /**\n * Get logs for a specific workflow run.\n */\n async getWorkflowRunLogs(\n namespaceName: string,\n runName: string,\n task?: string,\n token?: string,\n ): Promise<LogsResponse> {\n this.logger.debug(\n `Fetching logs for workflow run: ${runName} in namespace: ${namespaceName}${\n task ? `, task: ${task}` : ''\n }`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflowruns/{runName}/logs',\n {\n params: {\n path: { namespaceName, runName },\n query: { ...(task ? { task } : {}) },\n },\n },\n );\n\n if (!error && response.ok && Array.isArray(data) && data.length > 0) {\n const logs = data.map((entry: any) => ({\n timestamp: entry.timestamp ?? '',\n log: entry.log,\n }));\n this.logger.debug(\n `Fetched ${logs.length} live log entries for run: ${runName}`,\n );\n return { logs, totalCount: logs.length };\n }\n\n const {\n data: runData,\n error: runError,\n response: runResponse,\n } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflowruns/{runName}',\n { params: { path: { namespaceName, runName } } },\n );\n\n if (runError || !runResponse.ok || !runData) {\n return { logs: [], totalCount: 0 };\n }\n\n const runStatus = deriveWorkflowRunStatus(runData);\n if (!TERMINAL_STATUSES.has(runStatus)) {\n return { logs: [], totalCount: 0 };\n }\n\n let observerUrl: string | undefined;\n const projectName = (runData as any).metadata?.labels?.[\n CHOREO_LABELS.WORKFLOW_PROJECT\n ];\n try {\n if (projectName) {\n ({ observerUrl } = await this.resolver.resolveForBuild(\n namespaceName,\n projectName,\n token,\n ));\n } else {\n ({ observerUrl } = await this.resolver.resolveForEnvironment(\n namespaceName,\n 'development',\n token,\n ));\n }\n } catch (resolveErr) {\n this.logger.debug(\n `Could not resolve observer URL for run ${runName}: ${resolveErr}`,\n );\n return { logs: [], totalCount: 0 };\n }\n\n if (!observerUrl) {\n return { logs: [], totalCount: 0 };\n }\n\n const obsClient = createObservabilityClientWithUrl(\n observerUrl,\n token,\n this.logger,\n );\n const startTime = new Date(\n Date.now() - 30 * 24 * 60 * 60 * 1000,\n ).toISOString();\n const endTime = new Date().toISOString();\n\n this.logger.debug(\n `Querying observer logs for run ${runName}, namespace: ${namespaceName}`,\n );\n\n const {\n data: obsData,\n error: obsError,\n response: obsResponse,\n } = await obsClient.POST('/api/v1/logs/query', {\n body: {\n startTime,\n endTime,\n limit: 1000,\n sortOrder: 'asc',\n searchScope: {\n namespace: namespaceName,\n workflowRunName: runName,\n ...(task ? { taskName: task } : {}),\n },\n },\n });\n\n if (obsError || !obsResponse.ok) {\n this.logger.error(\n `Failed to fetch observer logs for run ${runName}: ${obsResponse.status} ${obsResponse.statusText}`,\n );\n return { logs: [], totalCount: 0 };\n }\n\n const obsLogs = ((obsData?.logs || []) as any[]).map((entry: any) => ({\n timestamp: entry.timestamp ?? '',\n log: entry.log,\n }));\n\n this.logger.debug(\n `Observer fetched ${obsLogs.length} log entries for run: ${runName}`,\n );\n\n return {\n logs: obsLogs,\n totalCount: obsData?.total ?? obsLogs.length,\n tookMs: obsData?.tookMs,\n };\n } catch (error: unknown) {\n this.logger.error(\n `Error fetching logs for workflow run ${runName} in namespace ${namespaceName}:`,\n error as Error,\n );\n throw error;\n }\n }\n\n /**\n * Get status (including steps) for a specific workflow run\n */\n async getWorkflowRunStatus(\n namespaceName: string,\n runName: string,\n token?: string,\n ): Promise<WorkflowRunStatusResponse> {\n this.logger.debug(\n `Fetching status for workflow run: ${runName} in namespace: ${namespaceName}`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflowruns/{runName}/status',\n {\n params: {\n path: { namespaceName, runName },\n },\n },\n );\n\n assertApiResponse({ data, error, response }, 'fetch workflow run status');\n\n this.logger.debug(\n `Successfully fetched status for workflow run: ${runName}`,\n );\n\n return data as WorkflowRunStatusResponse;\n } catch (error) {\n this.logger.error(\n `Failed to fetch status for workflow run ${runName} in namespace ${namespaceName}: ${error}`,\n );\n throw error;\n }\n }\n\n /**\n * Get Kubernetes events for a specific workflow run (optionally filtered by task)\n */\n async getWorkflowRunEvents(\n namespaceName: string,\n runName: string,\n task?: string,\n token?: string,\n ): Promise<WorkflowRunEventEntry[]> {\n this.logger.debug(\n `Fetching events for workflow run: ${runName} in namespace: ${namespaceName}${\n task ? `, task: ${task}` : ''\n }`,\n );\n\n try {\n const client = createOpenChoreoApiClient({\n baseUrl: this.baseUrl,\n token,\n logger: this.logger,\n });\n\n const { data, error, response } = await client.GET(\n '/api/v1/namespaces/{namespaceName}/workflowruns/{runName}/events',\n {\n params: {\n path: { namespaceName, runName },\n query: {\n ...(task ? { task } : {}),\n },\n },\n },\n );\n\n assertApiResponse({ data, error, response }, 'fetch workflow run events');\n\n if (!Array.isArray(data)) {\n return [];\n }\n\n const entries: WorkflowRunEventEntry[] = data.map((entry: any) => ({\n timestamp: entry.timestamp,\n type: entry.type,\n reason: entry.reason,\n message: entry.message,\n }));\n\n this.logger.debug(\n `Successfully fetched ${entries.length} events for workflow run: ${runName}`,\n );\n\n return entries;\n } catch (error) {\n this.logger.error(\n `Failed to fetch events for workflow run ${runName} in namespace ${namespaceName}: ${error}`,\n );\n throw error;\n }\n }\n}\n"],"names":["ObservabilityUrlResolver","createOpenChoreoApiClient","assertApiResponse","CHOREO_LABELS","createObservabilityClientWithUrl"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@openchoreo/backstage-plugin-openchoreo-workflows-backend",
3
+ "version": "1.1.0",
4
+ "license": "Apache-2.0",
5
+ "main": "dist/index.cjs.js",
6
+ "types": "dist/index.d.ts",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "main": "dist/index.cjs.js",
10
+ "types": "dist/index.d.ts"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/openchoreo/backstage-plugins.git",
15
+ "directory": "plugins/openchoreo-workflows-backend"
16
+ },
17
+ "backstage": {
18
+ "role": "backend-plugin",
19
+ "pluginId": "openchoreo-workflows-backend",
20
+ "pluginPackages": [
21
+ "@openchoreo/backstage-plugin-openchoreo-workflows-backend"
22
+ ],
23
+ "features": {
24
+ ".": "@backstage/BackendFeature"
25
+ }
26
+ },
27
+ "scripts": {
28
+ "start": "backstage-cli package start",
29
+ "build": "backstage-cli package build",
30
+ "test": "backstage-cli package test",
31
+ "clean": "backstage-cli package clean",
32
+ "prepack": "backstage-cli package prepack",
33
+ "postpack": "backstage-cli package postpack"
34
+ },
35
+ "dependencies": {
36
+ "@backstage/backend-defaults": "^0.12.1",
37
+ "@backstage/backend-plugin-api": "^1.4.3",
38
+ "@backstage/errors": "^1.2.7",
39
+ "@openchoreo/openchoreo-auth": "^1.1.0",
40
+ "@openchoreo/openchoreo-client-node": "^1.1.0",
41
+ "express": "4.21.1",
42
+ "express-promise-router": "4.1.1"
43
+ },
44
+ "devDependencies": {
45
+ "@backstage/backend-test-utils": "^1.9.0",
46
+ "@backstage/cli": "^0.34.3",
47
+ "@types/express": "4.17.21"
48
+ },
49
+ "files": [
50
+ "dist"
51
+ ],
52
+ "typesVersions": {
53
+ "*": {
54
+ "package.json": [
55
+ "package.json"
56
+ ]
57
+ }
58
+ }
59
+ }