@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 +5 -0
- package/dist/index.cjs.js +10 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/plugin.cjs.js +44 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/router.cjs.js +177 -0
- package/dist/router.cjs.js.map +1 -0
- package/dist/services/GenericWorkflowService.cjs.js +522 -0
- package/dist/services/GenericWorkflowService.cjs.js.map +1 -0
- package/package.json +59 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|