@manyos/smileconnect-api 1.71.7 → 1.72.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/conf/clients.json CHANGED
@@ -2693,7 +2693,33 @@
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": ["loginid"]
2710
+ },
2711
+ "responseSchema": {
2712
+ "type": "object",
2713
+ "properties": {
2714
+ "approver": {
2715
+ "type": "string",
2716
+ "description": "Approver username"
2717
+ }
2718
+ },
2719
+ "required": ["approver"]
2720
+ }
2721
+ }
2722
+ },
2697
2723
  "scripts": [
2698
2724
  "script1_env",
2699
2725
  "script1"
@@ -198,6 +198,7 @@ function queryCMDBObject(clientConfig, query, customFields, customOptions, inclu
198
198
  async function handleCMDBObjectResult(cmdbObject, mapping, clientConfig, includeArray, globalRelationObjects, deleteFields, globalScriptParams) {
199
199
  const internalReconId = cmdbObject['Reconciliation Identity'];
200
200
  const internalId = cmdbObject['Instance Id'];
201
+ const classId = cmdbObject['Class Id'];
201
202
 
202
203
  deleteFields.forEach(internalRequiredField => {
203
204
  delete cmdbObject[internalRequiredField];
package/docs/releases.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Release Notes
2
2
 
3
3
  ## API
4
+ ### 1.72.0 - 13.11.25
5
+ Improve OpenAPI Documentation generation. Add requestBody for POST and put requests. Allow custom definition for scriptendpoints.
6
+
7
+ ### 1.71.8 - 22.10.25
8
+ Fix error when include classAttributes was used in CI Search
9
+
4
10
  ### 1.71.7 - 10.07.25
5
11
  Return 404 if ticketWorklog is not found
6
12
  Return 404 if task is not found
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manyos/smileconnect-api",
3
- "version": "1.71.7",
3
+ "version": "1.72.0",
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
 
@@ -396,11 +398,13 @@ async function getDesignPackage(clientId) {
396
398
  if (clientObjectConfig.fields && clientObjectConfig.fields.length > 0){
397
399
  const mapping = getDeprecatedMappingAsCustom(config.requestType)
398
400
  schemas[config.requestType] = await getObjectSchema(clientObjectConfig.fields, mapping, config.forms.regular)
401
+ schemas[config.requestType+'_create_update'] = await getObjectSchema(clientObjectConfig.fields, mapping, config.forms.regular, true)
399
402
  paths = {...paths, ... await getPathDef(config.assocTicketType, config.baseURI, config.requestType)}
400
403
  }
401
404
  if (config.requestTypeWorkLog && clientConfig[config.requestTypeWorkLog].fields && clientConfig[config.requestTypeWorkLog].fields.length > 0) {
402
405
  const mapping = getDeprecatedMappingAsCustom(config.requestTypeWorkLog)
403
406
  schemas[config.requestTypeWorkLog] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog)
407
+ schemas[config.requestTypeWorkLog + '_create_update'] = await getObjectSchema(clientConfig[config.requestTypeWorkLog].fields, mapping, config.forms.workLog, true)
404
408
  paths = {...paths, ... await getPathDef(config.assocTicketType, config.baseURI + '/{ticketId}/worklogs', config.requestTypeWorkLog, true, true)}
405
409
  }
406
410
  if (config.requestTemplate && clientConfig[config.requestTemplate].fields && clientConfig[config.requestTemplate].fields.length > 0) {
@@ -454,7 +458,8 @@ async function getDesignPackage(clientId) {
454
458
  const clientObjectConfig = clientConfig[`custom_${formName}`]
455
459
  log.debug('key', key, clientObjectConfig)
456
460
  if (clientObjectConfig) {
457
- schemas[key] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName)
461
+ schemas[key] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName);
462
+ schemas[key+'_create_update'] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName, true);
458
463
  paths = {...paths, ... await getPathDef(key, '/v1/customForms/' + key, key)}
459
464
  }
460
465
  }
@@ -465,18 +470,21 @@ async function getDesignPackage(clientId) {
465
470
  const scriptKeys = Object.keys(clientConfig.scriptEndpoints)
466
471
  for (let x=0; x < scriptKeys.length; x++) {
467
472
  const key = scriptKeys[x]
473
+ const endpointConfig = clientConfig.scriptEndpoints[key];
474
+ const openAPISpec = endpointConfig.options.openAPISpec;
468
475
  log.debug('key', key)
469
- //schemas[key] = await getObjectSchema(clientObjectConfig.fields, customFormMappings[key].mapping, formName)
470
476
  const pathDef = {}
471
477
  pathDef[`/v1/scriptEndpoints/${key}`] = {};
472
478
  pathDef[`/v1/scriptEndpoints/${key}`].post = {
473
479
  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.",
480
+ summary: openAPISpec.summary || `Run scripts associated with this endpoint`,
481
+ description: openAPISpec.decription || "Run scripts associated that are associated with this endpoint. The return Schema is defined in the scripts.",
476
482
  parameters: [],
477
- responses: await getApiResponse("scriptEndpoint_" + key, true)
483
+ responses: await getApiResponse("scriptEndpoint_" + key, true),
484
+ requestBody: await getApiRequestBody("scriptEndpoint_" + key)
478
485
  }
479
- schemas["scriptEndpoint_" + key] = {};
486
+ schemas["scriptEndpoint_" + key] = openAPISpec.responseSchema || {};
487
+ schemas["scriptEndpoint_" + key + '_create_update'] = openAPISpec.requestSchema || {};
480
488
  paths = {...paths, ... pathDef}
481
489
  }
482
490
  }
@@ -486,6 +494,7 @@ async function getDesignPackage(clientId) {
486
494
 
487
495
  if (clientConfig.cmdbobject && clientConfig.cmdbobject.fields) {
488
496
  schemas['cmdbobject'] = await getObjectSchema(clientConfig.cmdbobject.fields, cmdbMapping, 'AST:BaseElement')
497
+ schemas['cmdbobject_create_update'] = await getObjectSchema(clientConfig.cmdbobject.fields, cmdbMapping, 'AST:BaseElement', true)
489
498
 
490
499
 
491
500
  const cmdbdef = Object.keys(clientConfig.cmdbobject)
@@ -505,6 +514,10 @@ async function getDesignPackage(clientId) {
505
514
  required: schemas['cmdbobject'].required.concat(classSchema.required),
506
515
  properties: {...schemas['cmdbobject'].properties, ...classSchema.properties}
507
516
  }
517
+ schemas[`${schemaName}_create_update`] = {
518
+ required: schemas['cmdbobject'].required.concat(classSchema.required),
519
+ properties: {...schemas['cmdbobject'].properties, ...classSchema.properties}
520
+ }
508
521
  log.debug('customForm', formName, fields, classMapping, classSchema)
509
522
  }
510
523
 
@@ -551,7 +564,7 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
551
564
  tags: tag,
552
565
  summary: `Get a single ${objectName} object by id`,
553
566
  description: "A single object is returned in the data attribute",
554
- parameters: [],
567
+ parameters: getOpenApiParameters('get'),
555
568
  responses: await getApiResponse(schema, true)
556
569
  }
557
570
  }
@@ -560,8 +573,9 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
560
573
  tags: tag,
561
574
  summary: `Update an object of type ${objectName}`,
562
575
  description: "A single object is returned in the data attribute",
563
- parameters: [],
564
- responses: await getApiResponse(schema, true)
576
+ parameters: getOpenApiParameters('put'),
577
+ responses: await getApiResponse(schema, true),
578
+ requestBody: await getApiRequestBody(schema)
565
579
  }
566
580
  }
