@manyos/smileconnect-api 1.71.8 → 1.72.1

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/conf/clients.json CHANGED
@@ -2693,7 +2693,38 @@
2693
2693
  },
2694
2694
  "scriptEndpoints": {
2695
2695
  "personData": {
2696
- "options": {},
2696
+ "options": {
2697
+ "openAPISpec": {
2698
+ "summary": "Bla bla",
2699
+ "decription": "Bla bla bla bla bla",
2700
+ "requestSchema": {
2701
+ "type": "object",
2702
+ "properties": {
2703
+ "loginid": {
2704
+ "type": "string",
2705
+ "maxLength": 15,
2706
+ "description": "Login ID"
2707
+ }
2708
+ },
2709
+ "required": [
2710
+ "loginid"
2711
+ ]
2712
+ },
2713
+ "responseSchema": {
2714
+ "type": "object",
2715
+ "properties": {
2716
+ "approver": {
2717
+ "type": "string",
2718
+ "description": "Approver username"
2719
+ }
2720
+ },
2721
+ "required": [
2722
+ "approver"
2723
+ ]
2724
+ },
2725
+ "description": "Run scripts associated with this endpoint"
2726
+ }
2727
+ },
2697
2728
  "scripts": [
2698
2729
  "script1_env",
2699
2730
  "script1"
@@ -2702,4 +2733,4 @@
2702
2733
  }
2703
2734
  }
2704
2735
  }
2705
- ]
2736
+ ]
package/docs/releases.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Release Notes
2
2
 
3
3
  ## API
4
+ ### 1.72.1 - 13.11.25
5
+ Improve OpenAPI Documentation generation. Add requestBody for POST and put requests. Allow custom definition for scriptendpoints.
6
+
4
7
  ### 1.71.8 - 22.10.25
5
8
  Fix error when include classAttributes was used in CI Search
6
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manyos/smileconnect-api",
3
- "version": "1.71.8",
3
+ "version": "1.72.1",
4
4
  "description": "A proxy and abstraction layer for BMCs IT Service Management Suite",
5
5
  "main": "app.js",
6
6
  "scripts": {
package/util/config.js CHANGED
@@ -11,6 +11,8 @@ const mappingUtil = require('../util/mappingUtil');
11
11
  const CacheService = require ('../util/cache.service');
12
12
 
13
13
  const CONSTANTS = require('./constants');
14
+ const {response} = require("express");
15
+ const {get} = require("mongoose");
14
16
 
15
17
  const configFileCustomFormMapping = 'conf/customFormMapping.json';
16
18
 
@@ -321,6 +323,35 @@ function checkClientConfig(client) {
321
323
  updateRequired = true;
322
324
  }
323
325
 
326
+ const scriptEnpointKeys = Object.keys(clientConfig.scriptEndpoints);
327
+ scriptEnpointKeys.forEach(scriptEnpointKey => {
328
+ const scriptEnpointConfig = clientConfig.scriptEndpoints[scriptEnpointKey];
329
+ if (!scriptEnpointConfig.options) {
330
+ scriptEnpointConfig.options = {};
331
+ updateRequired = true;
332
+ }
333
+ if (!scriptEnpointConfig.options.openAPISpec) {
334
+ scriptEnpointConfig.options.openAPISpec = {};
335
+ updateRequired = true;
336
+ }
337
+ if (!scriptEnpointConfig.options.openAPISpec.summary) {
338
+ scriptEnpointConfig.options.openAPISpec.summary = "Run scripts associated with this endpoint";
339
+ updateRequired = true;
340
+ }
341
+ if (!scriptEnpointConfig.options.openAPISpec.description) {
342
+ scriptEnpointConfig.options.openAPISpec.description = "Run scripts associated with this endpoint";
343
+ updateRequired = true;
344
+ }
345
+ if (!scriptEnpointConfig.options.openAPISpec.requestSchema) {
346
+ scriptEnpointConfig.options.openAPISpec.requestSchema = {};
347
+ updateRequired = true;
348
+ }
349
+ if (!scriptEnpointConfig.options.openAPISpec.responseSchema) {
350
+ scriptEnpointConfig.options.openAPISpec.responseSchema = {};
351
+ updateRequired = true;
352
+ }
353
+ });
354
+
324
355
  if (clientConfig.options.translateSelectionFields === undefined) {
325
356
  clientConfig.options.translateSelectionFields = true;
326
357
  updateRequired = true;
@@ -396,11 +427,13 @@ async function getDesignPackage(clientId) {
396
427
  if (clientObjectConfig.fields && clientObjectConfig.fields.length > 0){
397
428
  const mapping = getDeprecatedMappingAsCustom(config.requestType)
398
429
  schemas[config.requestType] = await getObjectSchema(clientObjectConfig.fields, mapping, config.forms.regular)
430
+ schemas[config.requestType+'_create_update'] = await getObjectSchema(clientObjectConfig.fields, mapping, config.forms.regular, true)
399
431
  paths = {...paths, ... await getPathDef(config.assocTicketType, config.baseURI, config.requestType)}
400
432
  }
401
433
  if (config.requestTypeWorkLog && clientConfig[config.requestTypeWorkLog].fields && clientConfig[config.requestTypeWorkLog].fields.length > 0) {
402
434
  const mapping = getDeprecatedMappingAsCustom(config.requestTypeWorkLog)
403
435
  schemas[config.requestTypeWorkLog] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog)
436
+ schemas[config.requestTypeWorkLog + '_create_update'] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog, true)
404
437
  paths = {...paths, ... await getPathDef(config.assocTicketType, config.baseURI + '/{ticketId}/worklogs', config.requestTypeWorkLog, true, true)}
405
438
  }
406
439
  if (config.requestTemplate && clientConfig[config.requestTemplate].fields && clientConfig[config.requestTemplate].fields.length > 0) {
@@ -454,7 +487,8 @@ async function getDesignPackage(clientId) {
454
487
  const clientObjectConfig = clientConfig[`custom_${formName}`]
455
488
  log.debug('key', key, clientObjectConfig)
456
489
  if (clientObjectConfig) {
457
- schemas[key] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName)
490
+ schemas[key] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName);
491
+ schemas[key+'_create_update'] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName, true);
458
492
  paths = {...paths, ... await getPathDef(key, '/v1/customForms/' + key, key)}
459
493
  }
