@cap-js/asyncapi
Version:
CAP tool for AsyncAPI
174 lines (147 loc) • 5.16 kB
JavaScript
const { getEventName } = require('../../utils');
const annotationMapping = {
'EventSpecVersion': 'x-sap-event-spec-version',
'EventSource': 'x-sap-event-source',
'EventSourceParams': 'x-sap-event-source-parameters',
'EventCharacteristics': 'x-sap-event-characteristics',
'EventStateInfo': 'x-sap-stateInfo',
'EventSchemaVersion': 'x-sap-event-version'
};
/**
* Get header properties from definition.
*/
function getHeaderProperties(name) {
return { type: { const: name } };
}
function nestedAnnotation(resObj, asyncapiProperty, keys, value) {
if (resObj[asyncapiProperty] === undefined) {
resObj[asyncapiProperty] = {};
}
let node = resObj[asyncapiProperty];
// traverse the annotation property and define the objects if they're not defined
for (let nestedIndex = 1; nestedIndex < keys.length - 1; nestedIndex++) {
const nestedElement = keys[nestedIndex];
if (node[nestedElement] === undefined) {
node[nestedElement] = {};
}
node = node[nestedElement];
}
// set value annotation property
node[keys[keys.length - 1]] = value;
}
function readAnnotations(definition, presets) {
let resObj = {};
for (const [key, value] of Object.entries(definition)) {
if (key.startsWith('@AsyncAPI') && !key.startsWith('@AsyncAPI.Extensions')) {
// get annotation value after @AsyncAPI
const annotationProperties = key.split('@AsyncAPI.')[1];
const keys = annotationProperties.split('.');
// convert annotation property (ex: EventSource) to asyncapi property (ex: x-sap-event-source)
// if annotation property isn't present in map then ignore the property
if (annotationMapping[keys[0]] === undefined) continue;
const asyncapiProperty = annotationMapping[keys[0]];
// if there's no nesting then just set the value
if (keys.length === 1) {
resObj[asyncapiProperty] = value;
} else {
nestedAnnotation(resObj, asyncapiProperty, keys, value)
}
}
else if (key.startsWith('@AsyncAPI.Extensions')) {
const annotationProperties = key.split('@AsyncAPI.Extensions.')[1];
const keys = annotationProperties.split('.');
keys[0] = "x-" + keys[0];
if (keys.length === 1) {
// if the key is already present, that means, Annotations have already been considered
if (resObj[keys[0]]) continue;
// else we populate
resObj[keys[0]] = value;
} else {
nestedAnnotation(resObj, keys[0], keys, value);
}
}
}
for (const [key, value] of Object.entries(presets)) {
if (resObj[key] === undefined) {
resObj[key] = value;
}
}
addDefaults(resObj);
return resObj;
}
function addDefaults(obj) {
const defaults = {
'x-sap-event-source': '/{region}/{applicationNamespace}/{instanceId}',
'x-sap-event-source-parameters': {
"region": {
"description": "The regional context of the application.",
"schema": {
"type": "string"
}
},
"applicationNamespace": {
"description": "The registered namespace of the application.",
"schema": {
"type": "string"
}
},
"instanceId": {
"description": "The instance id (tenant, installation, ...) of the application.",
"schema": {
"type": "string"
}
}
}
}
for (const [key, value] of Object.entries(defaults)) {
if (!obj[key]) {
obj[key] = value;
}
}
}
/**
* Get message for the event definition.
*/
function eventToMessage(definition, name, presets) {
let obj = readAnnotations(definition, presets);
let messageObj = {
name,
headers: {
type: 'object',
properties: getHeaderProperties(name)
},
payload: {
$ref: `#/components/schemas/${name}`
},
traits: [
{
$ref: '#/components/messageTraits/CloudEventsContext.v1'
}
]
}
const message = { ...obj, ...messageObj};
return message;
}
/**
* Event definitions of one CSN to messages.
*/
function entriesToMessages(definitions, events, presets) {
const messages = {};
events.forEach(event => {
let name = getEventName(event, definitions[event]);
messages[name] = eventToMessage(definitions[event], name, presets);
});
return messages;
}
/**
* Combining the definition objects from CSN into one schema
*/
function definitionsToMessages(definitions, events, presets) {
const messages = {};
Object.assign(messages, entriesToMessages(definitions, events, presets));
return messages;
}
module.exports = {
definitionsToMessages,
nestedAnnotation
}