@axis-backstage/plugin-jira-dashboard-backend 5.0.0 → 6.0.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 +29 -0
- package/README.md +74 -6
- package/config.d.ts +12 -0
- package/dist/config.cjs.js +15 -2
- package/dist/config.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/service/service.cjs.js +5 -3
- package/dist/service/service.cjs.js.map +1 -1
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @axis-backstage/plugin-jira-dashboard-backend
|
|
2
2
|
|
|
3
|
+
## 6.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 5f6da06: **Added configurable cache TTL (Time-To-Live) for Jira API responses**
|
|
8
|
+
|
|
9
|
+
This release introduces comprehensive cache TTL configuration support for the Jira Dashboard backend plugin:
|
|
10
|
+
|
|
11
|
+
## Configuration Examples
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
# Single instance with custom TTL
|
|
15
|
+
jira:
|
|
16
|
+
baseUrl: 'https://your-jira-instance.com'
|
|
17
|
+
cacheTtl: '30m' # Cache for 30 minutes
|
|
18
|
+
|
|
19
|
+
# Multiple instances with different TTL settings
|
|
20
|
+
jira:
|
|
21
|
+
instances:
|
|
22
|
+
- name: 'production'
|
|
23
|
+
baseUrl: 'https://prod-jira.com'
|
|
24
|
+
cacheTtl: '2h' # Cache for 2 hours
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Breaking Changes
|
|
28
|
+
|
|
29
|
+
- A default cache TTL of 1 hour is now applied to all cached responses
|
|
30
|
+
- This may change caching behavior for deployments relying on indefinite cache duration
|
|
31
|
+
|
|
3
32
|
## 5.0.0
|
|
4
33
|
|
|
5
34
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ jiraDashboard:
|
|
|
39
39
|
- `JIRA_API_URL`: Optional url for the Jira API, if different from the base url, including the API version. Necessary when using scoped API tokens with Jira Cloud. For instance: https://api.atlassian.com/ex/jira/7c9c39d8-d07d-43bd-924b-a82397d47f45/rest/api/2/
|
|
40
40
|
- The headers field can be used to add HTTP headers that will be added to the API requests.
|
|
41
41
|
- `JIRA_EMAIL_SUFFIX`: Optional email suffix used for retrieving a specific Jira user in a company. For instance: @your-company.com. If not provided, the user entity profile email is used instead.
|
|
42
|
-
- `JIRA_ANNOTATION_PREFIX`: Optional annotation prefix for retrieving a custom annotation.
|
|
42
|
+
- `JIRA_ANNOTATION_PREFIX`: Optional annotation prefix for retrieving a custom annotation. Default value is jira.com. If you want to configure the plugin to be compatible with [Roadie's Backstage Jira Plugin](https://roadie.io/backstage/plugins/jira/), use the following annotation prefix:
|
|
43
43
|
|
|
44
44
|
```yaml
|
|
45
45
|
jiraDashboard:
|
|
@@ -77,6 +77,51 @@ jiraDashboard:
|
|
|
77
77
|
|
|
78
78
|
**Note**: When using API v3, make sure your `baseUrl` also points to the v3 endpoint (e.g., `/rest/api/3/` instead of `/rest/api/2/`).
|
|
79
79
|
|
|
80
|
+
### Cache Configuration
|
|
81
|
+
|
|
82
|
+
The plugin includes configurable caching to improve performance by reducing API calls to Jira. You can configure the cache TTL (Time To Live) for each Jira instance:
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
jiraDashboard:
|
|
86
|
+
token: ${JIRA_TOKEN}
|
|
87
|
+
baseUrl: ${JIRA_BASE_URL}
|
|
88
|
+
cacheTtl: 30m # Cache for 30 minutes (defaults to 1h if not specified)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### Supported TTL Formats:
|
|
92
|
+
|
|
93
|
+
The `cacheTtl` option accepts human-readable duration formats as a string:
|
|
94
|
+
|
|
95
|
+
- `30s` - 30 seconds
|
|
96
|
+
- `10m` - 10 minutes
|
|
97
|
+
- `1h` - 1 hour
|
|
98
|
+
- `2h30m` - 2 hours and 30 minutes
|
|
99
|
+
- `1d` - 1 day
|
|
100
|
+
|
|
101
|
+
Or an object like:
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
jiraDashboard:
|
|
105
|
+
cacheTtl:
|
|
106
|
+
hours: 2
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Default Cache Behavior:
|
|
110
|
+
|
|
111
|
+
- **Default TTL**: 1 hour if not specified
|
|
112
|
+
- **Cache Scope**: Project information and JQL query results are cached per Jira instance
|
|
113
|
+
- **Cache Keys**: Unique per project key and JQL query to ensure data accuracy
|
|
114
|
+
|
|
115
|
+
#### Example with custom cache TTL:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
jiraDashboard:
|
|
119
|
+
token: Bearer <your-token>
|
|
120
|
+
baseUrl: https://your-domain.atlassian.net/rest/api/2/
|
|
121
|
+
cacheTtl: 15m # Cache responses for 15 minutes
|
|
122
|
+
userEmailSuffix: @your-company.com
|
|
123
|
+
```
|
|
124
|
+
|
|
80
125
|
### Multiple Jira instances
|
|
81
126
|
|
|
82
127
|
In case multiple Jira instances are being used, the configuration can be written on the form:
|
|
@@ -92,6 +137,7 @@ jiraDashboard:
|
|
|
92
137
|
headers: {} # Optional
|
|
93
138
|
userEmailSuffix: ${JIRA_EMAIL_SUFFIX} # Optional
|
|
94
139
|
useApiV3: false # Optional - defaults to false
|
|
140
|
+
cacheTtl: 1h # Optional - defaults to 1 hour
|
|
95
141
|
- name: separate-jira-instance
|
|
96
142
|
token: ${JIRA_TOKEN_SEPARATE}
|
|
97
143
|
baseUrl: ${JIRA_BASE_URL_SEPARATE}
|
|
@@ -99,11 +145,12 @@ jiraDashboard:
|
|
|
99
145
|
headers: {} # Optional
|
|
100
146
|
userEmailSuffix: ${JIRA_EMAIL_SUFFIX_SEPARATE} # Optional
|
|
101
147
|
useApiV3: true # Optional - enable API v3 for this instance
|
|
148
|
+
cacheTtl: 30m # Optional - cache for 30 minutes for this instance
|
|
102
149
|
```
|
|
103
150
|
|
|
104
|
-
Each instance can have its own `useApiV3`
|
|
151
|
+
Each instance can have its own `useApiV3` and `cacheTtl` settings, allowing you to mix v2 and v3 API usage and customize cache durations across different Jira instances.
|
|
105
152
|
|
|
106
|
-
In entity yamls that don't specify an instance, the one called `"default"` will be used. To specify another
|
|
153
|
+
In entity yamls that don't specify an instance, the one called `"default"` will be used. To specify another instance, prefix the project key with `instance-name/` such as:
|
|
107
154
|
|
|
108
155
|
```yaml
|
|
109
156
|
metadata:
|
|
@@ -135,7 +182,7 @@ jiraDashboard:
|
|
|
135
182
|
|
|
136
183
|
#### Authentication examples and trouble shooting
|
|
137
184
|
|
|
138
|
-
Either "Basic Auth" or "Personal
|
|
185
|
+
Either "Basic Auth" or "Personal Access Tokens" can be used.
|
|
139
186
|
|
|
140
187
|
This plugin will directly take the content of the "jiraDashboard.token" config string and
|
|
141
188
|
use as the "Authorization" header in all Jira REST API calls.
|
|
@@ -161,9 +208,9 @@ jiraDashboard:
|
|
|
161
208
|
userEmailSuffix: ${JIRA_EMAIL_SUFFIX}
|
|
162
209
|
```
|
|
163
210
|
|
|
164
|
-
##### Personal
|
|
211
|
+
##### Personal Access Tokens example
|
|
165
212
|
|
|
166
|
-
See the [Personal
|
|
213
|
+
See the [Personal Access Tokens](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html) documentation how to use and create
|
|
167
214
|
a PAT. The generated token can be directly used, there is no need to encode it. Using
|
|
168
215
|
`curl` with a PAT.
|
|
169
216
|
|
|
@@ -184,6 +231,27 @@ jiraDashboard:
|
|
|
184
231
|
userEmailSuffix: ${JIRA_EMAIL_SUFFIX}
|
|
185
232
|
```
|
|
186
233
|
|
|
234
|
+
Authentication with Jira Cloud may require configuring basic auth like this.
|
|
235
|
+
Note that the email and token must be base64 encoded. This what the configuration
|
|
236
|
+
will look like before the encoding step.
|
|
237
|
+
|
|
238
|
+
```yaml
|
|
239
|
+
jiraDashboard:
|
|
240
|
+
token: Basic email@example.com:pat_token
|
|
241
|
+
baseUrl: https://your-domain.atlassian.net/rest/api/2/
|
|
242
|
+
userEmailSuffix: ${JIRA_EMAIL_SUFFIX}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
And this is the actual Backstage configuration where the "email@example.com:pat_token" string
|
|
246
|
+
has been base64 encoded.
|
|
247
|
+
|
|
248
|
+
```yaml
|
|
249
|
+
jiraDashboard:
|
|
250
|
+
token: Basic ZW1haWxAZXhhbXBsZS5jb206cGF0X3Rva2Vu
|
|
251
|
+
baseUrl: https://your-domain.atlassian.net/rest/api/2/
|
|
252
|
+
userEmailSuffix: ${JIRA_EMAIL_SUFFIX}
|
|
253
|
+
```
|
|
254
|
+
|
|
187
255
|
##### Using Scoped API Tokens with Atlassian Cloud
|
|
188
256
|
|
|
189
257
|
If you want to use scoped API tokens, which allow limiting the permission of the token, you need to call a different Atlassian API endpoint and use a unique `apiUrl` ([see the documentation here](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/#Create-an-API-token-with-scopes)). You will still need to enter the `baseUrl` as well, which should point to your Jira instance (including the API version information).
|
package/config.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { HumanDuration } from '@backstage/types';
|
|
2
|
+
|
|
1
3
|
export interface Config {
|
|
2
4
|
/**
|
|
3
5
|
* Configuration options for the Jira Dashboard plugin
|
|
@@ -53,6 +55,11 @@ export interface Config {
|
|
|
53
55
|
*/
|
|
54
56
|
useApiV3?: boolean;
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Optional cache TTL, defaults to 1 hour.
|
|
60
|
+
*/
|
|
61
|
+
cacheTtl?: HumanDuration | string;
|
|
62
|
+
|
|
56
63
|
// Type helper
|
|
57
64
|
instances?: never;
|
|
58
65
|
}
|
|
@@ -111,6 +118,11 @@ export interface Config {
|
|
|
111
118
|
* Whether to use Jira API v3. Defaults to false for backward compatibility.
|
|
112
119
|
*/
|
|
113
120
|
useApiV3?: boolean;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Optional cache TTL, defaults to 1 hour.
|
|
124
|
+
*/
|
|
125
|
+
cacheTtl?: HumanDuration | string;
|
|
114
126
|
}[];
|
|
115
127
|
};
|
|
116
128
|
}
|
package/dist/config.cjs.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var errors = require('@backstage/errors');
|
|
4
|
+
var config = require('@backstage/config');
|
|
5
|
+
var types = require('@backstage/types');
|
|
4
6
|
|
|
5
7
|
function parseHeaders(config) {
|
|
6
8
|
if (!config) {
|
|
@@ -21,6 +23,15 @@ const JIRA_CONFIG_USER_EMAIL_SUFFIX = "userEmailSuffix";
|
|
|
21
23
|
const JIRA_CONFIG_ANNOTATION = "annotationPrefix";
|
|
22
24
|
const JIRA_FILTERS = "defaultFilters";
|
|
23
25
|
const JIRA_CONFIG_USE_API_V3 = "useApiV3";
|
|
26
|
+
const JIRA_CONFIG_CACHE_TTL = "cacheTtl";
|
|
27
|
+
function getCacheTtl(instanceConfig) {
|
|
28
|
+
if (!instanceConfig.has(JIRA_CONFIG_CACHE_TTL)) {
|
|
29
|
+
return 36e5;
|
|
30
|
+
}
|
|
31
|
+
return types.durationToMilliseconds(
|
|
32
|
+
config.readDurationFromConfig(instanceConfig, { key: JIRA_CONFIG_CACHE_TTL })
|
|
33
|
+
);
|
|
34
|
+
}
|
|
24
35
|
class JiraConfig {
|
|
25
36
|
instances = {};
|
|
26
37
|
/**
|
|
@@ -52,7 +63,8 @@ class JiraConfig {
|
|
|
52
63
|
shortName: filterConfig.getString("shortName"),
|
|
53
64
|
query: filterConfig.getString("query")
|
|
54
65
|
})),
|
|
55
|
-
useApiV3: inst.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false
|
|
66
|
+
useApiV3: inst.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false,
|
|
67
|
+
cacheTtl: getCacheTtl(inst)
|
|
56
68
|
};
|
|
57
69
|
});
|
|
58
70
|
} else {
|
|
@@ -67,7 +79,8 @@ class JiraConfig {
|
|
|
67
79
|
shortName: filterConfig.getString("shortName"),
|
|
68
80
|
query: filterConfig.getString("query")
|
|
69
81
|
})),
|
|
70
|
-
useApiV3: jira.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false
|
|
82
|
+
useApiV3: jira.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false,
|
|
83
|
+
cacheTtl: getCacheTtl(jira)
|
|
71
84
|
};
|
|
72
85
|
}
|
|
73
86
|
}
|
package/dist/config.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs.js","sources":["../src/config.ts"],"sourcesContent":["import { ConflictError, ServiceUnavailableError } from '@backstage/errors';\nimport { RootConfigService } from '@backstage/backend-plugin-api';\nimport { Filter } from '@axis-backstage/plugin-jira-dashboard-common';\n\ntype Config = ReturnType<RootConfigService['getConfig']>;\n\nfunction parseHeaders(config: Config | undefined): Record<string, string> {\n if (!config) {\n return {};\n }\n return Object.fromEntries(\n config.keys().map(key => {\n const value = config.getString(key);\n return [key, value] as const;\n }),\n );\n}\n\n/**\n * The configuration of a Jira instance\n *\n * @public\n */\nexport type ConfigInstance = {\n token: string;\n headers: Record<string, string>;\n baseUrl: string;\n apiUrl?: string;\n userEmailSuffix?: string;\n defaultFilters?: Filter[];\n useApiV3?: boolean;\n};\n\nconst JIRA_CONFIG_BASE_URL = 'baseUrl';\nconst JIRA_CONFIG_API_URL = 'apiUrl';\nconst JIRA_CONFIG_TOKEN = 'token';\nconst JIRA_CONFIG_HEADERS = 'headers';\nconst JIRA_CONFIG_USER_EMAIL_SUFFIX = 'userEmailSuffix';\nconst JIRA_CONFIG_ANNOTATION = 'annotationPrefix';\nconst JIRA_FILTERS = 'defaultFilters';\nconst JIRA_CONFIG_USE_API_V3 = 'useApiV3';\n\n/**\n * Class for reading Jira configuration from the root config\n *\n * @public\n */\nexport class JiraConfig {\n private instances: Record<string, ConfigInstance> = {};\n /**\n * The annotation prefix to use for Jira annotations\n */\n public readonly annotationPrefix: string;\n\n private constructor(config: RootConfigService) {\n const jira = config.getConfig('jiraDashboard');\n\n this.annotationPrefix =\n jira.getOptionalString(JIRA_CONFIG_ANNOTATION) ?? 'jira.com';\n\n const instances = jira.getOptionalConfigArray('instances');\n if (instances) {\n // Multiple instances form\n instances.forEach(inst => {\n const name = inst.getString('name');\n if (Object.getOwnPropertyNames(this.instances).includes(name)) {\n throw new ConflictError(\n `Duplicate jiraDashboard instances: '${name}'`,\n );\n }\n\n this.instances[name] = {\n token: inst.getString(JIRA_CONFIG_TOKEN),\n headers: parseHeaders(inst.getOptionalConfig(JIRA_CONFIG_HEADERS)),\n baseUrl: inst.getString(JIRA_CONFIG_BASE_URL),\n apiUrl: inst.getOptionalString(JIRA_CONFIG_API_URL),\n userEmailSuffix: inst.getOptionalString(\n JIRA_CONFIG_USER_EMAIL_SUFFIX,\n ),\n defaultFilters: inst\n .getOptionalConfigArray(JIRA_FILTERS)\n ?.map(filterConfig => ({\n name: filterConfig.getString('name'),\n shortName: filterConfig.getString('shortName'),\n query: filterConfig.getString('query'),\n })),\n useApiV3: inst.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false,\n };\n });\n } else {\n // Default form\n this.instances.default = {\n token: jira.getString(JIRA_CONFIG_TOKEN),\n headers: parseHeaders(jira.getOptionalConfig(JIRA_CONFIG_HEADERS)),\n baseUrl: jira.getString(JIRA_CONFIG_BASE_URL),\n apiUrl: jira.getOptionalString(JIRA_CONFIG_API_URL),\n userEmailSuffix: jira.getOptionalString(JIRA_CONFIG_USER_EMAIL_SUFFIX),\n defaultFilters: jira\n .getOptionalConfigArray(JIRA_FILTERS)\n ?.map(filterConfig => ({\n name: filterConfig.getString('name'),\n shortName: filterConfig.getString('shortName'),\n query: filterConfig.getString('query'),\n })),\n useApiV3: jira.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false,\n };\n }\n }\n\n /**\n * Create a JiraConfig from the root config\n */\n public static fromConfig(config: RootConfigService): JiraConfig {\n return new JiraConfig(config);\n }\n\n private forInstance(instanceName: string) {\n const instance = this.instances[instanceName];\n if (!instance) {\n throw new ServiceUnavailableError(\n `No such jira instance '${instanceName}'`,\n );\n }\n return instance;\n }\n\n /**\n * Returns the configuration all instances\n */\n getInstances() {\n return Object.getOwnPropertyNames(this.instances);\n }\n\n /**\n * Get the jira config for a specific instance\n */\n getInstance(instanceName?: string): ConfigInstance {\n return this.forInstance(instanceName ?? 'default');\n }\n\n /**\n * Get the jira base url for a given instance\n */\n resolveJiraBaseUrl(instanceName: string): string {\n const instance = this.forInstance(instanceName);\n return instance.baseUrl;\n }\n\n /**\n * Get the auth token for a given instance\n */\n resolveJiraToken(instanceName: string): string {\n const instance = this.forInstance(instanceName);\n return instance.token;\n }\n\n /**\n * Get the email suffice for a given instance\n */\n resolveUserEmailSuffix(instanceName: string): string | undefined {\n const instance = this.forInstance(instanceName);\n return instance.userEmailSuffix;\n }\n\n /**\n * Get the defined default filters for a given instance\n */\n resolveDefaultFilters(instanceName: string): Filter[] | undefined {\n const instance = this.forInstance(instanceName);\n return instance.defaultFilters;\n }\n}\n"],"names":["ConflictError","ServiceUnavailableError"],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.cjs.js","sources":["../src/config.ts"],"sourcesContent":["import { ConflictError, ServiceUnavailableError } from '@backstage/errors';\nimport { RootConfigService } from '@backstage/backend-plugin-api';\nimport { readDurationFromConfig } from '@backstage/config';\nimport { durationToMilliseconds } from '@backstage/types';\nimport { Filter } from '@axis-backstage/plugin-jira-dashboard-common';\n\ntype Config = ReturnType<RootConfigService['getConfig']>;\n\nfunction parseHeaders(config: Config | undefined): Record<string, string> {\n if (!config) {\n return {};\n }\n return Object.fromEntries(\n config.keys().map(key => {\n const value = config.getString(key);\n return [key, value] as const;\n }),\n );\n}\n\n/**\n * The configuration of a Jira instance\n *\n * @public\n */\nexport type ConfigInstance = {\n token: string;\n headers: Record<string, string>;\n baseUrl: string;\n apiUrl?: string;\n userEmailSuffix?: string;\n defaultFilters?: Filter[];\n useApiV3?: boolean;\n cacheTtl: number; // TTL in milliseconds\n};\n\nconst JIRA_CONFIG_BASE_URL = 'baseUrl';\nconst JIRA_CONFIG_API_URL = 'apiUrl';\nconst JIRA_CONFIG_TOKEN = 'token';\nconst JIRA_CONFIG_HEADERS = 'headers';\nconst JIRA_CONFIG_USER_EMAIL_SUFFIX = 'userEmailSuffix';\nconst JIRA_CONFIG_ANNOTATION = 'annotationPrefix';\nconst JIRA_FILTERS = 'defaultFilters';\nconst JIRA_CONFIG_USE_API_V3 = 'useApiV3';\nconst JIRA_CONFIG_CACHE_TTL = 'cacheTtl';\n\n/**\n * Get cache TTL in milliseconds for a Jira instance configuration\n * Defaults to 1 hour (3,600,000 ms) if not specified\n */\nfunction getCacheTtl(instanceConfig: Config): number {\n if (!instanceConfig.has(JIRA_CONFIG_CACHE_TTL)) {\n return 3_600_000;\n }\n\n return durationToMilliseconds(\n readDurationFromConfig(instanceConfig, { key: JIRA_CONFIG_CACHE_TTL }),\n );\n}\n\n/**\n * Class for reading Jira configuration from the root config\n *\n * @public\n */\nexport class JiraConfig {\n private instances: Record<string, ConfigInstance> = {};\n /**\n * The annotation prefix to use for Jira annotations\n */\n public readonly annotationPrefix: string;\n\n private constructor(config: RootConfigService) {\n const jira = config.getConfig('jiraDashboard');\n\n this.annotationPrefix =\n jira.getOptionalString(JIRA_CONFIG_ANNOTATION) ?? 'jira.com';\n\n const instances = jira.getOptionalConfigArray('instances');\n if (instances) {\n // Multiple instances form\n instances.forEach(inst => {\n const name = inst.getString('name');\n if (Object.getOwnPropertyNames(this.instances).includes(name)) {\n throw new ConflictError(\n `Duplicate jiraDashboard instances: '${name}'`,\n );\n }\n\n this.instances[name] = {\n token: inst.getString(JIRA_CONFIG_TOKEN),\n headers: parseHeaders(inst.getOptionalConfig(JIRA_CONFIG_HEADERS)),\n baseUrl: inst.getString(JIRA_CONFIG_BASE_URL),\n apiUrl: inst.getOptionalString(JIRA_CONFIG_API_URL),\n userEmailSuffix: inst.getOptionalString(\n JIRA_CONFIG_USER_EMAIL_SUFFIX,\n ),\n defaultFilters: inst\n .getOptionalConfigArray(JIRA_FILTERS)\n ?.map(filterConfig => ({\n name: filterConfig.getString('name'),\n shortName: filterConfig.getString('shortName'),\n query: filterConfig.getString('query'),\n })),\n useApiV3: inst.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false,\n cacheTtl: getCacheTtl(inst),\n };\n });\n } else {\n // Default form\n this.instances.default = {\n token: jira.getString(JIRA_CONFIG_TOKEN),\n headers: parseHeaders(jira.getOptionalConfig(JIRA_CONFIG_HEADERS)),\n baseUrl: jira.getString(JIRA_CONFIG_BASE_URL),\n apiUrl: jira.getOptionalString(JIRA_CONFIG_API_URL),\n userEmailSuffix: jira.getOptionalString(JIRA_CONFIG_USER_EMAIL_SUFFIX),\n defaultFilters: jira\n .getOptionalConfigArray(JIRA_FILTERS)\n ?.map(filterConfig => ({\n name: filterConfig.getString('name'),\n shortName: filterConfig.getString('shortName'),\n query: filterConfig.getString('query'),\n })),\n useApiV3: jira.getOptionalBoolean(JIRA_CONFIG_USE_API_V3) ?? false,\n cacheTtl: getCacheTtl(jira),\n };\n }\n }\n\n /**\n * Create a JiraConfig from the root config\n */\n public static fromConfig(config: RootConfigService): JiraConfig {\n return new JiraConfig(config);\n }\n\n private forInstance(instanceName: string) {\n const instance = this.instances[instanceName];\n if (!instance) {\n throw new ServiceUnavailableError(\n `No such jira instance '${instanceName}'`,\n );\n }\n return instance;\n }\n\n /**\n * Returns the configuration all instances\n */\n getInstances() {\n return Object.getOwnPropertyNames(this.instances);\n }\n\n /**\n * Get the jira config for a specific instance\n */\n getInstance(instanceName?: string): ConfigInstance {\n return this.forInstance(instanceName ?? 'default');\n }\n\n /**\n * Get the jira base url for a given instance\n */\n resolveJiraBaseUrl(instanceName: string): string {\n const instance = this.forInstance(instanceName);\n return instance.baseUrl;\n }\n\n /**\n * Get the auth token for a given instance\n */\n resolveJiraToken(instanceName: string): string {\n const instance = this.forInstance(instanceName);\n return instance.token;\n }\n\n /**\n * Get the email suffice for a given instance\n */\n resolveUserEmailSuffix(instanceName: string): string | undefined {\n const instance = this.forInstance(instanceName);\n return instance.userEmailSuffix;\n }\n\n /**\n * Get the defined default filters for a given instance\n */\n resolveDefaultFilters(instanceName: string): Filter[] | undefined {\n const instance = this.forInstance(instanceName);\n return instance.defaultFilters;\n }\n}\n"],"names":["durationToMilliseconds","readDurationFromConfig","ConflictError","ServiceUnavailableError"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,9 @@ const getProjectResponse = async (project, cache) => {
|
|
|
11
11
|
}
|
|
12
12
|
try {
|
|
13
13
|
projectResponse = await api.getProjectInfo(project);
|
|
14
|
-
cache.set(project.fullProjectKey, projectResponse
|
|
14
|
+
cache.set(project.fullProjectKey, projectResponse, {
|
|
15
|
+
ttl: project.instance.cacheTtl
|
|
16
|
+
});
|
|
15
17
|
} catch (err) {
|
|
16
18
|
if (err.message !== 200) {
|
|
17
19
|
throw Error(
|
|
@@ -30,7 +32,7 @@ const getJqlResponse = async (jql, config, cache, searchOptions) => {
|
|
|
30
32
|
}
|
|
31
33
|
try {
|
|
32
34
|
issuesResponse = (await api.searchJira(config, jql, searchOptions)).issues;
|
|
33
|
-
cache.set(cacheKey, issuesResponse);
|
|
35
|
+
cache.set(cacheKey, issuesResponse, { ttl: config.cacheTtl });
|
|
34
36
|
} catch (err) {
|
|
35
37
|
if (err.message !== 200) {
|
|
36
38
|
throw Error(
|
|
@@ -87,7 +89,7 @@ async function getJiraProjectsFromKeys(projectKeys, instance, cache) {
|
|
|
87
89
|
projectInfo = await api.getProjectInfo({
|
|
88
90
|
projectKey: key,
|
|
89
91
|
instance});
|
|
90
|
-
cache.set(key, projectInfo);
|
|
92
|
+
cache.set(key, projectInfo, { ttl: instance.cacheTtl });
|
|
91
93
|
}
|
|
92
94
|
jiraProjects.push({
|
|
93
95
|
instance,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.cjs.js","sources":["../../src/service/service.ts"],"sourcesContent":["import { CacheService } from '@backstage/backend-plugin-api';\nimport {\n type Filter,\n Issue,\n type JiraDataResponse,\n type Project,\n} from '@axis-backstage/plugin-jira-dashboard-common';\nimport {\n getFilterById,\n getIssuesByComponent,\n getIssuesByFilter,\n getProjectInfo,\n searchJira,\n SearchOptions,\n} from '../api';\nimport { jqlQueryBuilder } from '../queries';\nimport type { ConfigInstance } from '../config';\nimport { JiraProject } from '../lib';\n\nexport const getProjectResponse = async (\n project: JiraProject,\n cache: CacheService,\n): Promise<Project> => {\n let projectResponse: Project;\n\n projectResponse = (await cache.get(project.fullProjectKey)) as Project;\n\n if (projectResponse) {\n return projectResponse as Project;\n }\n\n try {\n projectResponse = await getProjectInfo(project);\n cache.set(project.fullProjectKey, projectResponse);\n } catch (err: any) {\n if (err.message !== 200) {\n throw Error(\n `Failed to get project info for project key ${project.fullProjectKey} with error: ${err.message}`,\n );\n }\n }\n return projectResponse;\n};\n\nexport const getJqlResponse = async (\n jql: string,\n config: ConfigInstance,\n cache: CacheService,\n searchOptions: SearchOptions,\n): Promise<Issue[]> => {\n let issuesResponse: Issue[];\n\n const cacheKey = `${config.baseUrl} ${jql}`;\n\n issuesResponse = (await cache.get(cacheKey)) as Issue[];\n\n if (issuesResponse) {\n return issuesResponse;\n }\n\n try {\n issuesResponse = (await searchJira(config, jql, searchOptions)).issues;\n cache.set(cacheKey, issuesResponse);\n } catch (err: any) {\n if (err.message !== 200) {\n throw Error(\n `Failed to get issues for JQL ${jql} with error: ${err.message}`,\n );\n }\n }\n return issuesResponse;\n};\n\nexport const getUserIssues = async (\n username: string,\n maxResults: number,\n config: ConfigInstance,\n cache: CacheService,\n filterName: string,\n): Promise<Issue[]> => {\n let jql = `assignee = \"${username}\" AND resolution = Unresolved ORDER BY priority DESC, updated DESC`;\n if (filterName !== 'default') {\n for (const filter of config.defaultFilters || []) {\n if (filterName === filter.name) {\n jql = `assignee = \"${username}\" AND ${filter.query}`;\n }\n }\n }\n\n return getJqlResponse(jql, config, cache, {\n fields: [\n 'key',\n 'issuetype',\n 'summary',\n 'status',\n 'priority',\n 'created',\n 'updated',\n ],\n maxResults,\n });\n};\n\nexport const getFiltersFromAnnotations = async (\n annotations: string[],\n config: ConfigInstance,\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};\nasync function getJiraProjectsFromKeys(\n projectKeys: string[],\n instance: ConfigInstance,\n cache: CacheService,\n): Promise<JiraProject[]> {\n const jiraProjects: JiraProject[] = [];\n for (const key of projectKeys) {\n const cachedProject = (await cache.get(key)) as Project;\n let projectInfo: Project;\n\n if (cachedProject) {\n projectInfo = cachedProject;\n } else {\n projectInfo = await getProjectInfo({\n projectKey: key,\n instance,\n fullProjectKey: '',\n });\n cache.set(key, projectInfo);\n }\n\n jiraProjects.push({\n instance,\n fullProjectKey: projectInfo.key,\n projectKey: projectInfo.key,\n });\n }\n return jiraProjects;\n}\nexport const getIssuesFromFilters = async (\n projectKeys: string[],\n components: string[],\n filters: Filter[],\n instance: ConfigInstance,\n cache: CacheService,\n jqlAnnotation?: string,\n): Promise<JiraDataResponse[]> => {\n const projects = await getJiraProjectsFromKeys(projectKeys, instance, cache);\n return await Promise.all(\n filters.map(async filter => {\n const combinedQuery = `${jqlAnnotation ? `(${jqlAnnotation}) AND ` : ''}${\n filter.query\n }`;\n return {\n name: filter.name,\n query: jqlQueryBuilder({\n project: projectKeys,\n components,\n query: combinedQuery,\n }),\n type: 'filter',\n issues: await getIssuesByFilter(projects, components, combinedQuery),\n };\n }),\n );\n};\n\nexport const getIssuesFromComponents = async (\n projectKeys: string[],\n componentAnnotations: string[],\n instance: ConfigInstance,\n cache: CacheService,\n jqlAnnotation?: string,\n): Promise<JiraDataResponse[]> => {\n const projects = await getJiraProjectsFromKeys(projectKeys, instance, cache);\n return await Promise.all(\n componentAnnotations.map(async componentKey => ({\n name: componentKey,\n query: jqlQueryBuilder({\n project: projectKeys,\n components: [componentKey],\n query: jqlQueryBuilder({\n project: projectKeys,\n components: componentAnnotations,\n query: jqlAnnotation,\n }),\n }),\n type: 'component',\n issues: await getIssuesByComponent(projects, componentKey, jqlAnnotation),\n })),\n );\n};\n"],"names":["getProjectInfo","searchJira","getFilterById","jqlQueryBuilder","getIssuesByFilter","getIssuesByComponent"],"mappings":";;;;;AAmBa,MAAA,kBAAA,GAAqB,OAChC,OAAA,EACA,KACqB,KAAA;AACrB,EAAI,IAAA,eAAA;AAEJ,EAAA,eAAA,GAAmB,MAAM,KAAA,CAAM,GAAI,CAAA,OAAA,CAAQ,cAAc,CAAA;AAEzD,EAAA,IAAI,eAAiB,EAAA;AACnB,IAAO,OAAA,eAAA;AAAA;AAGT,EAAI,IAAA;AACF,IAAkB,eAAA,GAAA,MAAMA,mBAAe,OAAO,CAAA;AAC9C,IAAM,KAAA,CAAA,GAAA,CAAI,OAAQ,CAAA,cAAA,EAAgB,
|
|
1
|
+
{"version":3,"file":"service.cjs.js","sources":["../../src/service/service.ts"],"sourcesContent":["import { CacheService } from '@backstage/backend-plugin-api';\nimport {\n type Filter,\n Issue,\n type JiraDataResponse,\n type Project,\n} from '@axis-backstage/plugin-jira-dashboard-common';\nimport {\n getFilterById,\n getIssuesByComponent,\n getIssuesByFilter,\n getProjectInfo,\n searchJira,\n SearchOptions,\n} from '../api';\nimport { jqlQueryBuilder } from '../queries';\nimport type { ConfigInstance } from '../config';\nimport { JiraProject } from '../lib';\n\nexport const getProjectResponse = async (\n project: JiraProject,\n cache: CacheService,\n): Promise<Project> => {\n let projectResponse: Project;\n\n projectResponse = (await cache.get(project.fullProjectKey)) as Project;\n\n if (projectResponse) {\n return projectResponse as Project;\n }\n\n try {\n projectResponse = await getProjectInfo(project);\n cache.set(project.fullProjectKey, projectResponse, {\n ttl: project.instance.cacheTtl,\n });\n } catch (err: any) {\n if (err.message !== 200) {\n throw Error(\n `Failed to get project info for project key ${project.fullProjectKey} with error: ${err.message}`,\n );\n }\n }\n return projectResponse;\n};\n\nexport const getJqlResponse = async (\n jql: string,\n config: ConfigInstance,\n cache: CacheService,\n searchOptions: SearchOptions,\n): Promise<Issue[]> => {\n let issuesResponse: Issue[];\n\n const cacheKey = `${config.baseUrl} ${jql}`;\n\n issuesResponse = (await cache.get(cacheKey)) as Issue[];\n\n if (issuesResponse) {\n return issuesResponse;\n }\n\n try {\n issuesResponse = (await searchJira(config, jql, searchOptions)).issues;\n cache.set(cacheKey, issuesResponse, { ttl: config.cacheTtl });\n } catch (err: any) {\n if (err.message !== 200) {\n throw Error(\n `Failed to get issues for JQL ${jql} with error: ${err.message}`,\n );\n }\n }\n return issuesResponse;\n};\n\nexport const getUserIssues = async (\n username: string,\n maxResults: number,\n config: ConfigInstance,\n cache: CacheService,\n filterName: string,\n): Promise<Issue[]> => {\n let jql = `assignee = \"${username}\" AND resolution = Unresolved ORDER BY priority DESC, updated DESC`;\n if (filterName !== 'default') {\n for (const filter of config.defaultFilters || []) {\n if (filterName === filter.name) {\n jql = `assignee = \"${username}\" AND ${filter.query}`;\n }\n }\n }\n\n return getJqlResponse(jql, config, cache, {\n fields: [\n 'key',\n 'issuetype',\n 'summary',\n 'status',\n 'priority',\n 'created',\n 'updated',\n ],\n maxResults,\n });\n};\n\nexport const getFiltersFromAnnotations = async (\n annotations: string[],\n config: ConfigInstance,\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};\nasync function getJiraProjectsFromKeys(\n projectKeys: string[],\n instance: ConfigInstance,\n cache: CacheService,\n): Promise<JiraProject[]> {\n const jiraProjects: JiraProject[] = [];\n for (const key of projectKeys) {\n const cachedProject = (await cache.get(key)) as Project;\n let projectInfo: Project;\n\n if (cachedProject) {\n projectInfo = cachedProject;\n } else {\n projectInfo = await getProjectInfo({\n projectKey: key,\n instance,\n fullProjectKey: '',\n });\n cache.set(key, projectInfo, { ttl: instance.cacheTtl });\n }\n\n jiraProjects.push({\n instance,\n fullProjectKey: projectInfo.key,\n projectKey: projectInfo.key,\n });\n }\n return jiraProjects;\n}\nexport const getIssuesFromFilters = async (\n projectKeys: string[],\n components: string[],\n filters: Filter[],\n instance: ConfigInstance,\n cache: CacheService,\n jqlAnnotation?: string,\n): Promise<JiraDataResponse[]> => {\n const projects = await getJiraProjectsFromKeys(projectKeys, instance, cache);\n return await Promise.all(\n filters.map(async filter => {\n const combinedQuery = `${jqlAnnotation ? `(${jqlAnnotation}) AND ` : ''}${\n filter.query\n }`;\n return {\n name: filter.name,\n query: jqlQueryBuilder({\n project: projectKeys,\n components,\n query: combinedQuery,\n }),\n type: 'filter',\n issues: await getIssuesByFilter(projects, components, combinedQuery),\n };\n }),\n );\n};\n\nexport const getIssuesFromComponents = async (\n projectKeys: string[],\n componentAnnotations: string[],\n instance: ConfigInstance,\n cache: CacheService,\n jqlAnnotation?: string,\n): Promise<JiraDataResponse[]> => {\n const projects = await getJiraProjectsFromKeys(projectKeys, instance, cache);\n return await Promise.all(\n componentAnnotations.map(async componentKey => ({\n name: componentKey,\n query: jqlQueryBuilder({\n project: projectKeys,\n components: [componentKey],\n query: jqlQueryBuilder({\n project: projectKeys,\n components: componentAnnotations,\n query: jqlAnnotation,\n }),\n }),\n type: 'component',\n issues: await getIssuesByComponent(projects, componentKey, jqlAnnotation),\n })),\n );\n};\n"],"names":["getProjectInfo","searchJira","getFilterById","jqlQueryBuilder","getIssuesByFilter","getIssuesByComponent"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axis-backstage/plugin-jira-dashboard-backend",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,7 +37,9 @@
|
|
|
37
37
|
"@backstage/backend-plugin-api": "^1.4.2",
|
|
38
38
|
"@backstage/catalog-client": "^1.11.0",
|
|
39
39
|
"@backstage/catalog-model": "^1.7.5",
|
|
40
|
+
"@backstage/config": "^1.3.5",
|
|
40
41
|
"@backstage/errors": "^1.2.7",
|
|
42
|
+
"@backstage/types": "^1.2.2",
|
|
41
43
|
"express": "^4.17.1",
|
|
42
44
|
"express-promise-router": "^4.1.0",
|
|
43
45
|
"node-fetch": "^2.6.7"
|