460
494
  }
@@ -465,18 +499,21 @@ async function getDesignPackage(clientId) {
465
499
  const scriptKeys = Object.keys(clientConfig.scriptEndpoints)
466
500
  for (let x=0; x < scriptKeys.length; x++) {
467
501
  const key = scriptKeys[x]
502
+ const endpointConfig = clientConfig.scriptEndpoints[key];
503
+ const openAPISpec = endpointConfig.options.openAPISpec;
468
504
  log.debug('key', key)
469
- //schemas[key] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName)
470
505
  const pathDef = {}
471
506
  pathDef[`/v1/scriptEndpoints/${key}`] = {};
472
507
  pathDef[`/v1/scriptEndpoints/${key}`].post = {
473
508
  tags: [key],
474
- summary: `Run scripts associated with this endpoint`,
475
- description: "Run scripts associated that are associated with this endpoint. The return Schema is defined in the scripts.",
509
+ summary: openAPISpec.summary || `Run scripts associated with this endpoint`,
510
+ description: openAPISpec.decription || "Run scripts associated that are associated with this endpoint. The return Schema is defined in the scripts.",
476
511
  parameters: [],
477
- responses: await getApiResponse("scriptEndpoint_" + key, true)
512
+ responses: await getApiResponse("scriptEndpoint_" + key, true),
513
+ requestBody: await getApiRequestBody("scriptEndpoint_" + key)
478
514
  }
479
- schemas["scriptEndpoint_" + key] = {};
515
+ schemas["scriptEndpoint_" + key] = openAPISpec.responseSchema || {};
516
+ schemas["scriptEndpoint_" + key + '_create_update'] = openAPISpec.requestSchema || {};
480
517
  paths = {...paths, ... pathDef}
481
518
  }
482
519
  }
