@manyos/smileconnect-api 1.74.0 → 1.74.3
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/.claude/settings.local.json +4 -1
- package/controller/scriptController.js +1 -1
- package/docs/releases.md +7 -1
- package/package.json +1 -1
- package/routes/appConfigRoutes.js +10 -6
- package/util/config.js +63 -23
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"Bash(npm view *)",
|
|
8
8
|
"Bash(npm pack *)",
|
|
9
9
|
"WebFetch(domain:unpkg.com)",
|
|
10
|
-
"WebFetch(domain:www.npmjs.com)"
|
|
10
|
+
"WebFetch(domain:www.npmjs.com)",
|
|
11
|
+
"Bash(node *)",
|
|
12
|
+
"Bash(grep -E '^\\(p-queue|proper-lockfile|async-mutex|p-limit\\)$')",
|
|
13
|
+
"Bash(awk NR>=160 && NR<=165 {printf \"%d: \", NR; for \\(i=1;i<=length\\($0\\);i++\\) {c=substr\\($0,i,1\\); printf \"%s\", c} printf \"\\\\n\"} *)"
|
|
11
14
|
]
|
|
12
15
|
}
|
|
13
16
|
}
|
package/docs/releases.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
3
|
## API
|
|
4
|
-
### 1.
|
|
4
|
+
### 1.74.3 - 20.05.26
|
|
5
|
+
Improve client config validation to avoid duplicate client configs.
|
|
6
|
+
|
|
7
|
+
### 1.74.1 - 12.05.26
|
|
8
|
+
Fix issue with dynamic openapi spec for worklogs.
|
|
9
|
+
|
|
10
|
+
### 1.74.0 - 29.04.26
|
|
5
11
|
Update node.js to v22 and replace request-native with native fetch in adapter.
|
|
6
12
|
|
|
7
13
|
### 1.73.0 - 28.04.26
|
package/package.json
CHANGED
|
@@ -295,7 +295,7 @@ module.exports = (function() {
|
|
|
295
295
|
});
|
|
296
296
|
|
|
297
297
|
routes.delete('/clients/:id', isAuthorizedAdmin,
|
|
298
|
-
function (req, res, next) {
|
|
298
|
+
async function (req, res, next) {
|
|
299
299
|
const id = req.params.id;
|
|
300
300
|
setEventData(
|
|
301
301
|
req,
|
|
@@ -305,7 +305,11 @@ module.exports = (function() {
|
|
|
305
305
|
id
|
|
306
306
|
);
|
|
307
307
|
try {
|
|
308
|
-
config.deleteClient(id);
|
|
308
|
+
const removed = await config.deleteClient(id);
|
|
309
|
+
if (!removed) {
|
|
310
|
+
req.errorStatus = 404;
|
|
311
|
+
return next(new Error(`client '${id}' not found`));
|
|
312
|
+
}
|
|
309
313
|
req.result = {
|
|
310
314
|
"status": "success"
|
|
311
315
|
};
|
|
@@ -317,7 +321,7 @@ module.exports = (function() {
|
|
|
317
321
|
|
|
318
322
|
routes.post('/clients', isAuthorizedAdmin,
|
|
319
323
|
checkSchema(clientConfigSchema),
|
|
320
|
-
function (req, res, next) {
|
|
324
|
+
async function (req, res, next) {
|
|
321
325
|
const origData = JSON.parse(JSON.stringify(req.body));
|
|
322
326
|
setEventData(
|
|
323
327
|
req,
|
|
@@ -336,7 +340,7 @@ module.exports = (function() {
|
|
|
336
340
|
try {
|
|
337
341
|
const clientConfig = req.body.data;
|
|
338
342
|
const clientName = clientConfig.name;
|
|
339
|
-
config.setClient(clientName, clientConfig)
|
|
343
|
+
await config.setClient(clientName, clientConfig)
|
|
340
344
|
const savedClientConfig = config.getClient(clientName);
|
|
341
345
|
req.result = {
|
|
342
346
|
"data": savedClientConfig
|
|
@@ -351,7 +355,7 @@ module.exports = (function() {
|
|
|
351
355
|
|
|
352
356
|
routes.put('/clients/:id', isAuthorizedAdmin,
|
|
353
357
|
checkSchema(clientConfigSchema),
|
|
354
|
-
function (req, res, next) {
|
|
358
|
+
async function (req, res, next) {
|
|
355
359
|
const id = req.params.id;
|
|
356
360
|
const origData = JSON.parse(JSON.stringify(req.body));
|
|
357
361
|
setEventData(
|
|
@@ -370,7 +374,7 @@ module.exports = (function() {
|
|
|
370
374
|
try {
|
|
371
375
|
const clientConfig = req.body.data;
|
|
372
376
|
const clientName = clientConfig.name;
|
|
373
|
-
config.setClient(id, clientConfig);
|
|
377
|
+
await config.setClient(id, clientConfig);
|
|
374
378
|
const savedClientConfig = config.getClient(clientName);
|
|
375
379
|
req.result = {
|
|
376
380
|
"data": savedClientConfig
|
package/util/config.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const fsPromises = require('fs').promises;
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const pLimit = require('p-limit');
|
|
3
5
|
require('dotenv').config();
|
|
4
6
|
|
|
5
7
|
const path = require('path');
|
|
6
8
|
const log = require('@manyos/logger').setupLog('SMILEconnect', path.basename(__filename));
|
|
7
9
|
|
|
8
10
|
const clientConfigFile = 'conf/clients.json';
|
|
11
|
+
const clientConfigWriteLimit = pLimit(1);
|
|
9
12
|
const arquery = require('../util/arquery');
|
|
10
13
|
const mappingUtil = require('../util/mappingUtil');
|
|
11
14
|
const CacheService = require ('../util/cache.service');
|
|
@@ -123,29 +126,66 @@ function setMappings(mapping) {
|
|
|
123
126
|
return true
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
function setClient(clientName, clientConfig) {
|
|
129
|
+
async function setClient(clientName, clientConfig) {
|
|
127
130
|
log.debug('start to update config in file', clientConfigFile);
|
|
128
131
|
log.debug('set client data', clientName, clientConfig);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
if (!clientConfig || typeof clientConfig !== 'object' || !clientConfig.name) {
|
|
133
|
+
throw new Error('setClient: clientConfig.name is required');
|
|
134
|
+
}
|
|
135
|
+
const normalizedParam = typeof clientName === 'string' ? clientName.trim() : clientName;
|
|
136
|
+
clientConfig.name = clientConfig.name.trim();
|
|
137
|
+
return clientConfigWriteLimit(async () => {
|
|
138
|
+
let config;
|
|
139
|
+
try {
|
|
140
|
+
const raw = await fsPromises.readFile(clientConfigFile, 'utf8');
|
|
141
|
+
config = JSON.parse(raw);
|
|
142
|
+
} catch (e) {
|
|
143
|
+
log.warn('setClient: cannot parse existing clients.json, starting empty', e.message);
|
|
144
|
+
config = [];
|
|
145
|
+
}
|
|
146
|
+
if (!Array.isArray(config)) config = [];
|
|
147
|
+
const namesToRemove = new Set([normalizedParam, clientConfig.name].filter(Boolean));
|
|
148
|
+
config = config.filter(obj => obj && !namesToRemove.has(obj.name && obj.name.trim ? obj.name.trim() : obj.name));
|
|
149
|
+
config.push(clientConfig);
|
|
150
|
+
log.debug('new config size', config.length);
|
|
151
|
+
const tmp = `${clientConfigFile}.tmp.${process.pid}.${crypto.randomBytes(4).toString('hex')}`;
|
|
152
|
+
await fsPromises.writeFile(tmp, JSON.stringify(config, null, 2));
|
|
153
|
+
await fsPromises.rename(tmp, clientConfigFile);
|
|
154
|
+
return true;
|
|
132
155
|
});
|
|
133
|
-
config.push(clientConfig)
|
|
134
|
-
log.debug('new config', config);
|
|
135
|
-
fs.writeFileSync(clientConfigFile, JSON.stringify(config, null, 2));
|
|
136
|
-
return true
|
|
137
156
|
}
|
|
138
157
|
|
|
139
|
-
function deleteClient(clientName) {
|
|
158
|
+
async function deleteClient(clientName) {
|
|
140
159
|
log.debug('start to update config in file', clientConfigFile);
|
|
141
160
|
log.debug('delete client data', clientName);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
161
|
+
const normalizedName = typeof clientName === 'string' ? clientName.trim() : clientName;
|
|
162
|
+
return clientConfigWriteLimit(async () => {
|
|
163
|
+
let config;
|
|
164
|
+
try {
|
|
165
|
+
const raw = await fsPromises.readFile(clientConfigFile, 'utf8');
|
|
166
|
+
config = JSON.parse(raw);
|
|
167
|
+
} catch (e) {
|
|
168
|
+
log.warn('deleteClient: cannot parse existing clients.json, nothing to delete', e.message);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (!Array.isArray(config)) return false;
|
|
172
|
+
const before = config.length;
|
|
173
|
+
config = config.filter(obj => {
|
|
174
|
+
if (!obj || !obj.name) return true;
|
|
175
|
+
const objName = typeof obj.name === 'string' ? obj.name.trim() : obj.name;
|
|
176
|
+
return objName !== normalizedName;
|
|
177
|
+
});
|
|
178
|
+
const removed = before - config.length;
|
|
179
|
+
if (removed === 0) {
|
|
180
|
+
log.warn('deleteClient: no entry matched', clientName);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
const tmp = `${clientConfigFile}.tmp.${process.pid}.${crypto.randomBytes(4).toString('hex')}`;
|
|
184
|
+
await fsPromises.writeFile(tmp, JSON.stringify(config, null, 2));
|
|
185
|
+
await fsPromises.rename(tmp, clientConfigFile);
|
|
186
|
+
log.debug('deleteClient: removed', removed, 'entries for', normalizedName);
|
|
187
|
+
return true;
|
|
145
188
|
});
|
|
146
|
-
log.debug('new config', config);
|
|
147
|
-
fs.writeFileSync(clientConfigFile, JSON.stringify(config, null, 2));
|
|
148
|
-
return true
|
|
149
189
|
}
|
|
150
190
|
|
|
151
191
|
function getConstants(objectType) {
|
|
@@ -243,7 +283,7 @@ async function checkCustomFormMapping() {
|
|
|
243
283
|
}
|
|
244
284
|
}
|
|
245
285
|
|
|
246
|
-
function checkClientConfig(client) {
|
|
286
|
+
async function checkClientConfig(client) {
|
|
247
287
|
let updateRequired = false;
|
|
248
288
|
const clientKeys = [
|
|
249
289
|
'cmdbobject',
|
|
@@ -361,25 +401,25 @@ function checkClientConfig(client) {
|
|
|
361
401
|
log.warn(`check config for client ${client.name} done. Update required.`);
|
|
362
402
|
client.config = clientConfig;
|
|
363
403
|
//save
|
|
364
|
-
setClient(client.name, client);
|
|
404
|
+
await setClient(client.name, client);
|
|
365
405
|
log.warn(`check config for client ${client.name} done. Update done.`);
|
|
366
406
|
}
|
|
367
407
|
log.info(`check config for client ${client.name} done. No update required.`);
|
|
368
408
|
}
|
|
369
409
|
|
|
370
|
-
function checkClientConfigs() {
|
|
410
|
+
async function checkClientConfigs() {
|
|
371
411
|
log.info('check client configs');
|
|
372
412
|
const clientConfigs = getClients();
|
|
373
|
-
|
|
374
|
-
checkClientConfig(clientConfig);
|
|
375
|
-
}
|
|
413
|
+
for (const clientConfig of clientConfigs) {
|
|
414
|
+
await checkClientConfig(clientConfig);
|
|
415
|
+
}
|
|
376
416
|
log.info('check client configs done');
|
|
377
417
|
}
|
|
378
418
|
|
|
379
419
|
async function checkConfig() {
|
|
380
420
|
log.info('check config');
|
|
381
421
|
checkMapping();
|
|
382
|
-
checkClientConfigs();
|
|
422
|
+
await checkClientConfigs();
|
|
383
423
|
await checkCustomFormMapping();
|
|
384
424
|
log.info('check config done');
|
|
385
425
|
}
|
|
@@ -434,7 +474,7 @@ async function getDesignPackage(clientId) {
|
|
|
434
474
|
const mapping = getDeprecatedMappingAsCustom(config.requestTypeWorkLog)
|
|
435
475
|
schemas[config.requestTypeWorkLog] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog)
|
|
436
476
|
schemas[config.requestTypeWorkLog + '_create_update'] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog, true)
|
|
437
|
-
paths = {...paths, ... await getPathDef(config.assocTicketType
|
|
477
|
+
paths = {...paths, ... await getPathDef(`${config.assocTicketType} Worklog`, config.baseURI + '/{ticketId}/worklogs', config.requestTypeWorkLog, true, true)}
|
|
438
478
|
paths = {...paths, ... await getAttachmentPathDef(config.assocTicketType, config.baseURI + '/{ticketId}/worklogs')}
|
|
439
479
|
}
|
|
440
480
|
if (config.requestTemplate && clientConfig[config.requestTemplate].fields && clientConfig[config.requestTemplate].fields.length > 0) {
|