567
581
 
@@ -570,7 +584,7 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
570
584
  tags: tag,
571
585
  summary: `Get a list of object of type ${objectName}`,
572
586
  description: "An array ob objects is returned in the data attribute",
573
- parameters: [],
587
+ parameters: getOpenApiParameters('getAll'),
574
588
  responses: await getApiResponse(schema, false)
575
589
  }
576
590
  }
@@ -579,8 +593,9 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
579
593
  tags: tag,
580
594
  summary: `Create a new object of type ${objectName}`,
581
595
  description: "A single object is returned in the data attribute",
582
- parameters: [],
583
- responses: await getApiResponse(schema, false)
596
+ parameters: getOpenApiParameters('post'),
597
+ responses: await getApiResponse(schema, false),
598
+ requestBody: await getApiRequestBody(schema)
584
599
  }
585
600
  }
586
601
  if (isSubTicket) {
@@ -589,6 +604,43 @@ async function getPathDef(objectName, baseUri, schema, isSubTicket, suppressUpda
589
604
  return pathDef
590
605
  }
591
606
 
607
+ function getOpenApiParameters(operation) {
608
+ const parameters = [
609
+ {
610
+ "examples": {
611
+ "sample": {
612
+ "value": "user123"
613
+ }
614
+ },
615
+ "name": "impersonateUser",
616
+ "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.",
617
+ "schema": {
618
+ "type": "string"
619
+ },
620
+ "in": "query",
621
+ "required": false
622
+ },
623
+ {
624
+ "examples": {
625
+ "include": {
626
+ "value": "ciRelations,ticketRelations"
627
+ }
628
+ },
629
+ "name": "include",
630
+ "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",
631
+ "schema": {
632
+ "type": "array",
633
+ "items": {
634
+ "type": "string"
635
+ }
636
+ },
637
+ "in": "query",
638
+ "required": false
639
+ }
640
+ ]
641
+ return parameters;
642
+ }
643
+
592
644
  async function getApiResponse(schema, single) {
593
645
  let dataValue = {
594
646
  "$ref": `#/components/schemas/${schema}`
@@ -628,7 +680,35 @@ async function getApiResponse(schema, single) {
628
680
  return responses
629
681
  }
630
682
 
631
- async function getObjectSchema(clientFields, mapping, formName) {
683
+ async function getApiRequestBody(schema) {
684
+ let dataValue = {
685
+ "$ref": `#/components/schemas/${schema}_create_update`
686
+ }
687
+ if (Array.isArray(schema)) {
688
+ dataValue = {
689
+ oneOf: schema.map(item => {
690
+ return {
691
+ "$ref": `#/components/schemas/${item}_create_update`
692
+ }
693
+ })
694
+ }
695
+ }
696
+ return requestBody = {
697
+ content: {
698
+ "application/json": {
699
+ schema: {
700
+ type: "object",
701
+ properties: {
702
+ data: dataValue
703
+ }
704
+ }
705
+ }
706
+ },
707
+ required: true
708
+ }
709
+ }
710
+
711
+ async function getObjectSchema(clientFields, mapping, formName, isCreateUpdate = false) {
632
712
  const fields = await getFields(formName)
633
713
  const required = []
634
714
  const properties = {}
@@ -638,37 +718,40 @@ async function getObjectSchema(clientFields, mapping, formName) {
638
718
  const fieldDef = {}
639
719
  const fieldDetails = fields.find(item => item.name === field)
640
720
 
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
721
+ // remove id field from create/update schema
722
+ if (!isCreateUpdate || mappedName !== 'id') {
723
+ if (fieldDetails) {
724
+ fieldDef.type = fieldDetails.type
725
+ if (fieldDetails.entryMode === 'Required') {
726
+ required.push(mappedName)
657
727
  }
728
+ if(fieldDetails.fieldLimit) {
729
+ fieldDef.maxLength = fieldDetails.fieldLimit.maxLength
730
+ }
731
+ if (fieldDetails.type === 'SelectionField') {
732
+ fieldDef.type = 'string'
733
+ if (fieldDetails.fieldLimit && fieldDetails.fieldLimit.values) {
734
+ const enumVals = fieldDetails.fieldLimit.values.map(item => item.enumItemName)
735
+ fieldDef.enum = enumVals
736
+ } else if (fieldDetails.fieldLimit && fieldDetails.fieldLimit.valueMapping) {
737
+ const enumVals = Object.values(fieldDetails.fieldLimit.valueMapping)
738
+ fieldDef.enum = enumVals
739
+ }
658
740
 
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'
741
+ } else if (fieldDetails.type === 'CharacterField') {
742
+ fieldDef.type = 'string'
743
+ } else if (fieldDetails.type === 'DateTimeField') {
744
+ fieldDef.type = 'string'
745
+ fieldDef.format = 'date-time'
746
+ } else if (fieldDetails.type === 'DateField') {
747
+ fieldDef.format = 'date'
748
+ } else if (fieldDetails.type === 'DecimalField') {
749
+ fieldDef.type = 'number'
750
+ }
751
+ //fieldDef.details = fieldDetails
668
752
  }
669
- //fieldDef.details = fieldDetails
753
+ properties[mappedName] = fieldDef
670
754
  }
671
- properties[mappedName] = fieldDef
672
755
  })
673
756
  //mappedFields.push(fields)
674
757
  return {required, properties}