@@ -486,6 +523,7 @@ async function getDesignPackage(clientId) {
486
523
 
487
524
  if (clientConfig.cmdbobject && clientConfig.cmdbobject.fields) {
488
525
  schemas['cmdbobject'] = await getObjectSchema(clientConfig.cmdbobject.fields, cmdbMapping, 'AST:BaseElement')
526
+ schemas['cmdbobject_create_update'] = await getObjectSchema(clientConfig.cmdbobject.fields, cmdbMapping, 'AST:BaseElement', true)
489
527
 
490
528
 
491
529
  const cmdbdef = Object.keys(clientConfig.cmdbobject)
@@ -505,6 +543,10 @@ async function getDesignPackage(clientId) {
505
543
  required: schemas['cmdbobject'].required.concat(classSchema.required),
506
544
  properties: {...schemas['cmdbobject'].properties, ...classSchema.properties}
507
545
  }
546
+ schemas[`${schemaName}_create_update`] = {
547
+ required: schemas['cmdbobject'].required.concat(classSchema.required),
548
+ properties: {...schemas['cmdbobject'].properties, ...classSchema.properties}
549
+ }
508
550
  log.debug('customForm', formName, fields, classMapping, classSchema)
509
551
  }
510
552
 
@@ -551,7 +593,7 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
551
593
  tags: tag,
552
594
  summary: `Get a single ${objectName} object by id`,
553
595
  description: "A single object is returned in the data attribute",
554
- parameters: [],
596
+ parameters: getOpenApiParameters('get'),
555
597
  responses: await getApiResponse(schema, true)
556
598
  }
557
599
  }
@@ -560,8 +602,9 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
560
602
  tags: tag,
561
603
  summary: `Update an object of type ${objectName}`,
562
604
  description: "A single object is returned in the data attribute",
563
- parameters: [],
564
- responses: await getApiResponse(schema, true)
605
+ parameters: getOpenApiParameters('put'),
606
+ responses: await getApiResponse(schema, true),
607
+ requestBody: await getApiRequestBody(schema)
565
608
  }
566
609
  }
567
610
 
@@ -570,7 +613,7 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
570
613
  tags: tag,
571
614
  summary: `Get a list of object of type ${objectName}`,
572
615
  description: "An array ob objects is returned in the data attribute",
573
- parameters: [],
616
+ parameters: getOpenApiParameters('getAll'),
574
617
  responses: await getApiResponse(schema, false)
575
618
  }
576
619
  }
@@ -579,8 +622,9 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
579
622
  tags: tag,
580
623
  summary: `Create a new object of type ${objectName}`,
581
624
  description: "A single object is returned in the data attribute",
582
- parameters: [],
583
- responses: await getApiResponse(schema, false)
625
+ parameters: getOpenApiParameters('post'),
626
+ responses: await getApiResponse(schema, false),
627
+ requestBody: await getApiRequestBody(schema)
584
628
  }
585
629
  }
586
630
  if (isSubTicket) {
@@ -589,6 +633,43 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
589
633
  return pathDef
590
634
  }
591
635
 
