@acorex/connectivity 21.0.0-next.75 → 21.0.0-next.76
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/fesm2022/{acorex-connectivity-mock-acorex-connectivity-mock-Btm13GJK.mjs → acorex-connectivity-mock-acorex-connectivity-mock-BUpepPMb.mjs} +4853 -2395
- package/fesm2022/acorex-connectivity-mock-acorex-connectivity-mock-BUpepPMb.mjs.map +1 -0
- package/fesm2022/{acorex-connectivity-mock-assign-to-manager.activity-CY_5d0KF.mjs → acorex-connectivity-mock-assign-to-manager.activity-ZdB4hvfp.mjs} +2 -2
- package/fesm2022/{acorex-connectivity-mock-assign-to-manager.activity-CY_5d0KF.mjs.map → acorex-connectivity-mock-assign-to-manager.activity-ZdB4hvfp.mjs.map} +1 -1
- package/fesm2022/acorex-connectivity-mock-category-metadata-inheritance.query-CIof4zTg.mjs.map +1 -1
- package/fesm2022/acorex-connectivity-mock-category-with-items.query-DAKUJIIt.mjs.map +1 -1
- package/fesm2022/{acorex-connectivity-mock-chat-generate-image.command-Hj-2bHyJ.mjs → acorex-connectivity-mock-chat-generate-image.command-Dk_PluzJ.mjs} +2 -2
- package/fesm2022/{acorex-connectivity-mock-chat-generate-image.command-Hj-2bHyJ.mjs.map → acorex-connectivity-mock-chat-generate-image.command-Dk_PluzJ.mjs.map} +1 -1
- package/fesm2022/{acorex-connectivity-mock-chat-synthesize-speech.command-0ZnevjmT.mjs → acorex-connectivity-mock-chat-synthesize-speech.command-Cl9Zu1y1.mjs} +2 -2
- package/fesm2022/{acorex-connectivity-mock-chat-synthesize-speech.command-0ZnevjmT.mjs.map → acorex-connectivity-mock-chat-synthesize-speech.command-Cl9Zu1y1.mjs.map} +1 -1
- package/fesm2022/{acorex-connectivity-mock-chat-transcribe-speech.command-D8sCgp69.mjs → acorex-connectivity-mock-chat-transcribe-speech.command-U1XcRyjN.mjs} +2 -2
- package/fesm2022/{acorex-connectivity-mock-chat-transcribe-speech.command-D8sCgp69.mjs.map → acorex-connectivity-mock-chat-transcribe-speech.command-U1XcRyjN.mjs.map} +1 -1
- package/fesm2022/{acorex-connectivity-mock-get-product-list.query-Bv1im5yj.mjs → acorex-connectivity-mock-get-product-list.query-o9TcY_RC.mjs} +2 -2
- package/fesm2022/acorex-connectivity-mock-get-product-list.query-o9TcY_RC.mjs.map +1 -0
- package/fesm2022/{acorex-connectivity-mock-get-product-pdf-data.query-Cp65_mpB.mjs → acorex-connectivity-mock-get-product-pdf-data.query-DUZvwsmY.mjs} +2 -2
- package/fesm2022/{acorex-connectivity-mock-get-product-pdf-data.query-Cp65_mpB.mjs.map → acorex-connectivity-mock-get-product-pdf-data.query-DUZvwsmY.mjs.map} +1 -1
- package/fesm2022/{acorex-connectivity-mock-get-subscriptions-chart-data.query-CkQnH-F_.mjs → acorex-connectivity-mock-get-subscriptions-chart-data.query-BXDzY2Sd.mjs} +2 -2
- package/fesm2022/{acorex-connectivity-mock-get-subscriptions-chart-data.query-CkQnH-F_.mjs.map → acorex-connectivity-mock-get-subscriptions-chart-data.query-BXDzY2Sd.mjs.map} +1 -1
- package/fesm2022/{acorex-connectivity-mock-get-subscriptions-timeline.query-C_ecGCyk.mjs → acorex-connectivity-mock-get-subscriptions-timeline.query-D_jVT-7a.mjs} +2 -2
- package/fesm2022/acorex-connectivity-mock-get-subscriptions-timeline.query-D_jVT-7a.mjs.map +1 -0
- package/fesm2022/{acorex-connectivity-mock-get-tenant-application-access.query-t4BHU8qk.mjs → acorex-connectivity-mock-get-tenant-application-access.query-BG6t0137.mjs} +2 -2
- package/fesm2022/acorex-connectivity-mock-get-tenant-application-access.query-BG6t0137.mjs.map +1 -0
- package/fesm2022/{acorex-connectivity-mock-get-tenant-user-distribution.query-jQAsUf_h.mjs → acorex-connectivity-mock-get-tenant-user-distribution.query-gqSqvIaa.mjs} +2 -2
- package/fesm2022/acorex-connectivity-mock-get-tenant-user-distribution.query-gqSqvIaa.mjs.map +1 -0
- package/fesm2022/acorex-connectivity-mock-jira-instance-sync.command-BKYjz6O5.mjs +466 -0
- package/fesm2022/acorex-connectivity-mock-jira-instance-sync.command-BKYjz6O5.mjs.map +1 -0
- package/fesm2022/{acorex-connectivity-mock-notification-management-email-send.command-DAQfIWoD.mjs → acorex-connectivity-mock-notification-management-email-send.command-Df6E-9-c.mjs} +2 -2
- package/fesm2022/acorex-connectivity-mock-notification-management-email-send.command-Df6E-9-c.mjs.map +1 -0
- package/fesm2022/{acorex-connectivity-mock-user-roles-for-list-column.query-D2QjJXu4.mjs → acorex-connectivity-mock-user-roles-for-list-column.query-ehilblWJ.mjs} +2 -2
- package/fesm2022/{acorex-connectivity-mock-user-roles-for-list-column.query-D2QjJXu4.mjs.map → acorex-connectivity-mock-user-roles-for-list-column.query-ehilblWJ.mjs.map} +1 -1
- package/fesm2022/acorex-connectivity-mock.mjs +1 -1
- package/package.json +2 -2
- package/types/acorex-connectivity-mock.d.ts +342 -88
- package/fesm2022/acorex-connectivity-mock-acorex-connectivity-mock-Btm13GJK.mjs.map +0 -1
- package/fesm2022/acorex-connectivity-mock-contact-core.module-5TLCGWOR.mjs +0 -968
- package/fesm2022/acorex-connectivity-mock-contact-core.module-5TLCGWOR.mjs.map +0 -1
- package/fesm2022/acorex-connectivity-mock-get-product-list.query-Bv1im5yj.mjs.map +0 -1
- package/fesm2022/acorex-connectivity-mock-get-subscriptions-timeline.query-C_ecGCyk.mjs.map +0 -1
- package/fesm2022/acorex-connectivity-mock-get-tenant-application-access.query-t4BHU8qk.mjs.map +0 -1
- package/fesm2022/acorex-connectivity-mock-get-tenant-user-distribution.query-jQAsUf_h.mjs.map +0 -1
- package/fesm2022/acorex-connectivity-mock-notification-management-email-send.command-DAQfIWoD.mjs.map +0 -1
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
|
|
2
|
+
import { AXTranslationService } from '@acorex/core/translation';
|
|
3
|
+
import { RootConfig } from '@acorex/modules/jira-management';
|
|
4
|
+
import * as i0 from '@angular/core';
|
|
5
|
+
import { inject, Injectable } from '@angular/core';
|
|
6
|
+
import { trim, defaultTo, isEmpty, get, isString } from 'lodash-es';
|
|
7
|
+
import { createMultiLanguageString } from '@acorex/platform/contracts';
|
|
8
|
+
import { AXPEntityStorageService } from '@acorex/platform/layout/entity';
|
|
9
|
+
|
|
10
|
+
//#region ---- Imports ----
|
|
11
|
+
class JiraApiError extends Error {
|
|
12
|
+
constructor(message, status, body) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.body = body;
|
|
16
|
+
this.name = 'JiraApiError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region ---- Constants ----
|
|
21
|
+
const JIRA_DEV_PROXY_PREFIX = '/connectivity/mock/jira-proxy';
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region ---- Helpers ----
|
|
24
|
+
function shouldUseDevJiraProxy() {
|
|
25
|
+
if (typeof window === 'undefined') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(window.location.origin);
|
|
29
|
+
}
|
|
30
|
+
function buildApiRoot(baseUrl, apiVersion) {
|
|
31
|
+
const base = trim(baseUrl).replace(/\/+$/, '');
|
|
32
|
+
const directRoot = `${base}/rest/api/${apiVersion}`;
|
|
33
|
+
if (!shouldUseDevJiraProxy()) {
|
|
34
|
+
return directRoot;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const { host } = new URL(base);
|
|
38
|
+
return `${JIRA_DEV_PROXY_PREFIX}/${host}/rest/api/${apiVersion}`;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return directRoot;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region ---- Client ----
|
|
46
|
+
class JiraApiClient {
|
|
47
|
+
constructor(options) {
|
|
48
|
+
const version = defaultTo(trim(defaultTo(options.apiVersion, '')), '2');
|
|
49
|
+
this.apiRoot = buildApiRoot(options.baseUrl, version);
|
|
50
|
+
this.token = trim(options.token);
|
|
51
|
+
}
|
|
52
|
+
async get(path, query) {
|
|
53
|
+
const url = new URL(`${this.apiRoot}${path.startsWith('/') ? path : `/${path}`}`, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
|
|
54
|
+
if (query) {
|
|
55
|
+
for (const [key, value] of Object.entries(query)) {
|
|
56
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
57
|
+
url.searchParams.set(key, String(value));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const response = await fetch(url.toString(), {
|
|
62
|
+
method: 'GET',
|
|
63
|
+
headers: {
|
|
64
|
+
Accept: 'application/json',
|
|
65
|
+
Authorization: `Bearer ${this.token}`,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const bodyText = await response.text();
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new JiraApiError(`Jira API request failed (${response.status} ${response.statusText})`, response.status, bodyText);
|
|
71
|
+
}
|
|
72
|
+
if (isEmpty(bodyText)) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(bodyText);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new JiraApiError('Jira API returned invalid JSON', response.status, bodyText);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function readIssueField(issue, path) {
|
|
84
|
+
return get(issue.fields, path);
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
|
|
88
|
+
//#region ---- Imports ----
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region ---- Service ----
|
|
91
|
+
class AXCJiraInstanceSyncService {
|
|
92
|
+
constructor() {
|
|
93
|
+
//#region ---- Services & Dependencies ----
|
|
94
|
+
this.storage = inject(AXPEntityStorageService);
|
|
95
|
+
this.instanceEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraInstance.name}`;
|
|
96
|
+
this.projectEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraProject.name}`;
|
|
97
|
+
this.issueTypeEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraIssueType.name}`;
|
|
98
|
+
this.fieldEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraField.name}`;
|
|
99
|
+
this.projectIssueTypeEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraProjectIssueType.name}`;
|
|
100
|
+
this.projectIssueTypeFieldEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraProjectIssueTypeField.name}`;
|
|
101
|
+
this.workflowStatusEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraWorkflowStatus.name}`;
|
|
102
|
+
this.issueEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraIssue.name}`;
|
|
103
|
+
this.worklogEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraWorklog.name}`;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region ---- Public API ----
|
|
107
|
+
async sync(input) {
|
|
108
|
+
const instance = (await this.storage.getOne(this.instanceEntity, input.instanceId));
|
|
109
|
+
if (!instance) {
|
|
110
|
+
throw new Error('Jira instance not found.');
|
|
111
|
+
}
|
|
112
|
+
const token = this.resolveToken(instance);
|
|
113
|
+
const baseUrl = defaultTo(trim(defaultTo(instance.baseUrl, '')), trim(defaultTo(instance.siteUrl, '')));
|
|
114
|
+
if (!baseUrl) {
|
|
115
|
+
throw new Error('Configure base URL before syncing.');
|
|
116
|
+
}
|
|
117
|
+
const client = new JiraApiClient({
|
|
118
|
+
baseUrl,
|
|
119
|
+
token,
|
|
120
|
+
apiVersion: instance.apiVersion,
|
|
121
|
+
});
|
|
122
|
+
const [jiraProjects, jiraIssueTypes, jiraFields, existingProjects, existingIssueTypes, existingFields, existingProjectIssueTypes, existingProjectIssueTypeFields, existingWorkflowStatuses, existingIssues, existingWorklogs,] = await Promise.all([
|
|
123
|
+
client.get('/project'),
|
|
124
|
+
client.get('/issuetype'),
|
|
125
|
+
client.get('/field'),
|
|
126
|
+
this.loadAll(this.projectEntity),
|
|
127
|
+
this.loadAll(this.issueTypeEntity),
|
|
128
|
+
this.loadAll(this.fieldEntity),
|
|
129
|
+
this.loadAll(this.projectIssueTypeEntity),
|
|
130
|
+
this.loadAll(this.projectIssueTypeFieldEntity),
|
|
131
|
+
this.loadAll(this.workflowStatusEntity),
|
|
132
|
+
this.loadAll(this.issueEntity),
|
|
133
|
+
this.loadAll(this.worklogEntity),
|
|
134
|
+
]);
|
|
135
|
+
const instanceTitle = instance.title;
|
|
136
|
+
const instanceName = instance.name;
|
|
137
|
+
let projectIssueTypes = 0;
|
|
138
|
+
let projectIssueTypeFields = 0;
|
|
139
|
+
let workflowStatuses = 0;
|
|
140
|
+
let issues = 0;
|
|
141
|
+
let worklogs = 0;
|
|
142
|
+
const issueTypeIdByJiraId = new Map();
|
|
143
|
+
for (const jiraIssueType of jiraIssueTypes) {
|
|
144
|
+
const id = await this.upsertRow(this.issueTypeEntity, existingIssueTypes, (row) => row['jiraInstanceId'] === instance.id && row['jiraIssueTypeId'] === jiraIssueType.id, {
|
|
145
|
+
jiraInstanceId: instance.id,
|
|
146
|
+
jiraInstance: { id: instance.id, title: instanceTitle },
|
|
147
|
+
jiraIssueTypeId: jiraIssueType.id,
|
|
148
|
+
name: jiraIssueType.name,
|
|
149
|
+
description: jiraIssueType.description ? createMultiLanguageString(jiraIssueType.description) : null,
|
|
150
|
+
subtask: jiraIssueType.subtask === true,
|
|
151
|
+
hierarchyLevel: defaultTo(jiraIssueType.hierarchyLevel, 0),
|
|
152
|
+
iconUrl: jiraIssueType.iconUrl ?? null,
|
|
153
|
+
});
|
|
154
|
+
issueTypeIdByJiraId.set(jiraIssueType.id, id);
|
|
155
|
+
}
|
|
156
|
+
const fieldIdByJiraFieldId = new Map();
|
|
157
|
+
for (const jiraField of jiraFields) {
|
|
158
|
+
const fieldId = await this.upsertRow(this.fieldEntity, existingFields, (row) => row['jiraInstanceId'] === instance.id && row['jiraFieldId'] === jiraField.id, {
|
|
159
|
+
jiraInstanceId: instance.id,
|
|
160
|
+
jiraInstance: { id: instance.id, name: instanceName },
|
|
161
|
+
jiraFieldId: jiraField.id,
|
|
162
|
+
name: jiraField.name,
|
|
163
|
+
custom: jiraField.custom === true,
|
|
164
|
+
fieldType: get(jiraField, 'schema.type') ?? null,
|
|
165
|
+
schema: get(jiraField, 'schema.custom') ?? null,
|
|
166
|
+
});
|
|
167
|
+
fieldIdByJiraFieldId.set(jiraField.id, fieldId);
|
|
168
|
+
}
|
|
169
|
+
const projectIdByJiraId = new Map();
|
|
170
|
+
for (const jiraProject of jiraProjects) {
|
|
171
|
+
const projectId = await this.upsertRow(this.projectEntity, existingProjects, (row) => row['jiraInstanceId'] === instance.id && row['jiraProjectId'] === jiraProject.id, {
|
|
172
|
+
jiraInstanceId: instance.id,
|
|
173
|
+
jiraInstance: { id: instance.id, title: instanceTitle, name: instanceName },
|
|
174
|
+
jiraProjectId: jiraProject.id,
|
|
175
|
+
key: jiraProject.key,
|
|
176
|
+
name: jiraProject.name,
|
|
177
|
+
projectTypeKey: jiraProject.projectTypeKey ?? null,
|
|
178
|
+
style: jiraProject.style ?? null,
|
|
179
|
+
simplified: jiraProject.simplified ?? null,
|
|
180
|
+
leadAccountId: jiraProject.lead?.accountId ?? null,
|
|
181
|
+
leadDisplayName: jiraProject.lead?.displayName ?? null,
|
|
182
|
+
avatarUrl: jiraProject.avatarUrls?.['48x48'] ?? null,
|
|
183
|
+
description: jiraProject.description ? createMultiLanguageString(jiraProject.description) : null,
|
|
184
|
+
projectStatusId: 'Active',
|
|
185
|
+
});
|
|
186
|
+
projectIdByJiraId.set(jiraProject.id, projectId);
|
|
187
|
+
const statusesByIssueType = await client.get(`/project/${jiraProject.key}/statuses`);
|
|
188
|
+
const linkedIssueTypeIds = new Set();
|
|
189
|
+
for (const issueTypeBlock of statusesByIssueType) {
|
|
190
|
+
const localIssueTypeId = issueTypeIdByJiraId.get(issueTypeBlock.id);
|
|
191
|
+
if (!localIssueTypeId) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
linkedIssueTypeIds.add(localIssueTypeId);
|
|
195
|
+
await this.upsertRow(this.projectIssueTypeEntity, existingProjectIssueTypes, (row) => row['projectId'] === projectId && row['issueTypeId'] === localIssueTypeId, {
|
|
196
|
+
projectId,
|
|
197
|
+
project: { id: projectId, key: jiraProject.key, name: jiraProject.name },
|
|
198
|
+
issueTypeId: localIssueTypeId,
|
|
199
|
+
issueType: { id: localIssueTypeId, name: issueTypeBlock.name },
|
|
200
|
+
});
|
|
201
|
+
projectIssueTypes += 1;
|
|
202
|
+
let order = 0;
|
|
203
|
+
for (const status of issueTypeBlock.statuses) {
|
|
204
|
+
order += 1;
|
|
205
|
+
await this.upsertRow(this.workflowStatusEntity, existingWorkflowStatuses, (row) => row['projectId'] === projectId &&
|
|
206
|
+
row['issueTypeId'] === localIssueTypeId &&
|
|
207
|
+
row['jiraStatusId'] === status.id, {
|
|
208
|
+
projectId,
|
|
209
|
+
project: { id: projectId, key: jiraProject.key, name: jiraProject.name },
|
|
210
|
+
issueTypeId: localIssueTypeId,
|
|
211
|
+
issueType: { id: localIssueTypeId, name: issueTypeBlock.name },
|
|
212
|
+
jiraStatusId: status.id,
|
|
213
|
+
name: status.name,
|
|
214
|
+
statusCategoryKey: this.mapStatusCategory(status.statusCategory?.key),
|
|
215
|
+
order,
|
|
216
|
+
});
|
|
217
|
+
workflowStatuses += 1;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const createMeta = await client.get('/issue/createmeta', {
|
|
221
|
+
projectKeys: jiraProject.key,
|
|
222
|
+
expand: 'projects.issuetypes.fields',
|
|
223
|
+
});
|
|
224
|
+
for (const projectMeta of createMeta.projects ?? []) {
|
|
225
|
+
for (const issueTypeMeta of projectMeta.issuetypes ?? []) {
|
|
226
|
+
const localIssueTypeId = issueTypeIdByJiraId.get(issueTypeMeta.id);
|
|
227
|
+
if (!localIssueTypeId || !linkedIssueTypeIds.has(localIssueTypeId)) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
let fieldOrder = 0;
|
|
231
|
+
for (const [jiraFieldKey, fieldMeta] of Object.entries(issueTypeMeta.fields ?? {})) {
|
|
232
|
+
const localFieldId = fieldIdByJiraFieldId.get(jiraFieldKey);
|
|
233
|
+
if (!localFieldId) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
fieldOrder += 1;
|
|
237
|
+
await this.upsertRow(this.projectIssueTypeFieldEntity, existingProjectIssueTypeFields, (row) => row['projectId'] === projectId &&
|
|
238
|
+
row['issueTypeId'] === localIssueTypeId &&
|
|
239
|
+
row['fieldId'] === localFieldId, {
|
|
240
|
+
projectId,
|
|
241
|
+
project: { id: projectId, key: jiraProject.key, name: jiraProject.name },
|
|
242
|
+
issueTypeId: localIssueTypeId,
|
|
243
|
+
issueType: { id: localIssueTypeId, name: issueTypeBlockName(issueTypeMeta.id, statusesByIssueType) },
|
|
244
|
+
fieldId: localFieldId,
|
|
245
|
+
field: { id: localFieldId, jiraFieldId: jiraFieldKey, name: jiraFieldKey },
|
|
246
|
+
required: fieldMeta.required === true,
|
|
247
|
+
hasDefaultValue: fieldMeta.hasDefaultValue === true,
|
|
248
|
+
defaultValue: fieldMeta.defaultValue != null ? JSON.stringify(fieldMeta.defaultValue) : null,
|
|
249
|
+
allowedValues: fieldMeta.allowedValues && fieldMeta.allowedValues.length > 0
|
|
250
|
+
? JSON.stringify(fieldMeta.allowedValues)
|
|
251
|
+
: null,
|
|
252
|
+
order: fieldOrder,
|
|
253
|
+
});
|
|
254
|
+
projectIssueTypeFields += 1;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const search = await client.get('/search', {
|
|
259
|
+
jql: `project = ${jiraProject.key} ORDER BY updated DESC`,
|
|
260
|
+
maxResults: 50,
|
|
261
|
+
fields: 'summary,description,issuetype,status,assignee,reporter,priority,parent,resolution,labels,created,updated',
|
|
262
|
+
});
|
|
263
|
+
for (const jiraIssue of search.issues ?? []) {
|
|
264
|
+
const jiraIssueTypeId = readIssueField(jiraIssue, 'issuetype.id')?.id;
|
|
265
|
+
const jiraStatusId = readIssueField(jiraIssue, 'status.id')?.id;
|
|
266
|
+
const localIssueTypeId = jiraIssueTypeId ? issueTypeIdByJiraId.get(jiraIssueTypeId) : undefined;
|
|
267
|
+
const workflowStatus = existingWorkflowStatuses.find((row) => row['projectId'] === projectId &&
|
|
268
|
+
row['issueTypeId'] === localIssueTypeId &&
|
|
269
|
+
row['jiraStatusId'] === jiraStatusId);
|
|
270
|
+
const parentJiraId = readIssueField(jiraIssue, 'parent.id')?.id;
|
|
271
|
+
const parentIssue = parentJiraId
|
|
272
|
+
? existingIssues.find((row) => row['jiraIssueId'] === parentJiraId)
|
|
273
|
+
: undefined;
|
|
274
|
+
const issueId = await this.upsertRow(this.issueEntity, existingIssues, (row) => row['jiraInstanceId'] === instance.id && row['jiraIssueId'] === jiraIssue.id, {
|
|
275
|
+
jiraInstanceId: instance.id,
|
|
276
|
+
jiraInstance: { id: instance.id, name: instanceName },
|
|
277
|
+
projectId,
|
|
278
|
+
project: { id: projectId, key: jiraProject.key, name: jiraProject.name },
|
|
279
|
+
issueTypeId: localIssueTypeId ?? '',
|
|
280
|
+
issueType: localIssueTypeId
|
|
281
|
+
? { id: localIssueTypeId, name: readIssueField(jiraIssue, 'issuetype.name')?.name }
|
|
282
|
+
: undefined,
|
|
283
|
+
workflowStatusId: workflowStatus?.id ?? '',
|
|
284
|
+
workflowStatus: workflowStatus
|
|
285
|
+
? {
|
|
286
|
+
id: workflowStatus.id,
|
|
287
|
+
name: workflowStatus['name'],
|
|
288
|
+
statusCategoryKey: workflowStatus['statusCategoryKey'],
|
|
289
|
+
}
|
|
290
|
+
: undefined,
|
|
291
|
+
jiraIssueId: jiraIssue.id,
|
|
292
|
+
jiraIssueKey: jiraIssue.key,
|
|
293
|
+
summary: defaultTo(readIssueField(jiraIssue, 'summary'), jiraIssue.key),
|
|
294
|
+
description: readIssueField(jiraIssue, 'description') ?? null,
|
|
295
|
+
assigneeAccountId: readIssueField(jiraIssue, 'assignee.accountId')?.accountId ?? null,
|
|
296
|
+
assigneeDisplayName: readIssueField(jiraIssue, 'assignee.displayName')?.displayName ?? null,
|
|
297
|
+
reporterAccountId: readIssueField(jiraIssue, 'reporter.accountId')?.accountId ?? null,
|
|
298
|
+
reporterDisplayName: readIssueField(jiraIssue, 'reporter.displayName')?.displayName ?? null,
|
|
299
|
+
priorityName: readIssueField(jiraIssue, 'priority.name')?.name ?? null,
|
|
300
|
+
parentIssueId: parentIssue?.id ?? null,
|
|
301
|
+
parentIssue: parentIssue
|
|
302
|
+
? { id: parentIssue.id, jiraIssueKey: parentIssue['jiraIssueKey'] }
|
|
303
|
+
: undefined,
|
|
304
|
+
resolutionName: readIssueField(jiraIssue, 'resolution.name')?.name ?? null,
|
|
305
|
+
labels: JSON.stringify(readIssueField(jiraIssue, 'labels') ?? []),
|
|
306
|
+
jiraCreated: readIssueField(jiraIssue, 'created') ?? null,
|
|
307
|
+
jiraUpdated: readIssueField(jiraIssue, 'updated') ?? null,
|
|
308
|
+
});
|
|
309
|
+
issues += 1;
|
|
310
|
+
const worklogList = await client.get(`/issue/${jiraIssue.key}/worklog`);
|
|
311
|
+
for (const worklog of worklogList.worklogs ?? []) {
|
|
312
|
+
await this.upsertRow(this.worklogEntity, existingWorklogs, (row) => row['issueId'] === issueId && row['jiraWorklogId'] === worklog.id, {
|
|
313
|
+
issueId,
|
|
314
|
+
issue: { id: issueId, jiraIssueKey: jiraIssue.key, summary: readIssueField(jiraIssue, 'summary') },
|
|
315
|
+
jiraWorklogId: worklog.id,
|
|
316
|
+
authorAccountId: worklog.author?.accountId ?? null,
|
|
317
|
+
authorDisplayName: worklog.author?.displayName ?? null,
|
|
318
|
+
started: worklog.started,
|
|
319
|
+
timeSpentSeconds: worklog.timeSpentSeconds,
|
|
320
|
+
timeSpentDisplay: worklog.timeSpent ?? null,
|
|
321
|
+
comment: isString(worklog.comment) ? worklog.comment : null,
|
|
322
|
+
jiraCreated: worklog.created ?? null,
|
|
323
|
+
jiraUpdated: worklog.updated ?? null,
|
|
324
|
+
});
|
|
325
|
+
worklogs += 1;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
await this.storage.updateOne(this.instanceEntity, instance.id, {
|
|
330
|
+
lastSyncAt: new Date().toISOString(),
|
|
331
|
+
});
|
|
332
|
+
return {
|
|
333
|
+
instanceId: instance.id,
|
|
334
|
+
projects: jiraProjects.length,
|
|
335
|
+
issueTypes: jiraIssueTypes.length,
|
|
336
|
+
fields: jiraFields.length,
|
|
337
|
+
workflowStatuses,
|
|
338
|
+
projectIssueTypes,
|
|
339
|
+
projectIssueTypeFields,
|
|
340
|
+
issues,
|
|
341
|
+
worklogs,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region ---- Utility Methods ----
|
|
346
|
+
resolveToken(instance) {
|
|
347
|
+
const adminToken = trim(defaultTo(instance.adminToken, ''));
|
|
348
|
+
if (adminToken) {
|
|
349
|
+
return adminToken;
|
|
350
|
+
}
|
|
351
|
+
const pat = trim(defaultTo(instance.personalAccessToken, ''));
|
|
352
|
+
if (pat) {
|
|
353
|
+
return pat;
|
|
354
|
+
}
|
|
355
|
+
throw new Error('Configure an admin token or personal access token before syncing.');
|
|
356
|
+
}
|
|
357
|
+
async loadAll(entityName) {
|
|
358
|
+
const result = await this.storage.query(entityName, { skip: 0, take: 50_000 });
|
|
359
|
+
return (result.items ?? []);
|
|
360
|
+
}
|
|
361
|
+
async upsertRow(entityName, cache, match, payload) {
|
|
362
|
+
const existing = cache.find(match);
|
|
363
|
+
if (existing) {
|
|
364
|
+
await this.storage.updateOne(entityName, existing.id, payload);
|
|
365
|
+
Object.assign(existing, payload);
|
|
366
|
+
return existing.id;
|
|
367
|
+
}
|
|
368
|
+
const id = crypto.randomUUID();
|
|
369
|
+
await this.storage.insertOne(entityName, { id, ...payload });
|
|
370
|
+
const created = { id, ...payload };
|
|
371
|
+
cache.push(created);
|
|
372
|
+
return id;
|
|
373
|
+
}
|
|
374
|
+
mapStatusCategory(key) {
|
|
375
|
+
if (key === 'done') {
|
|
376
|
+
return 'done';
|
|
377
|
+
}
|
|
378
|
+
if (key === 'indeterminate') {
|
|
379
|
+
return 'indeterminate';
|
|
380
|
+
}
|
|
381
|
+
return 'new';
|
|
382
|
+
}
|
|
383
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXCJiraInstanceSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
384
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXCJiraInstanceSyncService, providedIn: 'root' }); }
|
|
385
|
+
}
|
|
386
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXCJiraInstanceSyncService, decorators: [{
|
|
387
|
+
type: Injectable,
|
|
388
|
+
args: [{ providedIn: 'root' }]
|
|
389
|
+
}] });
|
|
390
|
+
function issueTypeBlockName(issueTypeId, blocks) {
|
|
391
|
+
return blocks.find((block) => block.id === issueTypeId)?.name ?? issueTypeId;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
//#region ---- Imports ----
|
|
395
|
+
const I18N = `${RootConfig.config.i18n}:instances.entities.instance`;
|
|
396
|
+
//#endregion
|
|
397
|
+
//#region ---- Command ----
|
|
398
|
+
class AXCJiraInstanceSyncCommand {
|
|
399
|
+
constructor() {
|
|
400
|
+
//#region ---- Services & Dependencies ----
|
|
401
|
+
this.syncService = inject(AXCJiraInstanceSyncService);
|
|
402
|
+
this.loadingDialog = inject(AXLoadingDialogService);
|
|
403
|
+
this.translation = inject(AXTranslationService);
|
|
404
|
+
}
|
|
405
|
+
//#endregion
|
|
406
|
+
//#region ---- Command Execution ----
|
|
407
|
+
async execute(input) {
|
|
408
|
+
const instanceId = defaultTo(get(input, 'instanceId'), '').trim();
|
|
409
|
+
if (!instanceId) {
|
|
410
|
+
return {
|
|
411
|
+
success: false,
|
|
412
|
+
message: {
|
|
413
|
+
text: await this.translation.translateAsync(`@${I18N}.actions.sync.messages.missing-instance`),
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
const [loadingTitle, loadingDescription] = await Promise.all([
|
|
418
|
+
this.translation.translateAsync(`@${I18N}.actions.sync.loading.title`),
|
|
419
|
+
this.translation.translateAsync(`@${I18N}.actions.sync.loading.description`),
|
|
420
|
+
]);
|
|
421
|
+
const progressDialog = this.loadingDialog.show({
|
|
422
|
+
title: loadingTitle,
|
|
423
|
+
mode: 'indeterminate',
|
|
424
|
+
progressColor: 'primary',
|
|
425
|
+
text: loadingDescription,
|
|
426
|
+
});
|
|
427
|
+
try {
|
|
428
|
+
const data = await this.syncService.sync({ instanceId });
|
|
429
|
+
return {
|
|
430
|
+
success: true,
|
|
431
|
+
data,
|
|
432
|
+
message: {
|
|
433
|
+
text: await this.translation.translateAsync(`@${I18N}.actions.sync.messages.success`, {
|
|
434
|
+
params: {
|
|
435
|
+
projects: data.projects,
|
|
436
|
+
issues: data.issues,
|
|
437
|
+
},
|
|
438
|
+
}),
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
const fallback = await this.translation.translateAsync(`@${I18N}.actions.sync.messages.failed`);
|
|
444
|
+
let text = error instanceof Error ? error.message : fallback;
|
|
445
|
+
if (error instanceof JiraApiError && error.status === 0) {
|
|
446
|
+
text = await this.translation.translateAsync(`@${I18N}.actions.sync.messages.network-error`);
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
success: false,
|
|
450
|
+
message: { text },
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
finally {
|
|
454
|
+
progressDialog.close();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXCJiraInstanceSyncCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
458
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXCJiraInstanceSyncCommand, providedIn: 'root' }); }
|
|
459
|
+
}
|
|
460
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXCJiraInstanceSyncCommand, decorators: [{
|
|
461
|
+
type: Injectable,
|
|
462
|
+
args: [{ providedIn: 'root' }]
|
|
463
|
+
}] });
|
|
464
|
+
|
|
465
|
+
export { AXCJiraInstanceSyncCommand };
|
|
466
|
+
//# sourceMappingURL=acorex-connectivity-mock-jira-instance-sync.command-BKYjz6O5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-connectivity-mock-jira-instance-sync.command-BKYjz6O5.mjs","sources":["../../../../libs/connectivity/mock/src/lib/jira-management/features/jira-instance-sync/jira-api.client.ts","../../../../libs/connectivity/mock/src/lib/jira-management/features/jira-instance-sync/jira-instance-sync.service.ts","../../../../libs/connectivity/mock/src/lib/jira-management/features/jira-instance-sync/jira-instance-sync.command.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { defaultTo, get, isEmpty, trim } from 'lodash-es';\n\n//#endregion\n\n//#region ---- Types ----\n\nexport interface JiraApiClientOptions {\n baseUrl: string;\n token: string;\n apiVersion?: string | null;\n}\n\nexport class JiraApiError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n readonly body?: string,\n ) {\n super(message);\n this.name = 'JiraApiError';\n }\n}\n\n//#endregion\n\n//#region ---- Constants ----\n\nconst JIRA_DEV_PROXY_PREFIX = '/connectivity/mock/jira-proxy';\n\n//#endregion\n\n//#region ---- Helpers ----\n\nfunction shouldUseDevJiraProxy(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n return /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/i.test(window.location.origin);\n}\n\nfunction buildApiRoot(baseUrl: string, apiVersion: string): string {\n const base = trim(baseUrl).replace(/\\/+$/, '');\n const directRoot = `${base}/rest/api/${apiVersion}`;\n\n if (!shouldUseDevJiraProxy()) {\n return directRoot;\n }\n\n try {\n const { host } = new URL(base);\n return `${JIRA_DEV_PROXY_PREFIX}/${host}/rest/api/${apiVersion}`;\n } catch {\n return directRoot;\n }\n}\n\n//#endregion\n\n//#region ---- Client ----\n\nexport class JiraApiClient {\n private readonly apiRoot: string;\n private readonly token: string;\n\n constructor(options: JiraApiClientOptions) {\n const version = defaultTo(trim(defaultTo(options.apiVersion, '')), '2');\n this.apiRoot = buildApiRoot(options.baseUrl, version);\n this.token = trim(options.token);\n }\n\n async get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T> {\n const url = new URL(\n `${this.apiRoot}${path.startsWith('/') ? path : `/${path}`}`,\n typeof window !== 'undefined' ? window.location.origin : 'http://localhost',\n );\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined && value !== null && value !== '') {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n Authorization: `Bearer ${this.token}`,\n },\n });\n\n const bodyText = await response.text();\n if (!response.ok) {\n throw new JiraApiError(\n `Jira API request failed (${response.status} ${response.statusText})`,\n response.status,\n bodyText,\n );\n }\n\n if (isEmpty(bodyText)) {\n return {} as T;\n }\n\n try {\n return JSON.parse(bodyText) as T;\n } catch {\n throw new JiraApiError('Jira API returned invalid JSON', response.status, bodyText);\n }\n }\n}\n\n//#endregion\n\n//#region ---- Response Shapes ----\n\nexport type JiraProjectDto = {\n id: string;\n key: string;\n name: string;\n projectTypeKey?: string;\n style?: string;\n simplified?: boolean;\n lead?: { accountId?: string; displayName?: string };\n avatarUrls?: Record<string, string>;\n description?: string;\n};\n\nexport type JiraIssueTypeDto = {\n id: string;\n name: string;\n description?: string;\n subtask?: boolean;\n hierarchyLevel?: number;\n iconUrl?: string;\n};\n\nexport type JiraFieldDto = {\n id: string;\n name: string;\n custom: boolean;\n schema?: { type?: string; custom?: string };\n};\n\nexport type JiraProjectStatusDto = {\n id: string;\n name: string;\n statuses: Array<{\n id: string;\n name: string;\n statusCategory?: { key?: string };\n }>;\n};\n\nexport type JiraSearchResultDto = {\n issues?: Array<{\n id: string;\n key: string;\n fields?: Record<string, unknown>;\n }>;\n};\n\nexport type JiraWorklogListDto = {\n worklogs?: Array<{\n id: string;\n author?: { accountId?: string; displayName?: string };\n started: string;\n timeSpentSeconds: number;\n timeSpent?: string;\n comment?: string;\n created?: string;\n updated?: string;\n }>;\n};\n\nexport type JiraCreateMetaDto = {\n projects?: Array<{\n issuetypes?: Array<{\n id: string;\n fields?: Record<\n string,\n {\n required?: boolean;\n hasDefaultValue?: boolean;\n defaultValue?: unknown;\n allowedValues?: unknown[];\n schema?: { type?: string };\n }\n >;\n }>;\n }>;\n};\n\nexport function readIssueField<T>(issue: { fields?: Record<string, unknown> }, path: string): T | undefined {\n return get(issue.fields, path) as T | undefined;\n}\n\n//#endregion\n","//#region ---- Imports ----\n\nimport {\n RootConfig,\n type AXMJiraManagementJiraInstanceEntityModel,\n} from '@acorex/modules/jira-management';\nimport { createMultiLanguageString } from '@acorex/platform/contracts';\nimport { AXPEntityStorageService } from '@acorex/platform/layout/entity';\nimport { inject, Injectable } from '@angular/core';\nimport { defaultTo, get, isString, trim } from 'lodash-es';\nimport {\n JiraApiClient,\n JiraApiError,\n readIssueField,\n type JiraCreateMetaDto,\n type JiraFieldDto,\n type JiraIssueTypeDto,\n type JiraProjectDto,\n type JiraProjectStatusDto,\n type JiraSearchResultDto,\n type JiraWorklogListDto,\n} from './jira-api.client';\n\n//#endregion\n\n//#region ---- Types ----\n\nexport interface JiraInstanceSyncInput {\n instanceId: string;\n}\n\nexport interface JiraInstanceSyncResult {\n instanceId: string;\n projects: number;\n issueTypes: number;\n fields: number;\n workflowStatuses: number;\n projectIssueTypes: number;\n projectIssueTypeFields: number;\n issues: number;\n worklogs: number;\n}\n\ntype StoredRow = { id: string } & Record<string, unknown>;\n\n//#endregion\n\n//#region ---- Service ----\n\n@Injectable({ providedIn: 'root' })\nexport class AXCJiraInstanceSyncService {\n //#region ---- Services & Dependencies ----\n\n private readonly storage = inject(AXPEntityStorageService);\n\n private readonly instanceEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraInstance.name}`;\n private readonly projectEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraProject.name}`;\n private readonly issueTypeEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraIssueType.name}`;\n private readonly fieldEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraField.name}`;\n private readonly projectIssueTypeEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraProjectIssueType.name}`;\n private readonly projectIssueTypeFieldEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraProjectIssueTypeField.name}`;\n private readonly workflowStatusEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraWorkflowStatus.name}`;\n private readonly issueEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraIssue.name}`;\n private readonly worklogEntity = `${RootConfig.module.name}.${RootConfig.entities.jiraWorklog.name}`;\n\n //#endregion\n\n //#region ---- Public API ----\n\n async sync(input: JiraInstanceSyncInput): Promise<JiraInstanceSyncResult> {\n const instance = (await this.storage.getOne(this.instanceEntity, input.instanceId)) as\n | AXMJiraManagementJiraInstanceEntityModel\n | null;\n\n if (!instance) {\n throw new Error('Jira instance not found.');\n }\n\n const token = this.resolveToken(instance);\n const baseUrl = defaultTo(trim(defaultTo(instance.baseUrl, '')), trim(defaultTo(instance.siteUrl, '')));\n if (!baseUrl) {\n throw new Error('Configure base URL before syncing.');\n }\n\n const client = new JiraApiClient({\n baseUrl,\n token,\n apiVersion: instance.apiVersion,\n });\n\n const [\n jiraProjects,\n jiraIssueTypes,\n jiraFields,\n existingProjects,\n existingIssueTypes,\n existingFields,\n existingProjectIssueTypes,\n existingProjectIssueTypeFields,\n existingWorkflowStatuses,\n existingIssues,\n existingWorklogs,\n ] = await Promise.all([\n client.get<JiraProjectDto[]>('/project'),\n client.get<JiraIssueTypeDto[]>('/issuetype'),\n client.get<JiraFieldDto[]>('/field'),\n this.loadAll(this.projectEntity),\n this.loadAll(this.issueTypeEntity),\n this.loadAll(this.fieldEntity),\n this.loadAll(this.projectIssueTypeEntity),\n this.loadAll(this.projectIssueTypeFieldEntity),\n this.loadAll(this.workflowStatusEntity),\n this.loadAll(this.issueEntity),\n this.loadAll(this.worklogEntity),\n ]);\n\n const instanceTitle = instance.title;\n const instanceName = instance.name;\n let projectIssueTypes = 0;\n let projectIssueTypeFields = 0;\n let workflowStatuses = 0;\n let issues = 0;\n let worklogs = 0;\n\n const issueTypeIdByJiraId = new Map<string, string>();\n\n for (const jiraIssueType of jiraIssueTypes) {\n const id = await this.upsertRow(\n this.issueTypeEntity,\n existingIssueTypes,\n (row) => row['jiraInstanceId'] === instance.id && row['jiraIssueTypeId'] === jiraIssueType.id,\n {\n jiraInstanceId: instance.id,\n jiraInstance: { id: instance.id, title: instanceTitle },\n jiraIssueTypeId: jiraIssueType.id,\n name: jiraIssueType.name,\n description: jiraIssueType.description ? createMultiLanguageString(jiraIssueType.description) : null,\n subtask: jiraIssueType.subtask === true,\n hierarchyLevel: defaultTo(jiraIssueType.hierarchyLevel, 0),\n iconUrl: jiraIssueType.iconUrl ?? null,\n },\n );\n issueTypeIdByJiraId.set(jiraIssueType.id, id);\n }\n\n const fieldIdByJiraFieldId = new Map<string, string>();\n for (const jiraField of jiraFields) {\n const fieldId = await this.upsertRow(\n this.fieldEntity,\n existingFields,\n (row) => row['jiraInstanceId'] === instance.id && row['jiraFieldId'] === jiraField.id,\n {\n jiraInstanceId: instance.id,\n jiraInstance: { id: instance.id, name: instanceName },\n jiraFieldId: jiraField.id,\n name: jiraField.name,\n custom: jiraField.custom === true,\n fieldType: get(jiraField, 'schema.type') ?? null,\n schema: get(jiraField, 'schema.custom') ?? null,\n },\n );\n fieldIdByJiraFieldId.set(jiraField.id, fieldId);\n }\n\n const projectIdByJiraId = new Map<string, string>();\n for (const jiraProject of jiraProjects) {\n const projectId = await this.upsertRow(\n this.projectEntity,\n existingProjects,\n (row) => row['jiraInstanceId'] === instance.id && row['jiraProjectId'] === jiraProject.id,\n {\n jiraInstanceId: instance.id,\n jiraInstance: { id: instance.id, title: instanceTitle, name: instanceName },\n jiraProjectId: jiraProject.id,\n key: jiraProject.key,\n name: jiraProject.name,\n projectTypeKey: jiraProject.projectTypeKey ?? null,\n style: jiraProject.style ?? null,\n simplified: jiraProject.simplified ?? null,\n leadAccountId: jiraProject.lead?.accountId ?? null,\n leadDisplayName: jiraProject.lead?.displayName ?? null,\n avatarUrl: jiraProject.avatarUrls?.['48x48'] ?? null,\n description: jiraProject.description ? createMultiLanguageString(jiraProject.description) : null,\n projectStatusId: 'Active',\n },\n );\n projectIdByJiraId.set(jiraProject.id, projectId);\n\n const statusesByIssueType = await client.get<JiraProjectStatusDto[]>(`/project/${jiraProject.key}/statuses`);\n const linkedIssueTypeIds = new Set<string>();\n\n for (const issueTypeBlock of statusesByIssueType) {\n const localIssueTypeId = issueTypeIdByJiraId.get(issueTypeBlock.id);\n if (!localIssueTypeId) {\n continue;\n }\n\n linkedIssueTypeIds.add(localIssueTypeId);\n await this.upsertRow(\n this.projectIssueTypeEntity,\n existingProjectIssueTypes,\n (row) => row['projectId'] === projectId && row['issueTypeId'] === localIssueTypeId,\n {\n projectId,\n project: { id: projectId, key: jiraProject.key, name: jiraProject.name },\n issueTypeId: localIssueTypeId,\n issueType: { id: localIssueTypeId, name: issueTypeBlock.name },\n },\n );\n projectIssueTypes += 1;\n\n let order = 0;\n for (const status of issueTypeBlock.statuses) {\n order += 1;\n await this.upsertRow(\n this.workflowStatusEntity,\n existingWorkflowStatuses,\n (row) =>\n row['projectId'] === projectId &&\n row['issueTypeId'] === localIssueTypeId &&\n row['jiraStatusId'] === status.id,\n {\n projectId,\n project: { id: projectId, key: jiraProject.key, name: jiraProject.name },\n issueTypeId: localIssueTypeId,\n issueType: { id: localIssueTypeId, name: issueTypeBlock.name },\n jiraStatusId: status.id,\n name: status.name,\n statusCategoryKey: this.mapStatusCategory(status.statusCategory?.key),\n order,\n },\n );\n workflowStatuses += 1;\n }\n }\n\n const createMeta = await client.get<JiraCreateMetaDto>('/issue/createmeta', {\n projectKeys: jiraProject.key,\n expand: 'projects.issuetypes.fields',\n });\n\n for (const projectMeta of createMeta.projects ?? []) {\n for (const issueTypeMeta of projectMeta.issuetypes ?? []) {\n const localIssueTypeId = issueTypeIdByJiraId.get(issueTypeMeta.id);\n if (!localIssueTypeId || !linkedIssueTypeIds.has(localIssueTypeId)) {\n continue;\n }\n\n let fieldOrder = 0;\n for (const [jiraFieldKey, fieldMeta] of Object.entries(issueTypeMeta.fields ?? {})) {\n const localFieldId = fieldIdByJiraFieldId.get(jiraFieldKey);\n if (!localFieldId) {\n continue;\n }\n\n fieldOrder += 1;\n await this.upsertRow(\n this.projectIssueTypeFieldEntity,\n existingProjectIssueTypeFields,\n (row) =>\n row['projectId'] === projectId &&\n row['issueTypeId'] === localIssueTypeId &&\n row['fieldId'] === localFieldId,\n {\n projectId,\n project: { id: projectId, key: jiraProject.key, name: jiraProject.name },\n issueTypeId: localIssueTypeId,\n issueType: { id: localIssueTypeId, name: issueTypeBlockName(issueTypeMeta.id, statusesByIssueType) },\n fieldId: localFieldId,\n field: { id: localFieldId, jiraFieldId: jiraFieldKey, name: jiraFieldKey },\n required: fieldMeta.required === true,\n hasDefaultValue: fieldMeta.hasDefaultValue === true,\n defaultValue: fieldMeta.defaultValue != null ? JSON.stringify(fieldMeta.defaultValue) : null,\n allowedValues:\n fieldMeta.allowedValues && fieldMeta.allowedValues.length > 0\n ? JSON.stringify(fieldMeta.allowedValues)\n : null,\n order: fieldOrder,\n },\n );\n projectIssueTypeFields += 1;\n }\n }\n }\n\n const search = await client.get<JiraSearchResultDto>('/search', {\n jql: `project = ${jiraProject.key} ORDER BY updated DESC`,\n maxResults: 50,\n fields: 'summary,description,issuetype,status,assignee,reporter,priority,parent,resolution,labels,created,updated',\n });\n\n for (const jiraIssue of search.issues ?? []) {\n const jiraIssueTypeId = readIssueField<{ id?: string }>(jiraIssue, 'issuetype.id')?.id;\n const jiraStatusId = readIssueField<{ id?: string }>(jiraIssue, 'status.id')?.id;\n const localIssueTypeId = jiraIssueTypeId ? issueTypeIdByJiraId.get(jiraIssueTypeId) : undefined;\n\n const workflowStatus = existingWorkflowStatuses.find(\n (row) =>\n row['projectId'] === projectId &&\n row['issueTypeId'] === localIssueTypeId &&\n row['jiraStatusId'] === jiraStatusId,\n );\n\n const parentJiraId = readIssueField<{ id?: string }>(jiraIssue, 'parent.id')?.id;\n const parentIssue = parentJiraId\n ? existingIssues.find((row) => row['jiraIssueId'] === parentJiraId)\n : undefined;\n\n const issueId = await this.upsertRow(\n this.issueEntity,\n existingIssues,\n (row) => row['jiraInstanceId'] === instance.id && row['jiraIssueId'] === jiraIssue.id,\n {\n jiraInstanceId: instance.id,\n jiraInstance: { id: instance.id, name: instanceName },\n projectId,\n project: { id: projectId, key: jiraProject.key, name: jiraProject.name },\n issueTypeId: localIssueTypeId ?? '',\n issueType: localIssueTypeId\n ? { id: localIssueTypeId, name: readIssueField<{ name?: string }>(jiraIssue, 'issuetype.name')?.name }\n : undefined,\n workflowStatusId: workflowStatus?.id ?? '',\n workflowStatus: workflowStatus\n ? {\n id: workflowStatus.id,\n name: workflowStatus['name'] as string,\n statusCategoryKey: workflowStatus['statusCategoryKey'] as string,\n }\n : undefined,\n jiraIssueId: jiraIssue.id,\n jiraIssueKey: jiraIssue.key,\n summary: defaultTo(readIssueField<string>(jiraIssue, 'summary'), jiraIssue.key),\n description: readIssueField<string>(jiraIssue, 'description') ?? null,\n assigneeAccountId: readIssueField<{ accountId?: string }>(jiraIssue, 'assignee.accountId')?.accountId ?? null,\n assigneeDisplayName:\n readIssueField<{ displayName?: string }>(jiraIssue, 'assignee.displayName')?.displayName ?? null,\n reporterAccountId: readIssueField<{ accountId?: string }>(jiraIssue, 'reporter.accountId')?.accountId ?? null,\n reporterDisplayName:\n readIssueField<{ displayName?: string }>(jiraIssue, 'reporter.displayName')?.displayName ?? null,\n priorityName: readIssueField<{ name?: string }>(jiraIssue, 'priority.name')?.name ?? null,\n parentIssueId: parentIssue?.id ?? null,\n parentIssue: parentIssue\n ? { id: parentIssue.id, jiraIssueKey: parentIssue['jiraIssueKey'] as string }\n : undefined,\n resolutionName: readIssueField<{ name?: string }>(jiraIssue, 'resolution.name')?.name ?? null,\n labels: JSON.stringify(readIssueField<string[]>(jiraIssue, 'labels') ?? []),\n jiraCreated: readIssueField<string>(jiraIssue, 'created') ?? null,\n jiraUpdated: readIssueField<string>(jiraIssue, 'updated') ?? null,\n },\n );\n issues += 1;\n\n const worklogList = await client.get<JiraWorklogListDto>(`/issue/${jiraIssue.key}/worklog`);\n for (const worklog of worklogList.worklogs ?? []) {\n await this.upsertRow(\n this.worklogEntity,\n existingWorklogs,\n (row) => row['issueId'] === issueId && row['jiraWorklogId'] === worklog.id,\n {\n issueId,\n issue: { id: issueId, jiraIssueKey: jiraIssue.key, summary: readIssueField<string>(jiraIssue, 'summary') },\n jiraWorklogId: worklog.id,\n authorAccountId: worklog.author?.accountId ?? null,\n authorDisplayName: worklog.author?.displayName ?? null,\n started: worklog.started,\n timeSpentSeconds: worklog.timeSpentSeconds,\n timeSpentDisplay: worklog.timeSpent ?? null,\n comment: isString(worklog.comment) ? worklog.comment : null,\n jiraCreated: worklog.created ?? null,\n jiraUpdated: worklog.updated ?? null,\n },\n );\n worklogs += 1;\n }\n }\n }\n\n await this.storage.updateOne(this.instanceEntity, instance.id, {\n lastSyncAt: new Date().toISOString(),\n });\n\n return {\n instanceId: instance.id,\n projects: jiraProjects.length,\n issueTypes: jiraIssueTypes.length,\n fields: jiraFields.length,\n workflowStatuses,\n projectIssueTypes,\n projectIssueTypeFields,\n issues,\n worklogs,\n };\n }\n\n //#endregion\n\n //#region ---- Utility Methods ----\n\n private resolveToken(instance: AXMJiraManagementJiraInstanceEntityModel): string {\n const adminToken = trim(defaultTo(instance.adminToken, ''));\n if (adminToken) {\n return adminToken;\n }\n\n const pat = trim(defaultTo(instance.personalAccessToken, ''));\n if (pat) {\n return pat;\n }\n\n throw new Error('Configure an admin token or personal access token before syncing.');\n }\n\n private async loadAll(entityName: string): Promise<StoredRow[]> {\n const result = await this.storage.query(entityName, { skip: 0, take: 50_000 });\n return (result.items ?? []) as StoredRow[];\n }\n\n private async upsertRow(\n entityName: string,\n cache: StoredRow[],\n match: (row: StoredRow) => boolean,\n payload: Record<string, unknown>,\n ): Promise<string> {\n const existing = cache.find(match);\n if (existing) {\n await this.storage.updateOne(entityName, existing.id, payload);\n Object.assign(existing, payload);\n return existing.id;\n }\n\n const id = crypto.randomUUID();\n await this.storage.insertOne(entityName, { id, ...payload });\n const created = { id, ...payload };\n cache.push(created);\n return id;\n }\n\n private mapStatusCategory(key?: string): 'new' | 'indeterminate' | 'done' {\n if (key === 'done') {\n return 'done';\n }\n if (key === 'indeterminate') {\n return 'indeterminate';\n }\n return 'new';\n }\n\n //#endregion\n}\n\nfunction issueTypeBlockName(issueTypeId: string, blocks: JiraProjectStatusDto[]): string {\n return blocks.find((block) => block.id === issueTypeId)?.name ?? issueTypeId;\n}\n\nexport { JiraApiError };\n\n//#endregion\n","//#region ---- Imports ----\n\nimport { AXLoadingDialogService } from '@acorex/components/loading-dialog';\nimport { AXTranslationService } from '@acorex/core/translation';\nimport { RootConfig } from '@acorex/modules/jira-management';\nimport { AXPExecuteCommandResult } from '@acorex/platform/core';\nimport { AXPCommand } from '@acorex/platform/runtime';\nimport { inject, Injectable } from '@angular/core';\nimport { defaultTo, get } from 'lodash-es';\nimport { AXCJiraInstanceSyncService, JiraApiError, type JiraInstanceSyncResult } from './jira-instance-sync.service';\n\n//#endregion\n\n//#region ---- Types ----\n\nexport interface JiraInstanceSyncCommandInput {\n instanceId?: string;\n}\n\nconst I18N = `${RootConfig.config.i18n}:instances.entities.instance`;\n\n//#endregion\n\n//#region ---- Command ----\n\n@Injectable({ providedIn: 'root' })\nexport class AXCJiraInstanceSyncCommand implements AXPCommand<JiraInstanceSyncCommandInput, JiraInstanceSyncResult> {\n //#region ---- Services & Dependencies ----\n\n private readonly syncService = inject(AXCJiraInstanceSyncService);\n private readonly loadingDialog = inject(AXLoadingDialogService);\n private readonly translation = inject(AXTranslationService);\n\n //#endregion\n\n //#region ---- Command Execution ----\n\n async execute(input?: JiraInstanceSyncCommandInput): Promise<AXPExecuteCommandResult<JiraInstanceSyncResult>> {\n const instanceId = defaultTo(get(input, 'instanceId'), '').trim();\n if (!instanceId) {\n return {\n success: false,\n message: {\n text: await this.translation.translateAsync(`@${I18N}.actions.sync.messages.missing-instance`),\n },\n };\n }\n\n const [loadingTitle, loadingDescription] = await Promise.all([\n this.translation.translateAsync(`@${I18N}.actions.sync.loading.title`),\n this.translation.translateAsync(`@${I18N}.actions.sync.loading.description`),\n ]);\n\n const progressDialog = this.loadingDialog.show({\n title: loadingTitle,\n mode: 'indeterminate',\n progressColor: 'primary',\n text: loadingDescription,\n });\n\n try {\n const data = await this.syncService.sync({ instanceId });\n return {\n success: true,\n data,\n message: {\n text: await this.translation.translateAsync(`@${I18N}.actions.sync.messages.success`, {\n params: {\n projects: data.projects,\n issues: data.issues,\n },\n }),\n },\n };\n } catch (error) {\n const fallback = await this.translation.translateAsync(`@${I18N}.actions.sync.messages.failed`);\n let text = error instanceof Error ? error.message : fallback;\n if (error instanceof JiraApiError && error.status === 0) {\n text = await this.translation.translateAsync(`@${I18N}.actions.sync.messages.network-error`);\n }\n return {\n success: false,\n message: { text },\n };\n } finally {\n progressDialog.close();\n }\n }\n\n //#endregion\n}\n\n//#endregion\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AXPDataGenerator } from '@acorex/platform/
|
|
1
|
+
import { AXPDataGenerator } from '@acorex/platform/contracts';
|
|
2
2
|
import { AXToastService } from '@acorex/components/toast';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { inject, Injectable } from '@angular/core';
|
|
@@ -70,4 +70,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
70
70
|
}] });
|
|
71
71
|
|
|
72
72
|
export { NotificationManagementEmailSendMockCommand };
|
|
73
|
-
//# sourceMappingURL=acorex-connectivity-mock-notification-management-email-send.command-
|
|
73
|
+
//# sourceMappingURL=acorex-connectivity-mock-notification-management-email-send.command-Df6E-9-c.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-connectivity-mock-notification-management-email-send.command-Df6E-9-c.mjs","sources":["../../../../libs/connectivity/mock/src/lib/notification-management/commands/notification-management-email-send.command.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { AXPDataGenerator } from '@acorex/platform/contracts';\n\nimport { AXPExecuteCommandResult } from '@acorex/platform/core';\nimport { AXPCommand } from '@acorex/platform/runtime';\nimport type {\n NotificationManagementEmailSendInput,\n NotificationManagementEmailSendResult,\n} from '@acorex/modules/notification-management';\nimport { AXToastService } from '@acorex/components/toast';\nimport { inject, Injectable } from '@angular/core';\n\n//#endregion\n\n//#region ---- Input parsing ----\n\nfunction parseSendInput(\n raw: unknown,\n): { ok: true; value: NotificationManagementEmailSendInput } | { ok: false; error: string } {\n const obj =\n raw != null && typeof raw === 'object' && !Array.isArray(raw) ? (raw as Record<string, unknown>) : null;\n if (!obj) {\n return { ok: false, error: 'Invalid arguments: expected an object.' };\n }\n const to = typeof obj['to'] === 'string' ? obj['to'].trim() : '';\n const subject = typeof obj['subject'] === 'string' ? obj['subject'].trim() : '';\n const body = typeof obj['body'] === 'string' ? obj['body'].trim() : '';\n if (!to) {\n return { ok: false, error: 'Invalid arguments: \"to\" is required.' };\n }\n if (!subject) {\n return { ok: false, error: 'Invalid arguments: \"subject\" is required.' };\n }\n if (!body) {\n return { ok: false, error: 'Invalid arguments: \"body\" is required.' };\n }\n const cc = typeof obj['cc'] === 'string' ? obj['cc'].trim() : '';\n const bcc = typeof obj['bcc'] === 'string' ? obj['bcc'].trim() : '';\n const tid = typeof obj['textTemplateId'] === 'string' ? obj['textTemplateId'].trim() : '';\n return {\n ok: true,\n value: {\n to,\n subject,\n body,\n ...(cc ? { cc } : {}),\n ...(bcc ? { bcc } : {}),\n ...(tid ? { textTemplateId: tid } : {}),\n },\n };\n}\n\n//#endregion\n\n//#region ---- Mock send command ----\n\nconst MOCK_SEND_LATENCY_MS = 120;\n\n@Injectable()\nexport class NotificationManagementEmailSendMockCommand\n implements AXPCommand<NotificationManagementEmailSendInput, NotificationManagementEmailSendResult> {\n private readonly toastService = inject(AXToastService);\n\n async execute(\n input: NotificationManagementEmailSendInput,\n ): Promise<AXPExecuteCommandResult<NotificationManagementEmailSendResult>> {\n await new Promise<void>((resolve) => setTimeout(resolve, MOCK_SEND_LATENCY_MS));\n const parsed = parseSendInput(input);\n if (!parsed.ok) {\n return { success: false, message: { text: parsed.error } };\n }\n // Mock transport: surface as UI toast only; replace with HTTP when backend exists.\n this.toastService.show({\n color: 'warning',\n title: 'Email send (simulation)',\n content: `No email was delivered — mock only.\\n To: ${parsed.value.to}.\\n Subject: ${parsed.value.subject}.`,\n });\n return {\n success: true,\n data: { messageId: AXPDataGenerator.uuid() },\n };\n }\n}\n\n//#endregion\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable } from '@angular/core';
|
|
3
|
-
import { m as mockUsers } from './acorex-connectivity-mock-acorex-connectivity-mock-
|
|
3
|
+
import { m as mockUsers } from './acorex-connectivity-mock-acorex-connectivity-mock-BUpepPMb.mjs';
|
|
4
4
|
|
|
5
5
|
//#endregion
|
|
6
6
|
/**
|
|
@@ -27,4 +27,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
27
27
|
}] });
|
|
28
28
|
|
|
29
29
|
export { UserRolesForListColumnQuery };
|
|
30
|
-
//# sourceMappingURL=acorex-connectivity-mock-user-roles-for-list-column.query-
|
|
30
|
+
//# sourceMappingURL=acorex-connectivity-mock-user-roles-for-list-column.query-ehilblWJ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acorex-connectivity-mock-user-roles-for-list-column.query-
|
|
1
|
+
{"version":3,"file":"acorex-connectivity-mock-user-roles-for-list-column.query-ehilblWJ.mjs","sources":["../../../../libs/connectivity/mock/src/lib/security-management/user/user-roles-for-list-column.query.ts"],"sourcesContent":["import { AXPQuery } from '@acorex/platform/runtime';\nimport { Injectable } from '@angular/core';\nimport { mockUsers } from './user.mock';\n\n//#region ---- Types ----\n\nexport interface UserRolesForListColumnQueryInput {\n userId?: string;\n}\n\nexport interface UserRolesForListColumnQueryResult {\n items: { id: string; title: unknown }[];\n}\n\n//#endregion\n\n/**\n * Resolves role rows for the user list lookup column when using {@link countOnly} column resolve.\n */\n@Injectable({ providedIn: 'root' })\nexport class UserRolesForListColumnQuery implements AXPQuery<UserRolesForListColumnQueryInput, UserRolesForListColumnQueryResult> {\n async fetch(input: UserRolesForListColumnQueryInput): Promise<UserRolesForListColumnQueryResult> {\n const userId = input?.userId?.trim();\n if (!userId) {\n return { items: [] };\n }\n const user = mockUsers.find((u) => u.id === userId);\n if (!user?.roles?.length) {\n return { items: [] };\n }\n return { items: user.roles };\n }\n}\n"],"names":[],"mappings":";;;;AAcA;AAEA;;AAEG;MAEU,2BAA2B,CAAA;IACtC,MAAM,KAAK,CAAC,KAAuC,EAAA;QACjD,MAAM,MAAM,GAAG,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;QACpC,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QACtB;AACA,QAAA,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC;AACnD,QAAA,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;AACxB,YAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QACtB;AACA,QAAA,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;IAC9B;8GAXW,2BAA2B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAA3B,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,2BAA2B,cADd,MAAM,EAAA,CAAA,CAAA;;2FACnB,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBADvC,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;;;"}
|