@manyos/smileconnect-api 1.28.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/.github/workflows/nodejs.yml +26 -0
- package/CHANGELOG.md +75 -0
- package/Dockerfile +21 -0
- package/README.md +1 -0
- package/app.js +316 -0
- package/conf/clients.json +2491 -0
- package/conf/mapping.json +1048 -0
- package/conf/scripts/p1.js +11 -0
- package/conf/scripts/p2.js +1 -0
- package/conf/scripts/p3.js +1 -0
- package/conf/scripts/p4.js +2 -0
- package/conf/scripts/script1.js +2 -0
- package/conf/scripts/script2.js +2 -0
- package/conf/scripts/script3.js +2 -0
- package/controller/cmdbobjectController.js +291 -0
- package/controller/eventLogController.js +78 -0
- package/controller/orgdataController.js +440 -0
- package/controller/relatedObjectsController.js +194 -0
- package/controller/scriptController.js +213 -0
- package/controller/taskController.js +368 -0
- package/controller/templateController.js +97 -0
- package/controller/ticketCIRelationController.js +522 -0
- package/controller/ticketController.js +329 -0
- package/controller/ticketWorkLogController.js +195 -0
- package/docs/.gitattributes +48 -0
- package/docs/404.html +13 -0
- package/docs/CNAME +1 -0
- package/docs/Gemfile +7 -0
- package/docs/Gemfile.lock +249 -0
- package/docs/_config.yml +257 -0
- package/docs/_data/SocialNetworks.yml +92 -0
- package/docs/_data/ui-text.yml +494 -0
- package/docs/_includes/disqus.html +17 -0
- package/docs/_includes/ext-css.html +7 -0
- package/docs/_includes/ext-js.html +7 -0
- package/docs/_includes/fb-comment.html +14 -0
- package/docs/_includes/footer-minimal.html +16 -0
- package/docs/_includes/footer-scripts.html +32 -0
- package/docs/_includes/footer.html +51 -0
- package/docs/_includes/google_analytics.html +14 -0
- package/docs/_includes/gtag.html +11 -0
- package/docs/_includes/gtm_body.html +6 -0
- package/docs/_includes/gtm_head.html +9 -0
- package/docs/_includes/head.html +131 -0
- package/docs/_includes/header.html +76 -0
- package/docs/_includes/just_comments.html +4 -0
- package/docs/_includes/matomo.html +17 -0
- package/docs/_includes/nav.html +57 -0
- package/docs/_includes/social-share.html +42 -0
- package/docs/_includes/staticman-comment.html +22 -0
- package/docs/_includes/staticman-comments.html +81 -0
- package/docs/_layouts/base.html +35 -0
- package/docs/_layouts/default.html +9 -0
- package/docs/_layouts/minimal.html +26 -0
- package/docs/_layouts/page.html +26 -0
- package/docs/_layouts/post.html +82 -0
- package/docs/_posts/2015-02-28-test-markdown.md +77 -0
- package/docs/aboutme.md +18 -0
- package/docs/css/bootstrap-social.css +147 -0
- package/docs/css/bootstrap-theme.css +476 -0
- package/docs/css/bootstrap-theme.css.map +1 -0
- package/docs/css/bootstrap-theme.min.css +5 -0
- package/docs/css/bootstrap.css +6566 -0
- package/docs/css/bootstrap.css.map +1 -0
- package/docs/css/bootstrap.min.css +5 -0
- package/docs/css/main-minimal.css +13 -0
- package/docs/css/main.css +788 -0
- package/docs/css/normalize.css +427 -0
- package/docs/css/pygment_highlights.css +61 -0
- package/docs/css/staticman.css +180 -0
- package/docs/eventlog/events.md +65 -0
- package/docs/feed.xml +24 -0
- package/docs/general/architecture.md +10 -0
- package/docs/general/config.md +192 -0
- package/docs/general/field-management.md +119 -0
- package/docs/general/release-notes.md +9 -0
- package/docs/getting-started.md +19 -0
- package/docs/howto/cmdbobjects.md +339 -0
- package/docs/howto/incident-worklogs.md +186 -0
- package/docs/howto/incidents.md +244 -0
- package/docs/howto/sample-config.md +518 -0
- package/docs/howto/token.md +71 -0
- package/docs/howto/worklog-attachment.md +113 -0
- package/docs/img/404-southpark.jpg +0 -0
- package/docs/img/architecture.jpeg +0 -0
- package/docs/img/attachment-upload.png +0 -0
- package/docs/img/avatar-icon.png +0 -0
- package/docs/img/bgimage.png +0 -0
- package/docs/img/gb-isapi.jpg +0 -0
- package/docs/img/install-steps.gif +0 -0
- package/docs/img/workflow.png +0 -0
- package/docs/index.md +41 -0
- package/docs/installation.md +123 -0
- package/docs/js/bootstrap.js +2306 -0
- package/docs/js/bootstrap.min.js +7 -0
- package/docs/js/jquery-1.11.2.min.js +4 -0
- package/docs/js/main.js +140 -0
- package/docs/js/staticman.js +54 -0
- package/docs/openapi.json +15097 -0
- package/docs/postinstall.md +169 -0
- package/docs/preinstall.md +19 -0
- package/docs/spec/index.html +24 -0
- package/docs/staticman.yml +110 -0
- package/docs/tags.html +34 -0
- package/docs/workflow.md +127 -0
- package/nodemon.json +3 -0
- package/package.json +46 -0
- package/routes/appConfigRoutes.js +352 -0
- package/routes/ciRelationRoutes.js +38 -0
- package/routes/cmdbObjectRoutes.js +154 -0
- package/routes/organisationRoutes.js +121 -0
- package/routes/peopleRelationRoutes.js +38 -0
- package/routes/personRoutes.js +131 -0
- package/routes/supportgroupRoutes.js +122 -0
- package/routes/taskRoutes.js +306 -0
- package/routes/templateRoutes.js +67 -0
- package/routes/ticketRoutes.js +181 -0
- package/routes/ticketWorkLogRoutes.js +185 -0
- package/screwdriver.yaml +52 -0
- package/test/appTest.js +3 -0
- package/test/changeTest.js +541 -0
- package/test/cmdbobjectTest.js +167 -0
- package/test/files/logo.png +0 -0
- package/test/incidentTest.js +539 -0
- package/test/orgdataTest.js +156 -0
- package/test/problemTest.js +512 -0
- package/test/templateTest.js +80 -0
- package/test/testUtils.js +21 -0
- package/test/workorderTest.js +544 -0
- package/util/arquery.js +416 -0
- package/util/auth.js +37 -0
- package/util/cache.service.js +52 -0
- package/util/config.js +361 -0
- package/util/constants.js +73 -0
- package/util/mappingUtil.js +96 -0
- package/util/paramHelper.js +43 -0
- package/util/relationUtil.js +63 -0
- package/util/responsehandler.js +92 -0
- package/util/schemas/clientConfigSchema.js +180 -0
- package/util/schemas/fieldMappingSchema.js +211 -0
- package/util/searchUtil.js +148 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const log = require('@manyos/logger').setupLog('SMILEconnect_' + path.basename(__filename));
|
|
3
|
+
const CONSTANTS = require('../util/constants');
|
|
4
|
+
const arquery = require('../util/arquery');
|
|
5
|
+
const CacheService = require ('../util/cache.service');
|
|
6
|
+
const {getLinkCI, applyMapping} = require('../util/paramHelper');
|
|
7
|
+
//const cmdbObjectController = require('./cmdbobjectController');
|
|
8
|
+
const {ticketConfig} = require ('../util/config');
|
|
9
|
+
|
|
10
|
+
const utilCache = new CacheService(process.env.CACHETTL_TICKETCIRELATIONS || 600); // Create a new cache service instance
|
|
11
|
+
|
|
12
|
+
function getSchemaName(lookupKeyword) {
|
|
13
|
+
log.debug('Search for SchemaName', lookupKeyword);
|
|
14
|
+
const query = `'Lookup Keyword'="${lookupKeyword}" AND 'Locale' = $NULL$`;
|
|
15
|
+
const form = CONSTANTS.FORM_SCHEMA_NAMES;
|
|
16
|
+
return utilCache.get(form + ':' + query, function () {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
//TODO: Handle error for CMDB Objects without asset form
|
|
19
|
+
arquery.executeARQuery(form, null, query, ['Schema Name']).then(queryResponse => {
|
|
20
|
+
const schemaName = queryResponse.data[0]['Schema Name'];
|
|
21
|
+
log.debug('Found schemaName', schemaName);
|
|
22
|
+
resolve(schemaName);
|
|
23
|
+
}).catch(reason => {
|
|
24
|
+
reject(reason);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function getTicketCIRelations(ticketId) {
|
|
31
|
+
const relations = await getCITicketRelations(null, ticketId);
|
|
32
|
+
if (relations && Array.isArray(relations)) {
|
|
33
|
+
let x=0;
|
|
34
|
+
for (x=0;x<relations.length;x++) {
|
|
35
|
+
const item = relations[x];
|
|
36
|
+
delete item['ticketId'];
|
|
37
|
+
const oppositeType = await getOppositeAssocTypeByType(item.relationType);
|
|
38
|
+
if (oppositeType) {
|
|
39
|
+
item.relationType = oppositeType.oppositeType;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return relations;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function getCITicketRelations(ciInstanceId, ticketId) {
|
|
47
|
+
let ci2ticketOnly = false;
|
|
48
|
+
if (ticketId === null || ticketId === undefined) {
|
|
49
|
+
ci2ticketOnly = true;
|
|
50
|
+
}
|
|
51
|
+
log.debug('Search for CI', ciInstanceId);
|
|
52
|
+
let query = '';
|
|
53
|
+
query = '';
|
|
54
|
+
if (ciInstanceId !== null && ciInstanceId !== undefined) {
|
|
55
|
+
const ciData = await getCIData(ciInstanceId);
|
|
56
|
+
log.debug('found CI', ciData);
|
|
57
|
+
query = query + `'Request ID02' = "${ciData.ReconciliationIdentity}"`;
|
|
58
|
+
}
|
|
59
|
+
if (ticketId != null && ticketId != undefined && ciInstanceId != null && ciInstanceId != undefined) {
|
|
60
|
+
query = query + ' AND ';
|
|
61
|
+
}
|
|
62
|
+
if (ticketId != null && ticketId != undefined) {
|
|
63
|
+
query = query + `'Request ID01' = "${ticketId}"`;
|
|
64
|
+
}
|
|
65
|
+
const form = CONSTANTS.FORM_ASSET_TICKET_ASSOC;
|
|
66
|
+
|
|
67
|
+
const result = await arquery.executeARQuery(form, null, query, ['Request ID02', 'Request ID01', 'Association Type01', 'Request Type01', 'Request Description01']);
|
|
68
|
+
const relations = [];
|
|
69
|
+
for (const relationData of result.data) {
|
|
70
|
+
let relation = {};
|
|
71
|
+
if (ci2ticketOnly) {
|
|
72
|
+
relation = {
|
|
73
|
+
ticketId: relationData['Request ID01'],
|
|
74
|
+
ticketType: relationData['Request Type01'],
|
|
75
|
+
ticketSummary: relationData['Request Description01'],
|
|
76
|
+
relationType: relationData['Association Type01']
|
|
77
|
+
};
|
|
78
|
+
} else {
|
|
79
|
+
const ci = await getCiByReconId(relationData['Request ID02']);
|
|
80
|
+
relation = {
|
|
81
|
+
ciId: ci['Instance Id'],
|
|
82
|
+
ciName: ci['Name'],
|
|
83
|
+
ciClass: ci['Class Id'],
|
|
84
|
+
ciStatus: ci['AssetLifecycleStatus'],
|
|
85
|
+
ciReconId: relationData['Request ID02'],
|
|
86
|
+
relationType: relationData['Association Type01'],
|
|
87
|
+
link: getLinkCI(ci['Instance Id'])
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
relations.push(relation);
|
|
91
|
+
}
|
|
92
|
+
return relations;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function getTicket2TicketRelations(ticketConfig, ticketId) {
|
|
96
|
+
const form = ticketConfig.forms.assoc;
|
|
97
|
+
const query = `'Request ID02' = "${ticketId}" AND NOT 'Request Type01' = "Configuration Item"`;
|
|
98
|
+
const result = await arquery.executeARQuery(form, null, query, ['Request ID02', 'Request ID01', 'Association Type01', 'Request Type01', 'Request Description01']);
|
|
99
|
+
const relations = [];
|
|
100
|
+
for (const relationData of result.data) {
|
|
101
|
+
const relation = {
|
|
102
|
+
ticketId: relationData['Request ID01'],
|
|
103
|
+
relationType: relationData['Association Type01'],
|
|
104
|
+
ticketType: relationData['Request Type01'],
|
|
105
|
+
ticketSummary: relationData['Request Description01']
|
|
106
|
+
};
|
|
107
|
+
relations.push(relation);
|
|
108
|
+
}
|
|
109
|
+
return relations;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function getAssocLookup(ticketForm, relationObject, assocType) {
|
|
113
|
+
const assocTypes = await getAssocTypesLookup(ticketForm, relationObject);
|
|
114
|
+
return assocTypes.find(assoc => {
|
|
115
|
+
return assoc.assocType === assocType;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function createTicketCIRelation(ticketConfig, ticketId, ciInstanceId, assocType, ticketSummary) {
|
|
120
|
+
if (ticketSummary === null || ticketSummary === undefined) {
|
|
121
|
+
ticketSummary = 'summary';
|
|
122
|
+
}
|
|
123
|
+
const ticketForm = ticketConfig.forms.regular;
|
|
124
|
+
const ticketRelationForm = ticketConfig.forms.assoc;
|
|
125
|
+
const ticketKeyword = ticketConfig.assocTicketKeyword;
|
|
126
|
+
const ticketType = ticketConfig.assocTicketType;
|
|
127
|
+
|
|
128
|
+
const assocLookup = await getAssocLookup(ticketForm, 'Configuration Item', assocType);
|
|
129
|
+
if (assocLookup === null || assocLookup === undefined) {
|
|
130
|
+
throw new Error(`Association Type ${assocType} not found.`);
|
|
131
|
+
}
|
|
132
|
+
log.debug('found AssocType', assocLookup);
|
|
133
|
+
const ciData = await getCIData(ciInstanceId);
|
|
134
|
+
if (ciData === null || ciData === undefined) {
|
|
135
|
+
throw new Error(`CI ${ciInstanceId} not found.`);
|
|
136
|
+
}
|
|
137
|
+
const schemaName = await getSchemaName(ciData.ClassId);
|
|
138
|
+
const ticketRelationFormData = {
|
|
139
|
+
"Form Name01" : schemaName,
|
|
140
|
+
"Request ID01" : ciData.ReconciliationIdentity,
|
|
141
|
+
"Request Type01" : 6000,
|
|
142
|
+
"Form Name02" : ticketForm,
|
|
143
|
+
"Association Type01": assocLookup.assocCode,
|
|
144
|
+
"Request Description01": ciData.Name,
|
|
145
|
+
"Request ID02": ticketId,
|
|
146
|
+
"Lookup Keyword": ciData.ClassId
|
|
147
|
+
};
|
|
148
|
+
const assetRelationFormData = {
|
|
149
|
+
"Form Name01" : ticketForm,
|
|
150
|
+
"Request ID01" : ticketId,
|
|
151
|
+
"Request Type01" : ticketType,
|
|
152
|
+
"Form Name02" : schemaName,
|
|
153
|
+
"Lookup Keyword01": ticketKeyword,
|
|
154
|
+
"Association Type01": assocLookup.oppositeCode,
|
|
155
|
+
"Request Description01": ticketSummary,
|
|
156
|
+
"Request ID02": ciData.ReconciliationIdentity,
|
|
157
|
+
"Lookup Keyword": ciData.ClassId,
|
|
158
|
+
"Parent_DataSet_ID01": "BMC.ASSET"
|
|
159
|
+
};
|
|
160
|
+
log.debug('Create CI Ticket Relation', ticketRelationFormData);
|
|
161
|
+
//todo: implement impersonate
|
|
162
|
+
const ticketRelation = await arquery.createEntry(ticketRelationForm, ticketRelationFormData);
|
|
163
|
+
const assetRelation = await arquery.createEntry(CONSTANTS.FORM_ASSET_TICKET_ASSOC, assetRelationFormData);
|
|
164
|
+
return {ticketRelation, assetRelation}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function createTicket2TicketRelation(ticketConfig, ticketId1, ticketConfig2, ticketId2, assocType, ticketSummary) {
|
|
168
|
+
//todo fix request description
|
|
169
|
+
if (ticketSummary === null || ticketSummary === undefined) {
|
|
170
|
+
ticketSummary = 'summary';
|
|
171
|
+
}
|
|
172
|
+
const ticketForm = ticketConfig.forms.regular;
|
|
173
|
+
|
|
174
|
+
const assocLookup = await getAssocLookup(ticketForm, ticketConfig2.assocTicketType, assocType);
|
|
175
|
+
if (assocLookup === null || assocLookup === undefined) {
|
|
176
|
+
throw new Error(`Association Type ${assocType} not found.`);
|
|
177
|
+
}
|
|
178
|
+
log.debug('found AssocType', assocLookup);
|
|
179
|
+
const ticketRelationFormData1 = {
|
|
180
|
+
"Form Name01" : ticketConfig2.forms.regular,
|
|
181
|
+
"Request ID01" : ticketId2,
|
|
182
|
+
"Request Type01" : ticketConfig2.assocTicketType,
|
|
183
|
+
"Form Name02" : ticketConfig.forms.regular,
|
|
184
|
+
"Association Type01": assocLookup.assocCode,
|
|
185
|
+
"Request Description01": ticketSummary,
|
|
186
|
+
"Request ID02": ticketId1,
|
|
187
|
+
"Lookup Keyword": ticketConfig2.assocTicketKeyword
|
|
188
|
+
};
|
|
189
|
+
const ticketRelationFormData2 = {
|
|
190
|
+
"Form Name01" : ticketConfig.forms.regular,
|
|
191
|
+
"Request ID01" : ticketId1,
|
|
192
|
+
"Request Type01" : ticketConfig.assocTicketType,
|
|
193
|
+
"Form Name02" : ticketConfig2.forms.regular,
|
|
194
|
+
"Association Type01": assocLookup.assocCode,
|
|
195
|
+
"Request Description01": ticketSummary,
|
|
196
|
+
"Request ID02": ticketId2,
|
|
197
|
+
"Lookup Keyword": ticketConfig2.assocTicketKeyword
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
//todo: implement impersonate
|
|
201
|
+
log.debug('Create Ticket 2 Ticket Relation', ticketRelationFormData1, ticketRelationFormData2);
|
|
202
|
+
const ticketRelation1 = await arquery.createEntry(ticketConfig.forms.assoc, ticketRelationFormData1);
|
|
203
|
+
const ticketRelation2 = await arquery.createEntry(ticketConfig2.forms.assoc, ticketRelationFormData2);
|
|
204
|
+
return {ticketRelation1, ticketRelation2}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
async function deleteTicketCIRelation(ticketConfig, ticketId, ciInstanceId) {
|
|
209
|
+
const ticketRelationForm = ticketConfig.forms.assoc;
|
|
210
|
+
const ciData = await getCIData(ciInstanceId);
|
|
211
|
+
const ticketRelationQuery = `'Request ID02'= "${ticketId}" AND 'Request ID01' = "${ciData.ReconciliationIdentity}"`;
|
|
212
|
+
const assetRelationQuery = `'Request ID02'= "${ciData.ReconciliationIdentity}" AND 'Request ID01' = "${ticketId}"`;
|
|
213
|
+
log.debug('Delete Ticket2CI Relation', ticketRelationQuery);
|
|
214
|
+
log.debug('Delete CI2Ticket Relation', assetRelationQuery);
|
|
215
|
+
const ticketRelation = await arquery.deleteEntries(ticketRelationForm, '1=1', ticketRelationQuery);
|
|
216
|
+
const assetRelation = await arquery.deleteEntries(CONSTANTS.FORM_ASSET_TICKET_ASSOC, '1=1', assetRelationQuery);
|
|
217
|
+
return {ticketRelation, assetRelation}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function deleteTicket2TicketRelation(ticketConfig1, ticketId1, ticketConfig2, ticketId2) {
|
|
221
|
+
const ticketRelationQuery1 = `'Request ID02'= "${ticketId1}" AND 'Request ID01' = "${ticketId2}"`;
|
|
222
|
+
const ticketRelationQuery2 = `'Request ID02'= "${ticketId2}" AND 'Request ID01' = "${ticketId1}"`;
|
|
223
|
+
log.debug('Delete Ticket2Ticket Relation1', ticketRelationQuery1);
|
|
224
|
+
log.debug('Delete Ticket2Ticket Relation2', ticketRelationQuery2);
|
|
225
|
+
const ticketRelation1 = await arquery.deleteEntries(ticketConfig1.forms.assoc, '1=1', ticketRelationQuery1);
|
|
226
|
+
const ticketRelation2 = await arquery.deleteEntries(ticketConfig2.forms.assoc, '1=1', ticketRelationQuery2);
|
|
227
|
+
return {ticketRelation1, ticketRelation2}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function prepareGlobalRelationObject() {
|
|
231
|
+
const globalRelationObjects = {
|
|
232
|
+
"persons":new Set(),
|
|
233
|
+
"organisations":new Set(),
|
|
234
|
+
"supportgroups":new Set(),
|
|
235
|
+
"cmdbObjects":new Set(),
|
|
236
|
+
"incidents":new Set(),
|
|
237
|
+
"changes":new Set(),
|
|
238
|
+
"problems":new Set(),
|
|
239
|
+
"workOrders":new Set()
|
|
240
|
+
};
|
|
241
|
+
return globalRelationObjects;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function getRelatedObjectList(cmdbObject, includeArray, globalRelationObjects) {
|
|
245
|
+
if (cmdbObject.relations) {
|
|
246
|
+
if (cmdbObject.relations.personRelations) {
|
|
247
|
+
cmdbObject.relations.personRelations.forEach(personRelation => {
|
|
248
|
+
globalRelationObjects.persons.add(personRelation.id);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (cmdbObject.relations.supportGroupRelations) {
|
|
252
|
+
cmdbObject.relations.supportGroupRelations.forEach(personRelation => {
|
|
253
|
+
globalRelationObjects.supportgroups.add(personRelation.id);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
if (cmdbObject.relations.organisationRelations) {
|
|
257
|
+
cmdbObject.relations.organisationRelations.forEach(orgRelation => {
|
|
258
|
+
globalRelationObjects.organisations.add(orgRelation.id);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
if (cmdbObject.relations.ciRelations) {
|
|
262
|
+
cmdbObject.relations.ciRelations.forEach(ciRelation => {
|
|
263
|
+
globalRelationObjects.cmdbObjects.add(ciRelation.ciId);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (cmdbObject.relations.ticketRelations) {
|
|
267
|
+
cmdbObject.relations.ticketRelations.forEach(ticketRelation => {
|
|
268
|
+
if (ticketRelation.ticketType === 'Incident') {
|
|
269
|
+
globalRelationObjects.incidents.add(ticketRelation.ticketId);
|
|
270
|
+
} else if (ticketRelation.ticketType === 'Work Order') {
|
|
271
|
+
globalRelationObjects.workOrders.add(ticketRelation.ticketId);
|
|
272
|
+
} else if (ticketRelation.ticketType === 'Infrastructure Change') {
|
|
273
|
+
globalRelationObjects.changes.add(ticketRelation.ticketId);
|
|
274
|
+
} else if (ticketRelation.ticketType === 'Problem Investigation') {
|
|
275
|
+
globalRelationObjects.problems.add(ticketRelation.ticketId);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function getAssocTypes() {
|
|
284
|
+
log.debug('read assoc type config');
|
|
285
|
+
let fields = ['Association Type01', 'Selection Code', 'OppositeAssocType', 'OppositeSelectionCode'];
|
|
286
|
+
|
|
287
|
+
const form = CONSTANTS.FORM_SYS_ASSOCIATION_TYPES_OPPO;
|
|
288
|
+
|
|
289
|
+
const mapping = [
|
|
290
|
+
{
|
|
291
|
+
"oldName": "Association Type01",
|
|
292
|
+
"newName": "assocType"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"oldName": "Selection Code",
|
|
296
|
+
"newName": "assocCode"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"oldName": "OppositeAssocType",
|
|
300
|
+
"newName": "oppositeType"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"oldName": "OppositeSelectionCode",
|
|
304
|
+
"newName": "oppositeCode"
|
|
305
|
+
}
|
|
306
|
+
];
|
|
307
|
+
log.debug('mapping', mapping);
|
|
308
|
+
|
|
309
|
+
return utilCache.get(form, async function () {
|
|
310
|
+
const result = await arquery.executeARQuery(form, null, '1=1', fields.toString() || '1');
|
|
311
|
+
if (result && result.data && result.data.length) {
|
|
312
|
+
let x = 0;
|
|
313
|
+
for (x = 0; x < result.data.length; x++) {
|
|
314
|
+
applyMapping(result.data[x],mapping,'Request ID');
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
log.debug('result', result.data);
|
|
318
|
+
return result.data;
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function getAssocTypesLookup(primaryForm, relationObject) {
|
|
323
|
+
log.debug('read assoc type config');
|
|
324
|
+
let fields = ['Association Type01', 'Selection Code', 'RequestTypeSelectionCode'];
|
|
325
|
+
|
|
326
|
+
const form = CONSTANTS.FORM_SYS_ASSOCIATION_TYPES_LOOKUP;
|
|
327
|
+
|
|
328
|
+
const mapping = [
|
|
329
|
+
{
|
|
330
|
+
"oldName": "Association Type01",
|
|
331
|
+
"newName": "assocType"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"oldName": "Selection Code",
|
|
335
|
+
"newName": "assocCode"
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"oldName": "RequestTypeSelectionCode",
|
|
339
|
+
"newName": "requestTypeSelectionCode"
|
|
340
|
+
}
|
|
341
|
+
];
|
|
342
|
+
log.debug('mapping', mapping);
|
|
343
|
+
|
|
344
|
+
const query = `'Locale' = $NULL$ AND 'Status-ATA' = "Enabled" AND 'Status-ATY' = "Enabled" AND 'Status-RTY' = "Enabled" AND 'Form Name02' = "${primaryForm}" AND 'Request Type01' = "${relationObject}"`;
|
|
345
|
+
|
|
346
|
+
return utilCache.get(primaryForm,async function () {
|
|
347
|
+
const result = await arquery.executeARQuery(form, null, query, fields.toString() || '1');
|
|
348
|
+
if (result && result.data && result.data.length) {
|
|
349
|
+
let x = 0;
|
|
350
|
+
for (x = 0; x < result.data.length; x++) {
|
|
351
|
+
const assocLookup = result.data[x];
|
|
352
|
+
applyMapping(assocLookup,mapping,'AssociationTypeAssocLookUP ID');
|
|
353
|
+
const opposite = await getOppositeAssocTypeByCode(assocLookup.assocCode);
|
|
354
|
+
assocLookup.oppositeCode = opposite.oppositeCode;
|
|
355
|
+
assocLookup.oppositeType = opposite.oppositeType;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
log.debug('result', result.data);
|
|
359
|
+
return result.data;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function getOppositeAssocTypeByType(assocType) {
|
|
364
|
+
const assocTypes = await getAssocTypes();
|
|
365
|
+
return assocTypes.find(assoc => {
|
|
366
|
+
return assoc.assocType === assocType;
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function getOppositeAssocTypeByCode(assocCode) {
|
|
371
|
+
const assocTypes = await getAssocTypes();
|
|
372
|
+
return assocTypes.find(assoc => {
|
|
373
|
+
return assoc.assocCode === assocCode;
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function doesCIRelationExists(ticketRelations, newRelation) {
|
|
378
|
+
const relationFound = ticketRelations.find(relation => {
|
|
379
|
+
return (relation.ciId === newRelation.ciId &&
|
|
380
|
+
relation.relationType === newRelation.relationType);
|
|
381
|
+
});
|
|
382
|
+
if (relationFound) {
|
|
383
|
+
return true;
|
|
384
|
+
} else {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function doesTicketRelationExists(ticketRelations, newRelation) {
|
|
390
|
+
const relationFound = ticketRelations.find(relation => {
|
|
391
|
+
return (relation.ticketId === newRelation.ticketId &&
|
|
392
|
+
relation.relationType === newRelation.relationType);
|
|
393
|
+
});
|
|
394
|
+
if (relationFound) {
|
|
395
|
+
return true;
|
|
396
|
+
} else {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function updateRelations(ticketConfig, userConfig, ticketId, relations) {
|
|
402
|
+
log.debug('Checker', {ticketConfig, userConfig, ticketId, relations});
|
|
403
|
+
//Cehck CI Relations
|
|
404
|
+
if (relations && relations.ciRelations && Array.isArray(relations.ciRelations)) {
|
|
405
|
+
const existingRelations = await getTicketCIRelations(ticketId);
|
|
406
|
+
log.debug('foundRelations', existingRelations);
|
|
407
|
+
let x=0;
|
|
408
|
+
//check for missiong relations
|
|
409
|
+
if (relations.ciRelations && Array.isArray(relations.ciRelations)) {
|
|
410
|
+
for (x=0; x<relations.ciRelations.length; x++) {
|
|
411
|
+
const relation = relations.ciRelations[x];
|
|
412
|
+
const relationsExists = doesCIRelationExists(existingRelations, relation);
|
|
413
|
+
log.debug('createRelation, already exists', relation, relationsExists);
|
|
414
|
+
if (!relationsExists) {
|
|
415
|
+
await createTicketCIRelation(ticketConfig, ticketId, relation.ciId, relation.relationType);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
//check for too many relations
|
|
420
|
+
if (existingRelations && Array.isArray(existingRelations)) {
|
|
421
|
+
for (x=0; x<existingRelations.length; x++) {
|
|
422
|
+
const relation = existingRelations[x];
|
|
423
|
+
const shouldRelationsExists = doesCIRelationExists(relations.ciRelations, relation);
|
|
424
|
+
log.debug('deleteRelation, already exists', relation, shouldRelationsExists);
|
|
425
|
+
if (!shouldRelationsExists) {
|
|
426
|
+
await deleteTicketCIRelation(ticketConfig, ticketId, relation.ciId);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
//check ticket2ticket relations
|
|
432
|
+
if (relations && relations.ticketRelations && Array.isArray(relations.ticketRelations)) {
|
|
433
|
+
const existingRelations = await getTicket2TicketRelations(ticketConfig, ticketId);
|
|
434
|
+
log.debug('foundRelations', existingRelations);
|
|
435
|
+
let x=0;
|
|
436
|
+
//check for missiong relations
|
|
437
|
+
if (relations.ticketRelations && Array.isArray(relations.ticketRelations)) {
|
|
438
|
+
for (x=0; x<relations.ticketRelations.length; x++) {
|
|
439
|
+
const relation = relations.ticketRelations[x];
|
|
440
|
+
const relationsExists = doesTicketRelationExists(existingRelations, relation);
|
|
441
|
+
log.debug('createRelation, already exists', relation, relationsExists);
|
|
442
|
+
if (!relationsExists) {
|
|
443
|
+
const ticketConfig2 = getTicketConfigByTicketType(relation.ticketType);
|
|
444
|
+
if (ticketConfig2 === null || ticketConfig2 === undefined) {
|
|
445
|
+
throw new Error(`TicketConfig for ${relation.ticketType} not found.`);
|
|
446
|
+
}
|
|
447
|
+
await createTicket2TicketRelation(ticketConfig, ticketId, ticketConfig2, relation.ticketId, relation.relationType);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
//check for too many relations
|
|
453
|
+
if (existingRelations && Array.isArray(existingRelations)) {
|
|
454
|
+
for (x=0; x<existingRelations.length; x++) {
|
|
455
|
+
const relation = existingRelations[x];
|
|
456
|
+
const shouldRelationsExists = doesTicketRelationExists(relations.ticketRelations, relation);
|
|
457
|
+
log.debug('deleteRelation, already exists', relation, shouldRelationsExists);
|
|
458
|
+
if (!shouldRelationsExists) {
|
|
459
|
+
const ticketConfig2 = getTicketConfigByTicketType(relation.ticketType);
|
|
460
|
+
if (ticketConfig2 === null || ticketConfig2 === undefined) {
|
|
461
|
+
throw new Error(`TicketConfig for ${relation.ticketType} not found.`);
|
|
462
|
+
}
|
|
463
|
+
await deleteTicket2TicketRelation(ticketConfig, ticketId, ticketConfig2, relation.ticketId);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function getTicketConfigByTicketType(ticketType) {
|
|
471
|
+
const keys = Object.keys(ticketConfig);
|
|
472
|
+
let config = null;
|
|
473
|
+
keys.forEach(key => {
|
|
474
|
+
const currentConfig = ticketConfig[key];
|
|
475
|
+
if (currentConfig.assocTicketType === ticketType) {
|
|
476
|
+
config = currentConfig;
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
return config;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function getCIData(instanceId) {
|
|
484
|
+
log.debug('Search for CI', instanceId);
|
|
485
|
+
//get only CIs with recon
|
|
486
|
+
const query = `'InstanceId'="${instanceId}" AND 'ReconciliationIdentity' != $NULL$ AND 'ReconciliationIdentity' != "0"`;
|
|
487
|
+
const form = CONSTANTS.FORM_CMDB_CI_BASE;
|
|
488
|
+
return utilCache.get(form + ':' + query, function () {
|
|
489
|
+
return new Promise((resolve, reject) => {
|
|
490
|
+
arquery.executeARQuery(form, null, query, ['ReconciliationIdentity', 'ClassId', 'Name']).then(queryResponse => {
|
|
491
|
+
//const schemaName = queryResponse.data[0]['Schema Name'];
|
|
492
|
+
log.debug('Found ci', queryResponse.data);
|
|
493
|
+
resolve(queryResponse.data[0]);
|
|
494
|
+
}).catch(reason => {
|
|
495
|
+
reject(reason);
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async function getCiByReconId(reconId) {
|
|
502
|
+
log.debug('Search for CI', reconId);
|
|
503
|
+
//get only CIs with recon
|
|
504
|
+
const query = `'Reconciliation Identity'="${reconId}" AND 'Data Set Id' = "BMC.ASSET"`;
|
|
505
|
+
const form = CONSTANTS.FORM_CMDB_AST_BASE;
|
|
506
|
+
const result = await arquery.executeARQuery(form, null, query, ['InstanceId', 'AssetLifecycleStatus', 'Name', 'Class Id', 'Instance Id']);
|
|
507
|
+
if (result !== null && result !== undefined && result.data !== undefined) {
|
|
508
|
+
return result.data[0];
|
|
509
|
+
} else {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
module.exports = {
|
|
516
|
+
deleteTicketCIRelation,
|
|
517
|
+
getTicketCIRelations,
|
|
518
|
+
getCITicketRelations,
|
|
519
|
+
getTicket2TicketRelations,
|
|
520
|
+
createTicketCIRelation,
|
|
521
|
+
updateRelations
|
|
522
|
+
};
|