@axis-backstage/plugin-jira-dashboard-backend 0.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/README.md +95 -0
- package/dist/index.cjs.js +341 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Jira Dashboard Backend
|
|
2
|
+
|
|
3
|
+
A plugin that makes requests to [Atlassian REST API](https://developer.atlassian.com/server/jira/platform/rest-apis/) to get issues and project information from Jira.
|
|
4
|
+
|
|
5
|
+
The frontend plugin that displays this information is [Jira Dashboard](https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/jira-dashboard).
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
The following sections will help you get the Jira Dashboard Backend plugin setup and running.
|
|
10
|
+
|
|
11
|
+
### Installation
|
|
12
|
+
|
|
13
|
+
Install the plugin by following the example below:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# From your Backstage root directory
|
|
17
|
+
yarn add --cwd packages/backend @axis-backstage/plugin-jira-dashboard-backend
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Configuration
|
|
21
|
+
|
|
22
|
+
The Jira Dashboard plugin requires the following YAML to be added to your app-config.yaml:
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
jiraDashboard:
|
|
26
|
+
token: ${JIRA_TOKEN}
|
|
27
|
+
baseUrl: ${JIRA_BASE_URL}'
|
|
28
|
+
userEmailSuffix: ${JIRA_EMAIL_SUFFIX}'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Configuration Details:
|
|
32
|
+
|
|
33
|
+
- `JIRA_TOKEN`: The API token to authenticate towards Jira. It can be found by visiting Atlassians page at https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/. In case you are using a Bearer or Basic token, you need to add it in the beginning of the token. For instance: `Bearer your-secret-token`
|
|
34
|
+
> Note: The JIRA_TOKEN variable from [Roadie's Backstage Jira plugin](https://roadie.io/backstage/plugins/jira) can not be reused here because of the added encoding in this token.
|
|
35
|
+
- `JIRA_BASE_URL`: The base url for Jira in your company, including the API version. For instance: https://jira.se.your-company.com/rest/api/2/'
|
|
36
|
+
- `JIRA_EMAIL_SUFFIX`: The email suffix used for retrieving a specific Jira user in a company. For instance: @your-company.com
|
|
37
|
+
|
|
38
|
+
### Integrating
|
|
39
|
+
|
|
40
|
+
Here's how to get the backend plugin up and running:
|
|
41
|
+
|
|
42
|
+
1. Create a new file named `packages/backend/src/plugins/jiraDashboard.ts`, and add the following to it:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { createRouter } from '@axis-backstage/plugin-jira-dashboard-backend';
|
|
46
|
+
import { Router } from 'express';
|
|
47
|
+
import { PluginEnvironment } from '../types';
|
|
48
|
+
|
|
49
|
+
export default async function createPlugin(
|
|
50
|
+
env: PluginEnvironment,
|
|
51
|
+
): Promise<Router> {
|
|
52
|
+
return await createRouter({
|
|
53
|
+
logger: env.logger,
|
|
54
|
+
config: env.config,
|
|
55
|
+
discovery: env.discovery,
|
|
56
|
+
identity: env.identity,
|
|
57
|
+
tokenManager: env.tokenManager,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
2. Wire this into the overall backend router by adding the following to `packages/backend/src/index.ts`:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import jiraDashboard from './plugins/jiraDashboard';
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
async function main() {
|
|
69
|
+
// Add this line under the other lines that follow the useHotMemoize pattern
|
|
70
|
+
const jiraDashboardEnv = useHotMemoize(module, () => createEnv('jira-dashboard'),
|
|
71
|
+
|
|
72
|
+
// Add this under the lines that add their routers to apiRouter
|
|
73
|
+
apiRouter.use('/jira-dashboard', await jiraDashboard(jiraDashboardEnv));
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. Now run `yarn start-backend` from the repo root.
|
|
78
|
+
|
|
79
|
+
4. In another terminal, run the command: `curl localhost:7007/api/jira-dashboard/health`. The request should return `{"status":"ok"}`.
|
|
80
|
+
|
|
81
|
+
### New Backend System
|
|
82
|
+
|
|
83
|
+
The Jira Dashboard backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/). Here is how you can set it up:
|
|
84
|
+
|
|
85
|
+
In your `packages/backend/src/index.ts` make the following changes:
|
|
86
|
+
|
|
87
|
+
```diff
|
|
88
|
+
+ import { jiraDashboardPlugin } from '@axis-backstage/plugin-jira-dashboard-backend';
|
|
89
|
+
|
|
90
|
+
const backend = createBackend();
|
|
91
|
+
+ backend.add(jiraDashboardPlugin());
|
|
92
|
+
// ... other feature additions
|
|
93
|
+
|
|
94
|
+
backend.start();
|
|
95
|
+
```
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var backendCommon = require('@backstage/backend-common');
|
|
6
|
+
var express = require('express');
|
|
7
|
+
var Router = require('express-promise-router');
|
|
8
|
+
var catalogClient = require('@backstage/catalog-client');
|
|
9
|
+
var pluginJiraDashboardCommon = require('@axis-backstage/plugin-jira-dashboard-common');
|
|
10
|
+
var stream = require('stream');
|
|
11
|
+
var fetch = require('node-fetch');
|
|
12
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
13
|
+
|
|
14
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
15
|
+
|
|
16
|
+
var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
17
|
+
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
18
|
+
var stream__default = /*#__PURE__*/_interopDefaultLegacy(stream);
|
|
19
|
+
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
20
|
+
|
|
21
|
+
const JIRA_BASE_URL_CONFIG_PATH = "jiraDashboard.baseUrl";
|
|
22
|
+
const JIRA_TOKEN_CONFIG_PATH = "jiraDashboard.token";
|
|
23
|
+
const JIRA_USER_CONFIG_EMAIL_SUFFIX = "jiraDashboard.userEmailSuffix";
|
|
24
|
+
function resolveJiraBaseUrl(config) {
|
|
25
|
+
try {
|
|
26
|
+
return config.getString(JIRA_BASE_URL_CONFIG_PATH);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new Error(`Invalid Jira baseUrl, ${error}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function resolveJiraToken(config) {
|
|
32
|
+
try {
|
|
33
|
+
return config.getString(JIRA_TOKEN_CONFIG_PATH);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
throw new Error(`Invalid Jira token, ${error}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function resolveUserEmailSuffix(config) {
|
|
39
|
+
try {
|
|
40
|
+
return config.getString(JIRA_USER_CONFIG_EMAIL_SUFFIX);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(`Invalid Jira user path, ${error}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const getUsernameFromRef = (userRef) => {
|
|
47
|
+
return userRef == null ? void 0 : userRef.split("/").slice(1)[0];
|
|
48
|
+
};
|
|
49
|
+
const openFilter = {
|
|
50
|
+
name: "Open Issues",
|
|
51
|
+
shortName: "OPEN",
|
|
52
|
+
query: "resolution = Unresolved ORDER BY updated DESC"
|
|
53
|
+
};
|
|
54
|
+
const incomingFilter = {
|
|
55
|
+
name: "Incoming Issues",
|
|
56
|
+
shortName: "INCOMING",
|
|
57
|
+
query: "status = New ORDER BY created ASC"
|
|
58
|
+
};
|
|
59
|
+
const getDefaultFilters = (config, userRef) => {
|
|
60
|
+
if (!userRef) {
|
|
61
|
+
return [openFilter, incomingFilter];
|
|
62
|
+
}
|
|
63
|
+
const username = getUsernameFromRef(userRef);
|
|
64
|
+
if (!username) {
|
|
65
|
+
return [openFilter, incomingFilter];
|
|
66
|
+
}
|
|
67
|
+
const assignedToMeFilter = {
|
|
68
|
+
name: "Assigned to me",
|
|
69
|
+
shortName: "ME",
|
|
70
|
+
query: `assignee = "${username}${resolveUserEmailSuffix(
|
|
71
|
+
config
|
|
72
|
+
)}" AND resolution = Unresolved ORDER BY updated DESC`
|
|
73
|
+
};
|
|
74
|
+
return [openFilter, incomingFilter, assignedToMeFilter];
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const getProjectInfo = async (projectKey, config) => {
|
|
78
|
+
const response = await fetch__default["default"](
|
|
79
|
+
`${resolveJiraBaseUrl(config)}project/${projectKey}`,
|
|
80
|
+
{
|
|
81
|
+
method: "GET",
|
|
82
|
+
headers: {
|
|
83
|
+
Authorization: resolveJiraToken(config),
|
|
84
|
+
Accept: "application/json"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
if (response.status !== 200) {
|
|
89
|
+
throw Error(`${response.status}`);
|
|
90
|
+
}
|
|
91
|
+
return response.json();
|
|
92
|
+
};
|
|
93
|
+
const getFilterById = async (id, config) => {
|
|
94
|
+
const response = await fetch__default["default"](`${resolveJiraBaseUrl(config)}filter/${id}`, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
headers: {
|
|
97
|
+
Authorization: resolveJiraToken(config),
|
|
98
|
+
Accept: "application/json"
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
if (response.status !== 200) {
|
|
102
|
+
throw Error(`${response.status}`);
|
|
103
|
+
}
|
|
104
|
+
const jsonResponse = await response.json();
|
|
105
|
+
return { name: jsonResponse.name, query: jsonResponse.jql };
|
|
106
|
+
};
|
|
107
|
+
const getIssuesByFilter = async (projectKey, query, config) => {
|
|
108
|
+
const response = await fetch__default["default"](
|
|
109
|
+
`${resolveJiraBaseUrl(
|
|
110
|
+
config
|
|
111
|
+
)}search?jql=project=${projectKey} AND ${query}`,
|
|
112
|
+
{
|
|
113
|
+
method: "GET",
|
|
114
|
+
headers: {
|
|
115
|
+
Authorization: resolveJiraToken(config),
|
|
116
|
+
Accept: "application/json"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
).then((resp) => resp.json());
|
|
120
|
+
return response.issues;
|
|
121
|
+
};
|
|
122
|
+
const getIssuesByComponent = async (projectKey, componentKey, config) => {
|
|
123
|
+
const response = await fetch__default["default"](
|
|
124
|
+
`${resolveJiraBaseUrl(
|
|
125
|
+
config
|
|
126
|
+
)}search?jql=project=${projectKey} AND component = "${componentKey}"`,
|
|
127
|
+
{
|
|
128
|
+
method: "GET",
|
|
129
|
+
headers: {
|
|
130
|
+
Authorization: resolveJiraToken(config),
|
|
131
|
+
Accept: "application/json"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
).then((resp) => resp.json());
|
|
135
|
+
return response.issues;
|
|
136
|
+
};
|
|
137
|
+
async function getProjectAvatar(url, config) {
|
|
138
|
+
const response = await fetch__default["default"](url, {
|
|
139
|
+
method: "GET",
|
|
140
|
+
headers: {
|
|
141
|
+
Authorization: resolveJiraToken(config)
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return response;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const getProjectResponse = async (projectKey, config, cache) => {
|
|
148
|
+
let projectResponse;
|
|
149
|
+
projectResponse = await cache.get(projectKey);
|
|
150
|
+
if (projectResponse)
|
|
151
|
+
return projectResponse;
|
|
152
|
+
try {
|
|
153
|
+
projectResponse = await getProjectInfo(projectKey, config);
|
|
154
|
+
cache.set(projectKey, projectResponse);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
if (err.message !== 200) {
|
|
157
|
+
throw Error(`${err.status}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return projectResponse;
|
|
161
|
+
};
|
|
162
|
+
const getFiltersFromAnnotations = async (annotations, config) => {
|
|
163
|
+
const filters = [];
|
|
164
|
+
for (const filter of annotations) {
|
|
165
|
+
try {
|
|
166
|
+
const response = await getFilterById(filter, config);
|
|
167
|
+
filters.push(response);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
console.warn(
|
|
170
|
+
`${err.message} : Could not find filter with filter id ${filter}`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return filters;
|
|
175
|
+
};
|
|
176
|
+
const getIssuesFromFilters = async (projectKey, filters, config) => {
|
|
177
|
+
return await Promise.all(
|
|
178
|
+
filters.map(async (filter) => ({
|
|
179
|
+
name: filter.name,
|
|
180
|
+
type: "filter",
|
|
181
|
+
issues: await getIssuesByFilter(projectKey, filter.query, config)
|
|
182
|
+
}))
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
const getIssuesFromComponents = async (projectKey, componentAnnotations, config) => {
|
|
186
|
+
return await Promise.all(
|
|
187
|
+
componentAnnotations.map(async (componentKey) => ({
|
|
188
|
+
name: componentKey,
|
|
189
|
+
type: "component",
|
|
190
|
+
issues: await getIssuesByComponent(projectKey, componentKey, config)
|
|
191
|
+
}))
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const DEFAULT_TTL = 1e3 * 60;
|
|
196
|
+
async function createRouter(options) {
|
|
197
|
+
const { logger, config, discovery, identity, tokenManager } = options;
|
|
198
|
+
const catalogClient$1 = new catalogClient.CatalogClient({ discoveryApi: discovery });
|
|
199
|
+
logger.info("Initializing Jira Dashboard backend");
|
|
200
|
+
const pluginCache = backendCommon.CacheManager.fromConfig(config).forPlugin("jira-dashboard");
|
|
201
|
+
const cache = pluginCache.getClient({ defaultTtl: DEFAULT_TTL });
|
|
202
|
+
const router = Router__default["default"]();
|
|
203
|
+
router.use(express__default["default"].json());
|
|
204
|
+
router.get("/health", (_, response) => {
|
|
205
|
+
response.json({ status: "ok" });
|
|
206
|
+
});
|
|
207
|
+
router.get(
|
|
208
|
+
"/dashboards/by-entity-ref/:entityRef",
|
|
209
|
+
async (request, response) => {
|
|
210
|
+
var _a, _b, _c, _d, _e, _f;
|
|
211
|
+
const entityRef = request.params.entityRef;
|
|
212
|
+
const { token } = await tokenManager.getToken();
|
|
213
|
+
const entity = await catalogClient$1.getEntityByRef(entityRef, { token });
|
|
214
|
+
if (!entity) {
|
|
215
|
+
logger.info(`No entity found for ${entityRef}`);
|
|
216
|
+
response.status(500).json({ error: `No entity found for ${entityRef}` });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const projectKey = (_a = entity.metadata.annotations) == null ? void 0 : _a[pluginJiraDashboardCommon.PROJECT_KEY_ANNOTATION];
|
|
220
|
+
if (!projectKey) {
|
|
221
|
+
const error = `No jira.com/project-key annotation found for ${entityRef}`;
|
|
222
|
+
logger.info(error);
|
|
223
|
+
response.status(404).json(error);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
let projectResponse;
|
|
227
|
+
try {
|
|
228
|
+
projectResponse = await getProjectResponse(projectKey, config, cache);
|
|
229
|
+
} catch (err) {
|
|
230
|
+
logger.error(`Could not find Jira project ${projectKey}`);
|
|
231
|
+
response.status(404).json({
|
|
232
|
+
error: `No Jira project found with key ${projectKey}`
|
|
233
|
+
});
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const userIdentity = await identity.getIdentity({ request });
|
|
237
|
+
if (!userIdentity) {
|
|
238
|
+
logger.warn(`Could not find user identity`);
|
|
239
|
+
}
|
|
240
|
+
let filters = [];
|
|
241
|
+
const customFilterAnnotations = (_c = (_b = entity.metadata.annotations) == null ? void 0 : _b[pluginJiraDashboardCommon.FILTER_ANNOTATION]) == null ? void 0 : _c.split(",");
|
|
242
|
+
filters = getDefaultFilters(
|
|
243
|
+
config,
|
|
244
|
+
(_d = userIdentity == null ? void 0 : userIdentity.identity) == null ? void 0 : _d.userEntityRef
|
|
245
|
+
);
|
|
246
|
+
if (customFilterAnnotations) {
|
|
247
|
+
filters.push(
|
|
248
|
+
...await getFiltersFromAnnotations(customFilterAnnotations, config)
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
let issues = await getIssuesFromFilters(projectKey, filters, config);
|
|
252
|
+
const componentAnnotations = (_f = (_e = entity.metadata.annotations) == null ? void 0 : _e[pluginJiraDashboardCommon.COMPONENT_ANNOTATION]) == null ? void 0 : _f.split(",");
|
|
253
|
+
if (componentAnnotations) {
|
|
254
|
+
const componentIssues = await getIssuesFromComponents(
|
|
255
|
+
projectKey,
|
|
256
|
+
componentAnnotations,
|
|
257
|
+
config
|
|
258
|
+
);
|
|
259
|
+
issues = issues.concat(componentIssues);
|
|
260
|
+
}
|
|
261
|
+
const jiraResponse = {
|
|
262
|
+
project: projectResponse,
|
|
263
|
+
data: issues
|
|
264
|
+
};
|
|
265
|
+
response.json(jiraResponse);
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
router.get("/avatar/by-entity-ref/:entityRef", async (request, response) => {
|
|
269
|
+
var _a;
|
|
270
|
+
const { entityRef } = request.params;
|
|
271
|
+
const { token } = await tokenManager.getToken();
|
|
272
|
+
const entity = await catalogClient$1.getEntityByRef(entityRef, { token });
|
|
273
|
+
if (!entity) {
|
|
274
|
+
logger.info(`No entity found for ${entityRef}`);
|
|
275
|
+
response.status(500).json({ error: `No entity found for ${entityRef}` });
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const projectKey = (_a = entity.metadata.annotations) == null ? void 0 : _a[pluginJiraDashboardCommon.PROJECT_KEY_ANNOTATION];
|
|
279
|
+
const projectResponse = await getProjectResponse(projectKey, config, cache);
|
|
280
|
+
if (!projectResponse) {
|
|
281
|
+
logger.error("Could not find project in Jira");
|
|
282
|
+
response.status(400).json({
|
|
283
|
+
error: `No Jira project found for project key ${projectKey}`
|
|
284
|
+
});
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const url = projectResponse.avatarUrls["48x48"];
|
|
288
|
+
const avatar = await getProjectAvatar(url, config);
|
|
289
|
+
const ps = new stream__default["default"].PassThrough();
|
|
290
|
+
const val = avatar.headers.get("content-type");
|
|
291
|
+
response.setHeader("content-type", val != null ? val : "");
|
|
292
|
+
stream__default["default"].pipeline(avatar.body, ps, (err) => {
|
|
293
|
+
if (err) {
|
|
294
|
+
logger.error(err);
|
|
295
|
+
response.sendStatus(400);
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
});
|
|
299
|
+
ps.pipe(response);
|
|
300
|
+
});
|
|
301
|
+
router.use(backendCommon.errorHandler());
|
|
302
|
+
return router;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const jiraDashboardPlugin = backendPluginApi.createBackendPlugin({
|
|
306
|
+
pluginId: "jira-dashboard",
|
|
307
|
+
register(env) {
|
|
308
|
+
env.registerInit({
|
|
309
|
+
deps: {
|
|
310
|
+
logger: backendPluginApi.coreServices.logger,
|
|
311
|
+
config: backendPluginApi.coreServices.rootConfig,
|
|
312
|
+
discovery: backendPluginApi.coreServices.discovery,
|
|
313
|
+
identity: backendPluginApi.coreServices.identity,
|
|
314
|
+
tokenManager: backendPluginApi.coreServices.tokenManager,
|
|
315
|
+
httpRouter: backendPluginApi.coreServices.httpRouter
|
|
316
|
+
},
|
|
317
|
+
async init({
|
|
318
|
+
logger,
|
|
319
|
+
config,
|
|
320
|
+
discovery,
|
|
321
|
+
identity,
|
|
322
|
+
tokenManager,
|
|
323
|
+
httpRouter
|
|
324
|
+
}) {
|
|
325
|
+
httpRouter.use(
|
|
326
|
+
await createRouter({
|
|
327
|
+
logger: backendCommon.loggerToWinstonLogger(logger),
|
|
328
|
+
config,
|
|
329
|
+
discovery,
|
|
330
|
+
identity,
|
|
331
|
+
tokenManager
|
|
332
|
+
})
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
exports.createRouter = createRouter;
|
|
340
|
+
exports["default"] = jiraDashboardPlugin;
|
|
341
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/config.ts","../src/filters.ts","../src/api.ts","../src/service/service.ts","../src/service/router.ts","../src/plugin.ts"],"sourcesContent":["import { Config } from '@backstage/config';\n\nconst JIRA_BASE_URL_CONFIG_PATH = 'jiraDashboard.baseUrl';\nconst JIRA_TOKEN_CONFIG_PATH = 'jiraDashboard.token';\nconst JIRA_USER_CONFIG_EMAIL_SUFFIX = 'jiraDashboard.userEmailSuffix';\n\nexport function resolveJiraBaseUrl(config: Config): string {\n try {\n return config.getString(JIRA_BASE_URL_CONFIG_PATH);\n } catch (error) {\n throw new Error(`Invalid Jira baseUrl, ${error}`);\n }\n}\n\nexport function resolveJiraToken(config: Config): string {\n try {\n return config.getString(JIRA_TOKEN_CONFIG_PATH);\n } catch (error) {\n throw new Error(`Invalid Jira token, ${error}`);\n }\n}\n\nexport function resolveUserEmailSuffix(config: Config): string {\n try {\n return config.getString(JIRA_USER_CONFIG_EMAIL_SUFFIX);\n } catch (error) {\n throw new Error(`Invalid Jira user path, ${error}`);\n }\n}\n","import { Config } from '@backstage/config';\nimport { resolveUserEmailSuffix } from './config';\nimport { Filter } from '@axis-backstage/plugin-jira-dashboard-common';\n\nconst getUsernameFromRef = (userRef: string) => {\n return userRef?.split('/').slice(1)[0];\n};\n\nconst openFilter: Filter = {\n name: 'Open Issues',\n shortName: 'OPEN',\n query: 'resolution = Unresolved ORDER BY updated DESC',\n};\n\nconst incomingFilter: Filter = {\n name: 'Incoming Issues',\n shortName: 'INCOMING',\n query: 'status = New ORDER BY created ASC',\n};\n\nexport const getDefaultFilters = (\n config: Config,\n userRef?: string,\n): Filter[] => {\n if (!userRef) {\n return [openFilter, incomingFilter];\n }\n const username = getUsernameFromRef(userRef);\n\n if (!username) {\n return [openFilter, incomingFilter];\n }\n\n const assignedToMeFilter: Filter = {\n name: 'Assigned to me',\n shortName: 'ME',\n query: `assignee = \"${username}${resolveUserEmailSuffix(\n config,\n )}\" AND resolution = Unresolved ORDER BY updated DESC`,\n };\n\n return [openFilter, incomingFilter, assignedToMeFilter];\n};\n","import { Config } from '@backstage/config';\nimport fetch from 'node-fetch';\nimport {\n Filter,\n Issue,\n Project,\n} from '@axis-backstage/plugin-jira-dashboard-common';\nimport { resolveJiraBaseUrl, resolveJiraToken } from './config';\n\nexport const getProjectInfo = async (\n projectKey: string,\n config: Config,\n): Promise<Project> => {\n const response = await fetch(\n `${resolveJiraBaseUrl(config)}project/${projectKey}`,\n {\n method: 'GET',\n headers: {\n Authorization: resolveJiraToken(config),\n Accept: 'application/json',\n },\n },\n );\n if (response.status !== 200) {\n throw Error(`${response.status}`);\n }\n return response.json();\n};\n\nexport const getFilterById = async (\n id: string,\n config: Config,\n): Promise<Filter> => {\n const response = await fetch(`${resolveJiraBaseUrl(config)}filter/${id}`, {\n method: 'GET',\n headers: {\n Authorization: resolveJiraToken(config),\n Accept: 'application/json',\n },\n });\n if (response.status !== 200) {\n throw Error(`${response.status}`);\n }\n const jsonResponse = await response.json();\n return { name: jsonResponse.name, query: jsonResponse.jql } as Filter;\n};\n\nexport const getIssuesByFilter = async (\n projectKey: string,\n query: string,\n config: Config,\n): Promise<Issue[]> => {\n const response = await fetch(\n `${resolveJiraBaseUrl(\n config,\n )}search?jql=project=${projectKey} AND ${query}`,\n {\n method: 'GET',\n headers: {\n Authorization: resolveJiraToken(config),\n Accept: 'application/json',\n },\n },\n ).then(resp => resp.json());\n return response.issues;\n};\n\nexport const getIssuesByComponent = async (\n projectKey: string,\n componentKey: string,\n config: Config,\n): Promise<Issue[]> => {\n const response = await fetch(\n `${resolveJiraBaseUrl(\n config,\n )}search?jql=project=${projectKey} AND component = \"${componentKey}\"`,\n {\n method: 'GET',\n headers: {\n Authorization: resolveJiraToken(config),\n Accept: 'application/json',\n },\n },\n ).then(resp => resp.json());\n return response.issues;\n};\n\nexport async function getProjectAvatar(url: string, config: Config) {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n Authorization: resolveJiraToken(config),\n },\n });\n return response;\n}\n","import { CacheClient } from '@backstage/backend-common';\nimport { Config } from '@backstage/config';\nimport {\n type Filter,\n type JiraDataResponse,\n type Project,\n} from '@axis-backstage/plugin-jira-dashboard-common';\nimport {\n getFilterById,\n getIssuesByComponent,\n getIssuesByFilter,\n getProjectInfo,\n} from '../api';\n\nexport const getProjectResponse = async (\n projectKey: string,\n config: Config,\n cache: CacheClient,\n): Promise<Project> => {\n let projectResponse: Project;\n\n projectResponse = (await cache.get(projectKey)) as Project;\n\n if (projectResponse) return projectResponse as Project;\n\n try {\n projectResponse = await getProjectInfo(projectKey, config);\n cache.set(projectKey, projectResponse);\n } catch (err: any) {\n if (err.message !== 200) {\n throw Error(`${err.status}`);\n }\n }\n return projectResponse;\n};\n\nexport const getFiltersFromAnnotations = async (\n annotations: string[],\n config: Config,\n): Promise<Filter[]> => {\n const filters: Filter[] = [];\n\n for (const filter of annotations) {\n try {\n const response = await getFilterById(filter, config);\n filters.push(response);\n } catch (err: any) {\n console.warn(\n `${err.message} : Could not find filter with filter id ${filter}`,\n );\n }\n }\n return filters;\n};\n\nexport const getIssuesFromFilters = async (\n projectKey: string,\n filters: Filter[],\n config: Config,\n): Promise<JiraDataResponse[]> => {\n return await Promise.all(\n filters.map(async filter => ({\n name: filter.name,\n type: 'filter',\n issues: await getIssuesByFilter(projectKey, filter.query, config),\n })),\n );\n};\n\nexport const getIssuesFromComponents = async (\n projectKey: string,\n componentAnnotations: string[],\n config: Config,\n): Promise<JiraDataResponse[]> => {\n return await Promise.all(\n componentAnnotations.map(async componentKey => ({\n name: componentKey,\n type: 'component',\n issues: await getIssuesByComponent(projectKey, componentKey, config),\n })),\n );\n};\n","import {\n CacheManager,\n TokenManager,\n errorHandler,\n} from '@backstage/backend-common';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { Config } from '@backstage/config';\nimport { Logger } from 'winston';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { DiscoveryApi } from '@backstage/plugin-permission-common';\nimport { IdentityApi } from '@backstage/plugin-auth-node';\n\nimport { getDefaultFilters } from '../filters';\nimport {\n COMPONENT_ANNOTATION,\n FILTER_ANNOTATION,\n type Filter,\n type JiraResponse,\n PROJECT_KEY_ANNOTATION,\n type Project,\n} from '@axis-backstage/plugin-jira-dashboard-common';\nimport stream from 'stream';\nimport { getProjectAvatar } from '../api';\nimport {\n getProjectResponse,\n getFiltersFromAnnotations,\n getIssuesFromFilters,\n getIssuesFromComponents,\n} from './service';\n\n/**\n * Constructs a jira dashboard router.\n * @public\n */\nexport interface RouterOptions {\n /**\n * Implementation of Winston logger\n */\n logger: Logger;\n\n /**\n * Backstage config object\n */\n config: Config;\n\n /**\n * Backstage discovery api instance\n */\n discovery: DiscoveryApi;\n\n /**\n * Backstage identity api instance\n */\n identity: IdentityApi;\n\n /**\n * Backstage token manager instance\n */\n tokenManager: TokenManager;\n}\n\nconst DEFAULT_TTL = 1000 * 60;\n\n/**\n * Constructs a jira dashboard router.\n *\n * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, config, discovery, identity, tokenManager } = options;\n const catalogClient = new CatalogClient({ discoveryApi: discovery });\n logger.info('Initializing Jira Dashboard backend');\n\n const pluginCache =\n CacheManager.fromConfig(config).forPlugin('jira-dashboard');\n const cache = pluginCache.getClient({ defaultTtl: DEFAULT_TTL });\n\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n router.get(\n '/dashboards/by-entity-ref/:entityRef',\n async (request, response) => {\n const entityRef = request.params.entityRef;\n const { token } = await tokenManager.getToken();\n const entity = await catalogClient.getEntityByRef(entityRef, { token });\n\n if (!entity) {\n logger.info(`No entity found for ${entityRef}`);\n response\n .status(500)\n .json({ error: `No entity found for ${entityRef}` });\n return;\n }\n\n const projectKey = entity.metadata.annotations?.[PROJECT_KEY_ANNOTATION]!;\n\n if (!projectKey) {\n const error = `No jira.com/project-key annotation found for ${entityRef}`;\n logger.info(error);\n response.status(404).json(error);\n return;\n }\n\n let projectResponse;\n\n try {\n projectResponse = await getProjectResponse(projectKey, config, cache);\n } catch (err) {\n logger.error(`Could not find Jira project ${projectKey}`);\n response.status(404).json({\n error: `No Jira project found with key ${projectKey}`,\n });\n return;\n }\n\n const userIdentity = await identity.getIdentity({ request: request });\n\n if (!userIdentity) {\n logger.warn(`Could not find user identity`);\n }\n\n let filters: Filter[] = [];\n\n const customFilterAnnotations =\n entity.metadata.annotations?.[FILTER_ANNOTATION]?.split(',')!;\n\n filters = getDefaultFilters(\n config,\n userIdentity?.identity?.userEntityRef,\n );\n\n if (customFilterAnnotations) {\n filters.push(\n ...(await getFiltersFromAnnotations(customFilterAnnotations, config)),\n );\n }\n\n let issues = await getIssuesFromFilters(projectKey, filters, config);\n\n const componentAnnotations =\n entity.metadata.annotations?.[COMPONENT_ANNOTATION]?.split(',')!;\n\n if (componentAnnotations) {\n const componentIssues = await getIssuesFromComponents(\n projectKey,\n componentAnnotations,\n config,\n );\n issues = issues.concat(componentIssues);\n }\n\n const jiraResponse: JiraResponse = {\n project: projectResponse as Project,\n data: issues,\n };\n response.json(jiraResponse);\n },\n );\n\n router.get('/avatar/by-entity-ref/:entityRef', async (request, response) => {\n const { entityRef } = request.params;\n const { token } = await tokenManager.getToken();\n const entity = await catalogClient.getEntityByRef(entityRef, { token });\n\n if (!entity) {\n logger.info(`No entity found for ${entityRef}`);\n response.status(500).json({ error: `No entity found for ${entityRef}` });\n return;\n }\n\n const projectKey = entity.metadata.annotations?.[PROJECT_KEY_ANNOTATION]!;\n\n const projectResponse = await getProjectResponse(projectKey, config, cache);\n\n if (!projectResponse) {\n logger.error('Could not find project in Jira');\n response.status(400).json({\n error: `No Jira project found for project key ${projectKey}`,\n });\n return;\n }\n\n const url = projectResponse.avatarUrls['48x48'];\n\n const avatar = await getProjectAvatar(url, config);\n\n const ps = new stream.PassThrough();\n const val = avatar.headers.get('content-type');\n\n response.setHeader('content-type', val ?? '');\n stream.pipeline(avatar.body, ps, err => {\n if (err) {\n logger.error(err);\n response.sendStatus(400);\n }\n return;\n });\n ps.pipe(response);\n });\n router.use(errorHandler());\n return router;\n}\n","import { loggerToWinstonLogger } from '@backstage/backend-common';\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\n\n/**\n * The Jira Dashboard backend plugin.\n *\n * @public\n */\nexport const jiraDashboardPlugin = createBackendPlugin({\n pluginId: 'jira-dashboard',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n discovery: coreServices.discovery,\n identity: coreServices.identity,\n tokenManager: coreServices.tokenManager,\n httpRouter: coreServices.httpRouter,\n },\n async init({\n logger,\n config,\n discovery,\n identity,\n tokenManager,\n httpRouter,\n }) {\n httpRouter.use(\n await createRouter({\n logger: loggerToWinstonLogger(logger),\n config,\n discovery,\n identity,\n tokenManager,\n }),\n );\n },\n });\n },\n});\n"],"names":["fetch","catalogClient","CatalogClient","CacheManager","Router","express","PROJECT_KEY_ANNOTATION","FILTER_ANNOTATION","COMPONENT_ANNOTATION","stream","errorHandler","createBackendPlugin","coreServices","loggerToWinstonLogger"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { TokenManager } from '@backstage/backend-common';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import { Config } from '@backstage/config';
|
|
4
|
+
import { Logger } from 'winston';
|
|
5
|
+
import { DiscoveryApi } from '@backstage/plugin-permission-common';
|
|
6
|
+
import { IdentityApi } from '@backstage/plugin-auth-node';
|
|
7
|
+
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Constructs a jira dashboard router.
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
interface RouterOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Implementation of Winston logger
|
|
16
|
+
*/
|
|
17
|
+
logger: Logger;
|
|
18
|
+
/**
|
|
19
|
+
* Backstage config object
|
|
20
|
+
*/
|
|
21
|
+
config: Config;
|
|
22
|
+
/**
|
|
23
|
+
* Backstage discovery api instance
|
|
24
|
+
*/
|
|
25
|
+
discovery: DiscoveryApi;
|
|
26
|
+
/**
|
|
27
|
+
* Backstage identity api instance
|
|
28
|
+
*/
|
|
29
|
+
identity: IdentityApi;
|
|
30
|
+
/**
|
|
31
|
+
* Backstage token manager instance
|
|
32
|
+
*/
|
|
33
|
+
tokenManager: TokenManager;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Constructs a jira dashboard router.
|
|
37
|
+
*
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
declare function createRouter(options: RouterOptions): Promise<express.Router>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The Jira Dashboard backend plugin.
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
declare const jiraDashboardPlugin: () => _backstage_backend_plugin_api.BackendFeature;
|
|
48
|
+
|
|
49
|
+
export { RouterOptions, createRouter, jiraDashboardPlugin as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axis-backstage/plugin-jira-dashboard-backend",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.cjs.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public",
|
|
9
|
+
"main": "dist/index.cjs.js",
|
|
10
|
+
"types": "dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"backstage": {
|
|
13
|
+
"role": "backend-plugin"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "backstage-cli package start",
|
|
17
|
+
"build": "backstage-cli package build",
|
|
18
|
+
"lint": "backstage-cli package lint",
|
|
19
|
+
"test": "backstage-cli package test",
|
|
20
|
+
"clean": "backstage-cli package clean",
|
|
21
|
+
"prepack": "backstage-cli package prepack",
|
|
22
|
+
"postpack": "backstage-cli package postpack"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@axis-backstage/plugin-jira-dashboard-common": "^0.1.0",
|
|
26
|
+
"@backstage/backend-common": "^0.19.8",
|
|
27
|
+
"@backstage/backend-plugin-api": "^0.6.6",
|
|
28
|
+
"@backstage/catalog-client": "^1.4.5",
|
|
29
|
+
"@backstage/config": "^1.1.1",
|
|
30
|
+
"@backstage/plugin-auth-node": "^0.4.0",
|
|
31
|
+
"@backstage/plugin-permission-common": "^0.7.9",
|
|
32
|
+
"@types/express": "*",
|
|
33
|
+
"express": "^4.17.1",
|
|
34
|
+
"express-promise-router": "^4.1.0",
|
|
35
|
+
"node-fetch": "^2.6.7",
|
|
36
|
+
"winston": "^3.2.1",
|
|
37
|
+
"yn": "^4.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@backstage/cli": "^0.23.0",
|
|
41
|
+
"@types/supertest": "^2.0.12",
|
|
42
|
+
"msw": "^1.0.0",
|
|
43
|
+
"supertest": "^6.2.4"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist"
|
|
47
|
+
]
|
|
48
|
+
}
|