636
+ function getOpenApiParameters(operation) {
637
+ const parameters = [
638
+ {
639
+ "examples": {
640
+ "sample": {
641
+ "value": "user123"
642
+ }
643
+ },
644
+ "name": "impersonateUser",
645
+ "description": "If the clientConfig has the option allowDynamicImpersonate set to *true* then the URL Parameter *impersonateUser* can be used to determine the used Remedy User.",
646
+ "schema": {
647
+ "type": "string"
648
+ },
649
+ "in": "query",
650
+ "required": false
651
+ },
652
+ {
653
+ "examples": {
654
+ "include": {
655
+ "value": "ciRelations,ticketRelations"
656
+ }
657
+ },
658
+ "name": "include",
659
+ "description": "Defines if and which related objects shoudl be included.\nPossible values:\nciRelations,ticketRelations,personRelations,organisationRelations,supportGroupRelations,persons,supportGroups,organisations,cmdbObjects,incidents,workOrders,changes, problems",
660
+ "schema": {
661
+ "type": "array",
662
+ "items": {
663
+ "type": "string"
664
+ }
665
+ },
666
+ "in": "query",
667
+ "required": false
668
+ }
669
+ ]
670
+ return parameters;
671
+ }
672
+
592
673
  async function getApiResponse(schema, single) {
593
674
  let dataValue = {
594
675
  "$ref": `#/components/schemas/${schema}`
@@ -628,7 +709,35 @@ async function getApiResponse(schema, single) {
628
709
  return responses
629
710
  }
630
711
 
631
- async function getObjectSchema(clientFields, mapping, formName) {
712
+ async function getApiRequestBody(schema) {
713
+ let dataValue = {
714
+ "$ref": `#/components/schemas/${schema}_create_update`
715
+ }
716
+ if (Array.isArray(schema)) {
717
+ dataValue = {
718
+ oneOf: schema.map(item => {
719
+ return {
720
+ "$ref": `#/components/schemas/${item}_create_update`
721
+ }
722
+ })
723
+ }
724
+ }
725
+ return requestBody = {
726
+ content: {
727
+ "application/json": {
728
+ schema: {
729
+ type: "object",
730
+ properties: {
731
+ data: dataValue
732
+ }
733
+ }
734
+ }
735
+ },
736
+ required: true
737
+ }
738
+ }
739
+
740
+ async function getObjectSchema(clientFields, mapping, formName, isCreateUpdate = false) {
632
741
  const fields = await getFields(formName)
633
742
  const required = []
634
743
  const properties = {}
@@ -638,37 +747,40 @@ async function getObjectSchema(clientFields, mapping, formName) {
638
747
  const fieldDef = {}
639
748
  const fieldDetails = fields.find(item => item.name === field)
640
749
 
641
- if (fieldDetails) {
642
- fieldDef.type = fieldDetails.type
643
- if (fieldDetails.entryMode === 'Required') {
644
- required.push(mappedName)
645
- }
646
- if(fieldDetails.fieldLimit) {
647
- fieldDef.maxLength = fieldDetails.fieldLimit.maxLength
648
- }
649
- if (fieldDetails.type === 'SelectionField') {
650
- fieldDef.type = 'string'
651
- if (fieldDetails.fieldLimit && fieldDetails.fieldLimit.values) {
652
- const enumVals = fieldDetails.fieldLimit.values.map(item => item.enumItemName)
653
- fieldDef.enum = enumVals
654
- } else if (fieldDetails.fieldLimit && fieldDetails.fieldLimit.valueMapping) {
655
- const enumVals = Object.values(fieldDetails.fieldLimit.valueMapping)
656
- fieldDef.enum = enumVals
750
+ // remove id field from create/update schema
751
+ if (!isCreateUpdate || mappedName !== 'id') {
752
+ if (fieldDetails) {
753
+ fieldDef.type = fieldDetails.type
754
+ if (fieldDetails.entryMode === 'Required') {
755
+ required.push(mappedName)
657
756
  }
757
+ if(fieldDetails.fieldLimit) {
758
+ fieldDef.maxLength = fieldDetails.fieldLimit.maxLength
759
+ }
760
+ if (fieldDetails.type === 'SelectionField') {
761
+ fieldDef.type = 'string'
762
+ if (fieldDetails.fieldLimit && fieldDetails.fieldLimit.values) {
763
+ const enumVals = fieldDetails.fieldLimit.values.map(item => item.enumItemName)
764
+ fieldDef.enum = enumVals
765
+ } else if (fieldDetails.fieldLimit && fieldDetails.fieldLimit.valueMapping) {
766
+ const enumVals = Object.values(fieldDetails.fieldLimit.valueMapping)
767
+ fieldDef.enum = enumVals
768
+ }
658
769
 
659
- } else if (fieldDetails.type === 'CharacterField') {
660
- fieldDef.type = 'string'
661
- } else if (fieldDetails.type === 'DateTimeField') {
662
- fieldDef.type = 'string'
663
- fieldDef.format = 'date-time'
664
- } else if (fieldDetails.type === 'DateField') {
665
- fieldDef.format = 'date'
666
- } else if (fieldDetails.type === 'DecimalField') {
667
- fieldDef.type = 'number'
770
+ } else if (fieldDetails.type === 'CharacterField') {
771
+ fieldDef.type = 'string'
772
+ } else if (fieldDetails.type === 'DateTimeField') {
773
+ fieldDef.type = 'string'
774
+ fieldDef.format = 'date-time'
775
+ } else if (fieldDetails.type === 'DateField') {
776
+ fieldDef.format = 'date'
777
+ } else if (fieldDetails.type === 'DecimalField') {
778
+ fieldDef.type = 'number'
779
+ }
780
+ //fieldDef.details = fieldDetails
668
781
  }
669
- //fieldDef.details = fieldDetails
782
+ properties[mappedName] = fieldDef
670
783
  }
671
- properties[mappedName] = fieldDef
672
784
  })
673
785
  //mappedFields.push(fields)
674
786
  return {required, properties}