@nu-art/jira-backend 0.400.7

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.
@@ -0,0 +1 @@
1
+ export declare const ModulePack_Backend_Jira: import("../modules/JiraModule.js").ModuleBE_Jira_Class[];
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Permissions management system, define access level for each of
3
+ * your server apis, and restrict users by giving them access levels
4
+ *
5
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ import { JiraModule } from '../modules/JiraModule.js';
20
+ export const ModulePack_Backend_Jira = [
21
+ JiraModule
22
+ ];
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./core/module-pack.js";
2
+ export * from "./modules/JiraModule.js";
3
+ export * from "./modules/utils.js";
package/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Permissions management system, define access level for each of
3
+ * your server apis, and restrict users by giving them access levels
4
+ *
5
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ export * from "./core/module-pack.js";
20
+ export * from "./modules/JiraModule.js";
21
+ export * from "./modules/utils.js";
@@ -0,0 +1,128 @@
1
+ import { Module, TypedMap } from '@nu-art/ts-common';
2
+ import { JiraIssueText } from './utils.js';
3
+ import { JiraVersion } from '@nu-art/jira-shared/version';
4
+ type Config = {
5
+ auth: JiraAuth;
6
+ defaultAssignee: JiraUser;
7
+ baseUrl?: string;
8
+ };
9
+ type JiraAuth = {
10
+ email: string;
11
+ apiKey: string;
12
+ };
13
+ type JiraUser = {
14
+ accountId: string;
15
+ name?: string;
16
+ email?: string;
17
+ };
18
+ type JiraMark = {
19
+ type: string;
20
+ attrs: {
21
+ href: string;
22
+ };
23
+ };
24
+ type JiraContent = {
25
+ type: 'paragraph' | string;
26
+ text?: string;
27
+ marks?: JiraMark[];
28
+ content?: JiraContent[];
29
+ };
30
+ type JiraDescription = string | {
31
+ type: 'doc' | string;
32
+ version: number;
33
+ content: JiraContent[];
34
+ };
35
+ export type JiraIssue_Fields = {
36
+ project: JiraProject;
37
+ issuetype: IssueType;
38
+ description: JiraDescription;
39
+ summary: string;
40
+ reporter?: {
41
+ id: string;
42
+ };
43
+ } & TypedMap<any>;
44
+ export type IssueType = {
45
+ id?: string;
46
+ name: string;
47
+ };
48
+ export type LabelType = {
49
+ label: string[];
50
+ };
51
+ export type JiraProject = {
52
+ id: string;
53
+ name: string;
54
+ key: string;
55
+ };
56
+ export type BaseIssue = {
57
+ id: string;
58
+ key: string;
59
+ self: string;
60
+ url: string;
61
+ };
62
+ export type JiraIssue = BaseIssue & {
63
+ expand: string;
64
+ fields: JiraIssue_Fields;
65
+ };
66
+ export type FixVersionType = {
67
+ fixVersions: {
68
+ name: string;
69
+ }[];
70
+ };
71
+ export type QueryItemWithOperator = {
72
+ value: string;
73
+ operator: string;
74
+ };
75
+ export type JiraQuery = TypedMap<string | string[] | QueryItemWithOperator> & {
76
+ status?: string | string[];
77
+ project?: string | string[];
78
+ fixVersion?: string | string[];
79
+ };
80
+ export type JiraResponse_IssuesQuery = {
81
+ expand: string;
82
+ startAt: number;
83
+ maxResults: number;
84
+ total: number;
85
+ issues: JiraIssue[];
86
+ };
87
+ export type ResponsePostIssue = BaseIssue;
88
+ export declare class ModuleBE_Jira_Class extends Module<Config> {
89
+ private headersJson;
90
+ private headersForm;
91
+ private projects;
92
+ private versions;
93
+ private restUrl;
94
+ protected init(): void;
95
+ private buildHeaders;
96
+ project: {
97
+ query: (projectKey: string) => Promise<JiraProject>;
98
+ };
99
+ version: {
100
+ query: (projectId: string, versionName: string) => Promise<JiraVersion | undefined>;
101
+ create: (projectId: string, versionName: string) => Promise<JiraVersion>;
102
+ };
103
+ comment: {
104
+ add: (issueKey: string, comment: string) => Promise<unknown>;
105
+ };
106
+ issue: {
107
+ query: (query: JiraQuery) => Promise<JiraIssue[]>;
108
+ get: (issueId: string) => Promise<JiraIssue>;
109
+ comment: {
110
+ add: (issueKey: string, comment: string) => Promise<unknown>;
111
+ };
112
+ create: (project: JiraProject, issueType: IssueType, summary: string, descriptions: JiraIssueText[], label: string[]) => Promise<ResponsePostIssue>;
113
+ update: (issueKey: string, fields: Partial<JiraIssue_Fields>) => Promise<unknown>;
114
+ resolve: (issueKey: string, projectKey: string, versionName: string, status: string) => Promise<unknown>;
115
+ };
116
+ getIssueTypes: (id: string) => Promise<unknown>;
117
+ query: (query: JiraQuery) => Promise<JiraIssue[]>;
118
+ getIssueRequest: (issueId: string) => Promise<JiraIssue>;
119
+ addIssueAttachment: (issue: string, file: Buffer) => Promise<unknown>;
120
+ private executeFormRequest;
121
+ private executePostRequest;
122
+ private executePutRequest;
123
+ private handleResponse;
124
+ private executeGetRequest;
125
+ private executeRequest;
126
+ }
127
+ export declare const JiraModule: ModuleBE_Jira_Class;
128
+ export {};
@@ -0,0 +1,191 @@
1
+ /*
2
+ * Permissions management system, define access level for each of
3
+ * your server apis, and restrict users by giving them access levels
4
+ *
5
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ import { ApiException, BadImplementationException, composeUrl, ImplementationMissingException, MimeType_json, Module } from '@nu-art/ts-common';
20
+ import { promisifyRequest } from '@nu-art/thunderstorm-backend';
21
+ import { JiraUtils } from './utils.js';
22
+ import { HeaderKey_ContentType, HttpMethod } from '@nu-art/thunderstorm-shared';
23
+ const createFormData = (filename, buffer) => ({ file: { value: buffer, options: { filename } } });
24
+ export class ModuleBE_Jira_Class extends Module {
25
+ headersJson;
26
+ headersForm;
27
+ projects;
28
+ versions = {};
29
+ restUrl;
30
+ init() {
31
+ if (!this.config.baseUrl)
32
+ throw new ImplementationMissingException('Missing Jira baseUrl for JiraModule, please add the key baseUrl to the config');
33
+ this.restUrl = this.config.baseUrl + '/rest/api/3';
34
+ this.logInfo(`Rest URL: ${this.restUrl}`);
35
+ if (!this.config.auth || !this.config.auth.apiKey || !this.config.auth.email)
36
+ throw new ImplementationMissingException('Missing auth config variables for JiraModule');
37
+ this.headersJson = this.buildHeaders(this.config.auth, true);
38
+ this.headersForm = this.buildHeaders(this.config.auth, false);
39
+ }
40
+ buildHeaders = ({ apiKey, email }, check) => {
41
+ const headers = {
42
+ Authorization: `Basic ${Buffer.from(email + ':' + apiKey).toString('base64')}`
43
+ };
44
+ if (!check) {
45
+ headers['X-Atlassian-Token'] = 'no-check';
46
+ headers[HeaderKey_ContentType] = 'multipart/form-data';
47
+ }
48
+ else {
49
+ headers.Accept = MimeType_json;
50
+ headers[HeaderKey_ContentType] = MimeType_json;
51
+ }
52
+ return headers;
53
+ };
54
+ project = {
55
+ query: async (projectKey) => {
56
+ if (!this.projects)
57
+ this.projects = await this.executeGetRequest(`/project`);
58
+ const project = this.projects.find(_project => _project.key === projectKey);
59
+ if (!project)
60
+ throw new BadImplementationException(`Could not find project: ${projectKey}`);
61
+ return project;
62
+ },
63
+ };
64
+ version = {
65
+ query: async (projectId, versionName) => {
66
+ if (!this.versions[projectId])
67
+ this.versions[projectId] = await this.executeGetRequest(`/project/${projectId}/versions`);
68
+ return this.versions[projectId].find(version => version.name === versionName);
69
+ },
70
+ create: async (projectId, versionName) => {
71
+ const version = await this.executePostRequest(`/version`, {
72
+ projectId,
73
+ name: versionName
74
+ });
75
+ this.versions[projectId].push(version);
76
+ return version;
77
+ }
78
+ };
79
+ comment = {
80
+ add: async (issueKey, comment) => {
81
+ return this.executePostRequest(`/issue/${issueKey}/comment`, JiraUtils.createText(comment));
82
+ }
83
+ };
84
+ issue = {
85
+ query: async (query) => {
86
+ return (await this.executeGetRequest(`/search`, { jql: JiraUtils.buildJQL(query) })).issues;
87
+ },
88
+ get: async (issueId) => {
89
+ return this.executeGetRequest(`/issue/${issueId}`);
90
+ },
91
+ comment: this.comment,
92
+ create: async (project, issueType, summary, descriptions, label) => {
93
+ const issue = await this.executePostRequest('/issue', {
94
+ fields: {
95
+ project,
96
+ issuetype: issueType,
97
+ description: JiraUtils.createText(...descriptions),
98
+ summary,
99
+ labels: label,
100
+ assignee: {
101
+ accountId: this.config.defaultAssignee.accountId
102
+ }
103
+ }
104
+ });
105
+ issue.url = `${this.config.baseUrl}/browse/${issue.key}`;
106
+ return issue;
107
+ },
108
+ update: async (issueKey, fields) => {
109
+ return this.executePutRequest(`/issue/${issueKey}`, { fields });
110
+ },
111
+ resolve: async (issueKey, projectKey, versionName, status) => {
112
+ const project = await JiraModule.project.query(projectKey);
113
+ let version = await JiraModule.version.query(projectKey, versionName);
114
+ if (!version)
115
+ version = await JiraModule.version.create(project.id, versionName);
116
+ return this.executePutRequest(`/issue/${issueKey}`, { fields: { fixVersions: [{ id: version.id }] } });
117
+ },
118
+ };
119
+ getIssueTypes = async (id) => {
120
+ return this.executeGetRequest('/issue/createmetadata', { projectKeys: id });
121
+ };
122
+ query = async (query) => {
123
+ return (await this.executeGetRequest(`/search`, { jql: JiraUtils.buildJQL(query) })).issues;
124
+ };
125
+ getIssueRequest = async (issueId) => {
126
+ return this.executeGetRequest(`/issue/${issueId}`);
127
+ };
128
+ addIssueAttachment = async (issue, file) => {
129
+ return this.executeFormRequest(`/issue/${issue}/attachments`, file);
130
+ };
131
+ executeFormRequest = async (url, buffer) => {
132
+ const request = {
133
+ headers: this.headersForm,
134
+ uri: `${this.restUrl}${url}`,
135
+ formData: createFormData('logs.zip', buffer),
136
+ method: HttpMethod.POST,
137
+ };
138
+ return this.executeRequest(request);
139
+ };
140
+ async executePostRequest(url, body, label) {
141
+ const request = {
142
+ headers: this.headersJson,
143
+ uri: `${this.restUrl}${url}`,
144
+ body,
145
+ method: HttpMethod.POST,
146
+ json: true
147
+ };
148
+ return this.executeRequest(request);
149
+ }
150
+ async executePutRequest(url, body) {
151
+ const request = {
152
+ headers: this.headersJson,
153
+ uri: `${this.restUrl}${url}`,
154
+ body,
155
+ method: HttpMethod.PUT,
156
+ json: true
157
+ };
158
+ return this.executeRequest(request);
159
+ }
160
+ // async executeGetRequestNew<T>(url: string, _params?: { [k: string]: string }): Promise<T> {
161
+ // if (!this.restUrl)
162
+ // throw new ImplementationMissingException('Need a baseUrl');
163
+ //
164
+ // return AxiosHttpModule
165
+ // .createRequest(HttpMethod.GET, generateHex(8))
166
+ // .setOrigin(this.restUrl)
167
+ // .setUrl(url)
168
+ // .setUrlParams(_params)
169
+ // .setHeaders(this.headersJson)
170
+ // .executeSync();
171
+ // }
172
+ handleResponse(response) {
173
+ if (`${response.statusCode}`[0] !== '2')
174
+ throw new ApiException(response.statusCode, response.body);
175
+ return response.toJSON().body;
176
+ }
177
+ async executeGetRequest(url, _params) {
178
+ const request = {
179
+ headers: this.headersJson,
180
+ uri: `${composeUrl(`${this.restUrl}${url}`, _params)}`,
181
+ method: HttpMethod.GET,
182
+ json: true
183
+ };
184
+ return this.executeRequest(request);
185
+ }
186
+ async executeRequest(request) {
187
+ const response = await promisifyRequest(request, false);
188
+ return this.handleResponse(response);
189
+ }
190
+ }
191
+ export const JiraModule = new ModuleBE_Jira_Class();
@@ -0,0 +1,32 @@
1
+ import { JiraQuery } from "./JiraModule.js";
2
+ export type JiraIssueText = string | {
3
+ href: string;
4
+ text: string;
5
+ };
6
+ declare function createText(...texts: JiraIssueText[]): {
7
+ type: string;
8
+ version: number;
9
+ content: {
10
+ type: string;
11
+ content: ({
12
+ type: string;
13
+ text: string;
14
+ marks?: undefined;
15
+ } | {
16
+ type: string;
17
+ text: string;
18
+ marks: {
19
+ type: string;
20
+ attrs: {
21
+ href: string;
22
+ };
23
+ }[];
24
+ })[];
25
+ }[];
26
+ };
27
+ declare function buildJQL(query: JiraQuery): string;
28
+ export declare const JiraUtils: {
29
+ createText: typeof createText;
30
+ buildJQL: typeof buildJQL;
31
+ };
32
+ export {};
@@ -0,0 +1,72 @@
1
+ /*
2
+ * Permissions management system, define access level for each of
3
+ * your server apis, and restrict users by giving them access levels
4
+ *
5
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ import { _keys } from "@nu-art/ts-common";
20
+ function createText(...texts) {
21
+ return {
22
+ type: "doc",
23
+ version: 1,
24
+ content: [
25
+ {
26
+ type: "paragraph",
27
+ content: texts.map(text => {
28
+ if (typeof text === "string")
29
+ return {
30
+ type: "text",
31
+ text
32
+ };
33
+ return {
34
+ type: "text",
35
+ text: text.text,
36
+ marks: [
37
+ {
38
+ type: "link",
39
+ attrs: {
40
+ href: text.href
41
+ }
42
+ }
43
+ ]
44
+ };
45
+ })
46
+ }
47
+ ]
48
+ };
49
+ }
50
+ function buildJQL(query) {
51
+ const params = _keys(query).map((key) => {
52
+ let queryValue;
53
+ let operator = '=';
54
+ if (Array.isArray(query[key])) {
55
+ queryValue = query[key].map(value => `"${value}"`).join(",");
56
+ queryValue = `(${queryValue})`;
57
+ }
58
+ else if (typeof query[key] === 'object') {
59
+ const queryItemWithOperator = query[key];
60
+ queryValue = `"${queryItemWithOperator.value}"`;
61
+ operator = queryItemWithOperator.operator;
62
+ }
63
+ else
64
+ queryValue = `"${query[key]}"`;
65
+ return `${key}${operator}${queryValue}`;
66
+ });
67
+ return params.join(" and ");
68
+ }
69
+ export const JiraUtils = {
70
+ createText,
71
+ buildJQL,
72
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@nu-art/jira-backend",
3
+ "version": "0.400.7",
4
+ "description": "Jira api Module Backend",
5
+ "keywords": [
6
+ "TacB0sS",
7
+ "backend",
8
+ "boilerplate",
9
+ "Jira",
10
+ "typescript"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+ssh://git@github.com:nu-art-js/thunderstorm-boilerplate.git"
15
+ },
16
+ "publishConfig": {
17
+ "directory": "dist",
18
+ "linkDirectory": true
19
+ },
20
+ "license": "Apache-2.0",
21
+ "author": "TacB0sS",
22
+ "dependencies": {
23
+ "@nu-art/jira-shared": "0.400.7",
24
+ "@nu-art/firebase-backend": "0.400.7",
25
+ "@nu-art/firebase-shared": "0.400.7",
26
+ "@nu-art/thunderstorm-backend": "0.400.7",
27
+ "@nu-art/thunderstorm-shared": "0.400.7",
28
+ "@nu-art/ts-common": "0.400.7",
29
+ "body-parser": "^1.18.3",
30
+ "compression": "^1.7.4",
31
+ "express": "^4.18.2",
32
+ "firebase-admin": "13.4.0",
33
+ "moment": "^2.29.4",
34
+ "request": "^2.88.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/debug": "^4.1.2",
38
+ "@types/express": "^4.17.17",
39
+ "@types/js-base64": "^2.3.1",
40
+ "@types/request": "^2.48.3",
41
+ "@types/saml2-js": "^1.6.8"
42
+ },
43
+ "unitConfig": {
44
+ "type": "typescript-lib"
45
+ },
46
+ "type": "module",
47
+ "exports": {
48
+ ".": {
49
+ "types": "./index.d.ts",
50
+ "import": "./index.js"
51
+ },
52
+ "./*": {
53
+ "types": "./*.d.ts",
54
+ "import": "./*.js"
55
+ }
56
+ }
57
+ }