@manyos/smileconnect-api 1.72.4 → 1.73.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/docs/releases.md +4 -1
- package/package.json +3 -1
- package/test/incidentTest.js +13 -0
- package/test/testUtils.js +1 -1
- package/util/arquery.js +15 -2
- package/util/config.js +90 -1
- package/util/responsehandler.js +5 -0
package/docs/releases.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
3
|
## API
|
|
4
|
-
### 1.
|
|
4
|
+
### 1.73.0 - 28.04.26
|
|
5
|
+
Add detectMime flag to attachments & attachments to dynamic openapi spec.
|
|
6
|
+
|
|
7
|
+
### 1.72.4 - 18.03.26
|
|
5
8
|
Improve connection handling on OAUTH2 Adapater & LDAP Adapter
|
|
6
9
|
|
|
7
10
|
### 1.72.2 - 14.11.25
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manyos/smileconnect-api",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.73.0",
|
|
4
4
|
"description": "A proxy and abstraction layer for BMCs IT Service Management Suite",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"scripts": {
|
|
@@ -27,8 +27,10 @@
|
|
|
27
27
|
"express-request-id": "^1.4.1",
|
|
28
28
|
"express-validator": "^6.10.1",
|
|
29
29
|
"fast-xml-parser": "^3.20.3",
|
|
30
|
+
"file-type": "^16.5.4",
|
|
30
31
|
"https-proxy-agent": "^5.0.1",
|
|
31
32
|
"jsonwebtoken": "^9.0.2",
|
|
33
|
+
"mime-types": "^2.1.35",
|
|
32
34
|
"moment": "^2.29.1",
|
|
33
35
|
"mongoose": "^5.12.5",
|
|
34
36
|
"node-cache": "^4.2.1",
|
package/test/incidentTest.js
CHANGED
|
@@ -362,6 +362,19 @@ describe('Integration Tests - Incidents', function () {
|
|
|
362
362
|
})
|
|
363
363
|
})
|
|
364
364
|
|
|
365
|
+
it('it should detect image/png when detectMime=true', function (done) {
|
|
366
|
+
this.timeout(5000);
|
|
367
|
+
chai.request(server)
|
|
368
|
+
.get(attachmentUrl + '?detectMime=true')
|
|
369
|
+
.set('Authorization', 'Bearer ' + authUser.access_token)
|
|
370
|
+
.end(function (err, res) {
|
|
371
|
+
res.should.have.status(200);
|
|
372
|
+
res.should.have.header('content-type', 'image/png');
|
|
373
|
+
res.should.have.header('content-disposition', 'attachment; filename=logo.png');
|
|
374
|
+
done();
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
|
|
365
378
|
|
|
366
379
|
it ('it should get all incident worklogs', function (done) {
|
|
367
380
|
this.timeout(5000);
|
package/test/testUtils.js
CHANGED
|
@@ -4,7 +4,7 @@ const expect = chai.expect;
|
|
|
4
4
|
this.authenticateUser = (user, app) =>
|
|
5
5
|
new Promise((resolve, reject) => {
|
|
6
6
|
chai.request('https://sso.manyos.it')
|
|
7
|
-
.post('/
|
|
7
|
+
.post('/realms/itsmproxy/protocol/openid-connect/token')
|
|
8
8
|
.auth(user.id, user.secret)
|
|
9
9
|
.type('form')
|
|
10
10
|
.send({
|
package/util/arquery.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require('dotenv').config();
|
|
2
2
|
const request = require('request-promise-native');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const mime = require('mime-types');
|
|
5
|
+
const FileType = require('file-type');
|
|
4
6
|
const {getEventLog} = require("./eventLog");
|
|
5
7
|
let log = require('@manyos/logger').setupLog('SMILEconnect', path.basename(__filename));
|
|
6
8
|
|
|
@@ -315,13 +317,24 @@ function getAttachment(form, entryId, attachmentfield) {
|
|
|
315
317
|
|
|
316
318
|
log.debug('start attachment request on:', uri);
|
|
317
319
|
request(options).auth(process.env.AR_USER, process.env.AR_PASSWORD, true)
|
|
318
|
-
.then(function (response) {
|
|
320
|
+
.then(async function (response) {
|
|
319
321
|
log.debug('RAPI return', response.headers);
|
|
320
322
|
const fileName = response.headers['content-disposition'].split(';')[1].split('=')[1];
|
|
321
323
|
log.debug('filename', fileName);
|
|
324
|
+
const fromExt = mime.lookup(fileName);
|
|
325
|
+
let fromMagic = null;
|
|
326
|
+
if (!fromExt) {
|
|
327
|
+
try {
|
|
328
|
+
fromMagic = (await FileType.fromBuffer(response.body))?.mime;
|
|
329
|
+
} catch (err) {
|
|
330
|
+
log.debug('file-type detection failed, falling back to octet-stream', err.message);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const contentType = fromExt || fromMagic || 'application/octet-stream';
|
|
322
334
|
const file = {
|
|
323
335
|
data : response.body,
|
|
324
|
-
fileName : fileName
|
|
336
|
+
fileName : fileName,
|
|
337
|
+
contentType : contentType
|
|
325
338
|
}
|
|
326
339
|
resolve(file);
|
|
327
340
|
})
|
package/util/config.js
CHANGED
|
@@ -435,6 +435,7 @@ async function getDesignPackage(clientId) {
|
|
|
435
435
|
schemas[config.requestTypeWorkLog] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog)
|
|
436
436
|
schemas[config.requestTypeWorkLog + '_create_update'] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog, true)
|
|
437
437
|
paths = {...paths, ... await getPathDef(config.assocTicketType, config.baseURI + '/{ticketId}/worklogs', config.requestTypeWorkLog, true, true)}
|
|
438
|
+
paths = {...paths, ... await getAttachmentPathDef(config.assocTicketType, config.baseURI + '/{ticketId}/worklogs')}
|
|
438
439
|
}
|
|
439
440
|
if (config.requestTemplate && clientConfig[config.requestTemplate].fields && clientConfig[config.requestTemplate].fields.length > 0) {
|
|
440
441
|
const mapping = getDeprecatedMappingAsCustom(config.requestTemplate)
|
|
@@ -552,7 +553,25 @@ async function getDesignPackage(clientId) {
|
|
|
552
553
|
paths = {...paths, ... await getPathDef('cmdbobject', '/v1/cmdbobjects', cmdbSchemaList)}
|
|
553
554
|
}
|
|
554
555
|
|
|
555
|
-
|
|
556
|
+
const attachmentResponse = {
|
|
557
|
+
description: "Attachment binary content. Content-Type is application/octet-stream by default; with ?detectMime=true it reflects the detected MIME type (extension lookup with magic-byte fallback).",
|
|
558
|
+
headers: {
|
|
559
|
+
"Content-Disposition": {
|
|
560
|
+
description: "attachment; filename=<filename>",
|
|
561
|
+
schema: { type: "string" }
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
content: {
|
|
565
|
+
"application/octet-stream": { schema: { type: "string", format: "binary" } },
|
|
566
|
+
"application/pdf": { schema: { type: "string", format: "binary" } },
|
|
567
|
+
"image/png": { schema: { type: "string", format: "binary" } },
|
|
568
|
+
"image/jpeg": { schema: { type: "string", format: "binary" } },
|
|
569
|
+
"image/gif": { schema: { type: "string", format: "binary" } },
|
|
570
|
+
"text/plain": { schema: { type: "string", format: "binary" } }
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return {openapi: "3.0.1", info, servers, paths, components: {schemas, responses: {attachmentResponse}}}
|
|
556
575
|
}
|
|
557
576
|
|
|
558
577
|
async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpdate, suppressCreate) {
|
|
@@ -632,6 +651,76 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
|
|
|
632
651
|
return pathDef
|
|
633
652
|
}
|
|
634
653
|
|
|
654
|
+
async function getAttachmentPathDef(objectName, workLogBaseUri) {
|
|
655
|
+
const ticketIdParam = {
|
|
656
|
+
name: "ticketId",
|
|
657
|
+
description: "id of the parent ticket",
|
|
658
|
+
schema: { "type": "string" },
|
|
659
|
+
in: "path",
|
|
660
|
+
required: true
|
|
661
|
+
}
|
|
662
|
+
const worklogIdParam = {
|
|
663
|
+
name: "worklogId",
|
|
664
|
+
description: "id of the worklog the attachment belongs to",
|
|
665
|
+
schema: { "type": "string" },
|
|
666
|
+
in: "path",
|
|
667
|
+
required: true
|
|
668
|
+
}
|
|
669
|
+
const detectMimeParam = {
|
|
670
|
+
name: "detectMime",
|
|
671
|
+
description: "If set to 'true', the response Content-Type reflects the detected MIME type of the attachment (filename extension lookup with magic-byte fallback). Without the parameter the response keeps the legacy default 'application/octet-stream' for backwards compatibility.",
|
|
672
|
+
schema: {
|
|
673
|
+
type: "string",
|
|
674
|
+
enum: ["true", "false"]
|
|
675
|
+
},
|
|
676
|
+
in: "query",
|
|
677
|
+
required: false,
|
|
678
|
+
examples: {
|
|
679
|
+
detect: { value: "true" }
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const pathDef = {}
|
|
684
|
+
for (const slot of [1, 2, 3]) {
|
|
685
|
+
const uri = `${workLogBaseUri}/{worklogId}/attachments/${slot}`
|
|
686
|
+
pathDef[uri] = {
|
|
687
|
+
parameters: [ticketIdParam, worklogIdParam],
|
|
688
|
+
get: {
|
|
689
|
+
tags: [objectName, 'Attachments'],
|
|
690
|
+
summary: `Download attachment ${slot} of a ${objectName} worklog`,
|
|
691
|
+
description: "Returns the binary attachment. Default Content-Type is application/octet-stream; pass ?detectMime=true to receive the detected MIME type.",
|
|
692
|
+
parameters: [detectMimeParam],
|
|
693
|
+
responses: {
|
|
694
|
+
"200": { "$ref": "#/components/responses/attachmentResponse" }
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
post: {
|
|
698
|
+
tags: [objectName, 'Attachments'],
|
|
699
|
+
summary: `Upload attachment ${slot} for a ${objectName} worklog`,
|
|
700
|
+
description: 'Send the file in the form field "file".',
|
|
701
|
+
requestBody: {
|
|
702
|
+
required: true,
|
|
703
|
+
content: {
|
|
704
|
+
"multipart/form-data": {
|
|
705
|
+
schema: {
|
|
706
|
+
type: "object",
|
|
707
|
+
properties: {
|
|
708
|
+
file: { type: "string", format: "binary" }
|
|
709
|
+
},
|
|
710
|
+
required: ["file"]
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
responses: {
|
|
716
|
+
"200": { description: "Attachment added" }
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return pathDef
|
|
722
|
+
}
|
|
723
|
+
|
|
635
724
|
function getOpenApiParameters(operation) {
|
|
636
725
|
const parameters = [
|
|
637
726
|
{
|
package/util/responsehandler.js
CHANGED
|
@@ -103,6 +103,11 @@ function eventQueueHandler(req, res, next) {
|
|
|
103
103
|
req.log
|
|
104
104
|
).then( success => {
|
|
105
105
|
if (req.downloadResult) {
|
|
106
|
+
const detect = req.query && req.query.detectMime === 'true';
|
|
107
|
+
const contentType = detect
|
|
108
|
+
? (req.downloadResult.contentType || 'application/octet-stream')
|
|
109
|
+
: 'application/octet-stream';
|
|
110
|
+
res.set('Content-Type', contentType);
|
|
106
111
|
res.set('Content-disposition', 'attachment; filename=' + req.downloadResult.fileName);
|
|
107
112
|
res.status(200).send(req.downloadResult.data);
|
|
108
113
|
} else {
|