Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@sap-ux/annotation-converter

Package Overview
Dependencies
Maintainers
3
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap-ux/annotation-converter - npm Package Compare versions

Comparing version 0.6.7 to 0.6.8

6

CHANGELOG.md
# @sap-ux/annotation-converter
## 0.6.8
### Patch Changes
- 6c325c5: We have improved the handling of aliases
## 0.6.7

@@ -4,0 +10,0 @@

222

dist/converter.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convert = void 0;
const VocabularyReferences_1 = require("@sap-ux/vocabularies-types/vocabularies/VocabularyReferences");
const utils_1 = require("./utils");
const VocabularyReferences_1 = require("@sap-ux/vocabularies-types/vocabularies/VocabularyReferences");
/**

@@ -125,3 +125,3 @@ * Symbol to extend an annotation with the reference to its target.

const thisElement = current.target;
if (segment === '' || segment === thisElement.fullyQualifiedName) {
if (segment === '' || converter.unalias(segment) === thisElement.fullyQualifiedName) {
return current;

@@ -175,3 +175,4 @@ }

}
const action = thisElement.actions[segment];
const actionName = (0, utils_1.substringBeforeFirst)(converter.unalias(segment), '(');
const action = thisElement.actions[actionName];
if (action) {

@@ -428,44 +429,26 @@ current.target = action;

function parseRecord(converter, currentTerm, currentTarget, currentProperty, currentSource, annotationRecord, currentFQN) {
var _a;
const annotationTerm = {
const record = {
$Type: parseRecordType(converter, currentTerm, currentTarget, currentProperty, annotationRecord),
fullyQualifiedName: currentFQN,
[ANNOTATION_TARGET]: currentTarget
[ANNOTATION_TARGET]: currentTarget,
__source: currentSource
};
for (const propertyValue of annotationRecord.propertyValues) {
(0, utils_1.lazy)(record, propertyValue.name, () => parseValue(converter, currentTarget, currentTerm, propertyValue.name, currentSource, propertyValue.value, `${currentFQN}/${propertyValue.name}`));
}
// annotations on the record
(0, utils_1.lazy)(annotationTerm, 'annotations', () => {
var _a;
// be graceful when resolving annotations on annotations: Sometimes they are referenced directly, sometimes they
// are part of the global annotations list
let annotations;
if (annotationRecord.annotations && annotationRecord.annotations.length > 0) {
annotations = annotationRecord.annotations;
}
else {
annotations = (_a = converter.rawAnnotationsPerTarget[currentFQN]) === null || _a === void 0 ? void 0 : _a.annotations;
}
annotations === null || annotations === void 0 ? void 0 : annotations.forEach((annotation) => {
annotation.target = currentFQN;
annotation.__source = currentSource;
annotation[ANNOTATION_TARGET] = currentTarget;
annotation.fullyQualifiedName = `${currentFQN}@${annotation.term}`;
});
return createAnnotationsObject(converter, annotationTerm, annotations !== null && annotations !== void 0 ? annotations : []);
});
const annotationContent = (_a = annotationRecord.propertyValues) === null || _a === void 0 ? void 0 : _a.reduce((annotationContent, propertyValue) => {
(0, utils_1.lazy)(annotationContent, propertyValue.name, () => parseValue(converter, currentTarget, currentTerm, propertyValue.name, currentSource, propertyValue.value, `${currentFQN}/${propertyValue.name}`));
return annotationContent;
}, annotationTerm);
if (isDataFieldWithForAction(annotationContent)) {
(0, utils_1.lazy)(annotationContent, 'ActionTarget', () => {
var _a, _b;
// try to resolve to a bound action of the annotation target
let actionTarget = (_a = currentTarget.actions) === null || _a === void 0 ? void 0 : _a[annotationContent.Action];
(0, utils_1.lazy)(record, 'annotations', resolveAnnotationsOnAnnotation(converter, annotationRecord, record));
if (isDataFieldWithForAction(record)) {
(0, utils_1.lazy)(record, 'ActionTarget', () => {
var _a, _b, _c;
const actionTargetFQN = converter.unalias((_a = record.Action) === null || _a === void 0 ? void 0 : _a.toString());
// (1) Bound action of the annotation target?
let actionTarget = (_b = currentTarget.actions) === null || _b === void 0 ? void 0 : _b[actionTargetFQN];
if (!actionTarget) {
// try to find a corresponding unbound action
actionTarget = (_b = converter.getConvertedActionImport(annotationContent.Action)) === null || _b === void 0 ? void 0 : _b.action;
// (2) ActionImport (= unbound action)?
actionTarget = (_c = converter.getConvertedActionImport(actionTargetFQN)) === null || _c === void 0 ? void 0 : _c.action;
}
if (!actionTarget) {
// try to find a corresponding bound (!) action
actionTarget = converter.getConvertedAction(annotationContent.Action);
// (3) Bound action of a different EntityType
actionTarget = converter.getConvertedAction(actionTargetFQN);
if (!(actionTarget === null || actionTarget === void 0 ? void 0 : actionTarget.isBound)) {

@@ -476,3 +459,3 @@ actionTarget = undefined;

if (!actionTarget) {
converter.logError(`Unable to resolve the action '${annotationContent.Action}' defined for '${annotationTerm.fullyQualifiedName}'`);
converter.logError(`${record.fullyQualifiedName}: Unable to resolve '${record.Action}' ('${actionTargetFQN}')`);
}

@@ -482,3 +465,3 @@ return actionTarget;

}
return annotationContent;
return record;
}

@@ -643,26 +626,7 @@ /**

const [vocAlias, vocTerm] = converter.splitTerm(rawAnnotation.term);
annotation.term = converter.unalias(`${vocAlias}.${vocTerm}`);
annotation.term = converter.unalias(`${vocAlias}.${vocTerm}`, VocabularyReferences_1.VocabularyReferences);
annotation.qualifier = rawAnnotation.qualifier;
annotation.__source = rawAnnotation.__source;
try {
(0, utils_1.lazy)(annotation, 'annotations', () => {
var _a;
const annotationFQN = annotation.fullyQualifiedName;
// be graceful when resolving annotations on annotations: Sometimes they are referenced directly, sometimes they
// are part of the global annotations list
let annotations;
if (rawAnnotation.annotations && rawAnnotation.annotations.length > 0) {
annotations = rawAnnotation.annotations;
}
else {
annotations = (_a = converter.rawAnnotationsPerTarget[annotationFQN]) === null || _a === void 0 ? void 0 : _a.annotations;
}
annotations === null || annotations === void 0 ? void 0 : annotations.forEach((rawSubAnnotation) => {
rawSubAnnotation.target = annotationFQN;
rawSubAnnotation.__source = annotation.__source;
rawSubAnnotation[ANNOTATION_TARGET] = target;
rawSubAnnotation.fullyQualifiedName = `${annotationFQN}@${rawSubAnnotation.term}`;
});
return createAnnotationsObject(converter, annotation, annotations !== null && annotations !== void 0 ? annotations : []);
});
(0, utils_1.lazy)(annotation, 'annotations', resolveAnnotationsOnAnnotation(converter, rawAnnotation, annotation));
}

@@ -674,60 +638,45 @@ catch (e) {

}
function getAnnotationFQN(currentTargetName, references, annotation) {
const annotationFQN = `${currentTargetName}@${(0, utils_1.unalias)(references, annotation.term)}`;
if (annotation.qualifier) {
return `${annotationFQN}#${annotation.qualifier}`;
}
else {
return annotationFQN;
}
}
/**
* Merge annotation from different source together by overwriting at the term level.
*
* @param rawMetadata
* @param converter
* @returns the resulting merged annotations
*/
function mergeAnnotations(rawMetadata) {
const annotationListPerTarget = {};
Object.keys(rawMetadata.schema.annotations).forEach((annotationSource) => {
rawMetadata.schema.annotations[annotationSource].forEach((annotationList) => {
const currentTargetName = (0, utils_1.unalias)(rawMetadata.references, annotationList.target);
annotationList.__source = annotationSource;
if (!annotationListPerTarget[currentTargetName]) {
annotationListPerTarget[currentTargetName] = {
annotations: annotationList.annotations.map((annotation) => {
annotation.fullyQualifiedName = getAnnotationFQN(currentTargetName, rawMetadata.references, annotation);
annotation.__source = annotationSource;
return annotation;
}),
target: currentTargetName
};
annotationListPerTarget[currentTargetName].__source = annotationSource;
function mergeAnnotations(converter) {
return Object.keys(converter.rawSchema.annotations).reduceRight((annotationsPerTarget, annotationSource) => {
for (const { target: rawTarget, annotations: rawAnnotations } of converter.rawSchema.annotations[annotationSource]) {
const target = converter.unalias(rawTarget);
if (!annotationsPerTarget[target]) {
annotationsPerTarget[target] = [];
}
else {
annotationList.annotations.forEach((annotation) => {
const findIndex = annotationListPerTarget[currentTargetName].annotations.findIndex((referenceAnnotation) => {
return (referenceAnnotation.term === annotation.term &&
referenceAnnotation.qualifier === annotation.qualifier);
});
annotation.__source = annotationSource;
annotation.fullyQualifiedName = getAnnotationFQN(currentTargetName, rawMetadata.references, annotation);
if (findIndex !== -1) {
annotationListPerTarget[currentTargetName].annotations.splice(findIndex, 1, annotation);
}
else {
annotationListPerTarget[currentTargetName].annotations.push(annotation);
}
});
}
});
});
return annotationListPerTarget;
annotationsPerTarget[target].push(...rawAnnotations
.filter((rawAnnotation) => !annotationsPerTarget[target].some((existingAnnotation) => existingAnnotation.term === rawAnnotation.term &&
existingAnnotation.qualifier === rawAnnotation.qualifier))
.map((rawAnnotation) => {
let annotationFQN = `${target}@${converter.unalias(rawAnnotation.term)}`;
if (rawAnnotation.qualifier) {
annotationFQN = `${annotationFQN}#${rawAnnotation.qualifier}`;
}
const annotation = rawAnnotation;
annotation.fullyQualifiedName = annotationFQN;
annotation.__source = annotationSource;
return annotation;
}));
}
return annotationsPerTarget;
}, {});
}
class Converter {
get rawAnnotationsPerTarget() {
if (this._rawAnnotationsPerTarget === undefined) {
this._rawAnnotationsPerTarget = mergeAnnotations(this.rawMetadata);
/**
* Get preprocessed annotations on the specified target.
*
* @param target The annotation target
* @returns An array of annotations
*/
getAnnotations(target) {
var _a;
if (this.annotationyByTarget === undefined) {
this.annotationyByTarget = mergeAnnotations(this);
}
return this._rawAnnotationsPerTarget;
return (_a = this.annotationyByTarget[target]) !== null && _a !== void 0 ? _a : [];
}

@@ -816,5 +765,5 @@ getConvertedEntityContainer() {

}
unalias(value) {
unalias(value, references = this.rawMetadata.references) {
var _a;
return (_a = (0, utils_1.unalias)(this.rawMetadata.references, value)) !== null && _a !== void 0 ? _a : '';
return (_a = (0, utils_1.unalias)(references, value)) !== null && _a !== void 0 ? _a : '';
}

@@ -854,5 +803,23 @@ }

const nestedAnnotations = rawAnnotationTarget.annotations;
return () => createAnnotationsObject(converter, rawAnnotationTarget, nestedAnnotations !== null && nestedAnnotations !== void 0 ? nestedAnnotations : converter.getAnnotations(rawAnnotationTarget.fullyQualifiedName));
}
function resolveAnnotationsOnAnnotation(converter, annotationRecord, annotationTerm) {
return () => {
var _a, _b;
return createAnnotationsObject(converter, rawAnnotationTarget, (_b = nestedAnnotations !== null && nestedAnnotations !== void 0 ? nestedAnnotations : (_a = converter.rawAnnotationsPerTarget[rawAnnotationTarget.fullyQualifiedName]) === null || _a === void 0 ? void 0 : _a.annotations) !== null && _b !== void 0 ? _b : []);
const currentFQN = annotationTerm.fullyQualifiedName;
// be graceful when resolving annotations on annotations: Sometimes they are referenced directly, sometimes they
// are part of the global annotations list
let annotations;
if (annotationRecord.annotations && annotationRecord.annotations.length > 0) {
annotations = annotationRecord.annotations;
}
else {
annotations = converter.getAnnotations(currentFQN);
}
annotations === null || annotations === void 0 ? void 0 : annotations.forEach((annotation) => {
annotation.target = currentFQN;
annotation.__source = annotationTerm.__source;
annotation[ANNOTATION_TARGET] = annotationTerm[ANNOTATION_TARGET];
annotation.fullyQualifiedName = `${currentFQN}@${annotation.term}`;
});
return createAnnotationsObject(converter, annotationTerm, annotations !== null && annotations !== void 0 ? annotations : []);
};

@@ -1032,20 +999,13 @@ }

(0, utils_1.lazy)(convertedAction, 'annotations', () => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
// this.is.the.action(on.this.type) --> action: 'this.is.the.action', overload: 'on.this.type'
// this.is.the.action() --> action: 'this.is.the.action', overload: undefined
// this.is.the.action --> action: 'this.is.the.action', overload: undefined
const actionAndOverload = rawAction.fullyQualifiedName.match(/(?<action>[^()]+)(?:\((?<overload>.*)\))?/);
let rawAnnotations = [];
if (actionAndOverload) {
if ((_a = actionAndOverload.groups) === null || _a === void 0 ? void 0 : _a.overload) {
rawAnnotations = (_c = (_b = converter.rawAnnotationsPerTarget[rawAction.fullyQualifiedName]) === null || _b === void 0 ? void 0 : _b.annotations) !== null && _c !== void 0 ? _c : [];
const action = (0, utils_1.substringBeforeFirst)(rawAction.fullyQualifiedName, '(');
// if the action is unbound (e.g. "myAction"), the annotation target is "myAction()"
const annotationTargetFQN = rawAction.isBound
? rawAction.fullyQualifiedName
: `${rawAction.fullyQualifiedName}()`;
const rawAnnotations = converter.getAnnotations(annotationTargetFQN);
const baseAnnotations = converter.getAnnotations(action);
for (const baseAnnotation of baseAnnotations) {
if (!rawAnnotations.some((annotation) => annotation.term === baseAnnotation.term && annotation.qualifier === baseAnnotation.qualifier)) {
rawAnnotations.push(baseAnnotation);
}
else {
rawAnnotations =
(_f = (_e = converter.rawAnnotationsPerTarget[`${(_d = actionAndOverload.groups) === null || _d === void 0 ? void 0 : _d.action}()`]) === null || _e === void 0 ? void 0 : _e.annotations) !== null && _f !== void 0 ? _f : [];
}
if (((_g = actionAndOverload.groups) === null || _g === void 0 ? void 0 : _g.action) && ((_h = actionAndOverload.groups) === null || _h === void 0 ? void 0 : _h.action) !== rawAction.fullyQualifiedName) {
const baseAnnotations = (_l = (_k = converter.rawAnnotationsPerTarget[(_j = actionAndOverload.groups) === null || _j === void 0 ? void 0 : _j.action]) === null || _k === void 0 ? void 0 : _k.annotations) !== null && _l !== void 0 ? _l : [];
rawAnnotations = rawAnnotations.concat(baseAnnotations);
}
}

@@ -1052,0 +1012,0 @@ return createAnnotationsObject(converter, rawAction, rawAnnotations);

export * from './converter';
export * from './utils';
export * from './writeback';
export * from './utils';
//# sourceMappingURL=index.d.ts.map

@@ -18,4 +18,4 @@ "use strict";

__exportStar(require("./converter"), exports);
__exportStar(require("./utils"), exports);
__exportStar(require("./writeback"), exports);
__exportStar(require("./utils"), exports);
//# sourceMappingURL=index.js.map

@@ -1,5 +0,5 @@

import type { ComplexType, Reference, TypeDefinition, ArrayWithIndex } from '@sap-ux/vocabularies-types';
export { VocabularyReferences as defaultReferences } from '@sap-ux/vocabularies-types/vocabularies/VocabularyReferences';
import type { ArrayWithIndex, ComplexType, Reference, TypeDefinition } from '@sap-ux/vocabularies-types';
export { EnumIsFlag } from '@sap-ux/vocabularies-types/vocabularies/EnumIsFlag';
export { TermToTypes } from '@sap-ux/vocabularies-types/vocabularies/TermToTypes';
export { VocabularyReferences as defaultReferences } from '@sap-ux/vocabularies-types/vocabularies/VocabularyReferences';
export type ReferencesWithMap = Reference[] & {

@@ -50,7 +50,7 @@ referenceMap?: Record<string, Reference>;

/**
* Transform an aliased string representation annotation to the unaliased version.
* Transform an aliased string to its unaliased version given a set of references.
*
* @param references currentReferences for the project
* @param aliasedValue the aliased value
* @returns the unaliased string representing the same
* @param references The references to use for unaliasing.
* @param aliasedValue The aliased value
* @returns The equal unaliased string.
*/

@@ -57,0 +57,0 @@ export declare function unalias(references: ReferencesWithMap, aliasedValue: string | undefined): string | undefined;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addGetByValue = exports.createIndexedFind = exports.lazy = exports.Decimal = exports.isComplexTypeDefinition = exports.unalias = exports.alias = exports.substringBeforeLast = exports.substringBeforeFirst = exports.splitAtLast = exports.splitAtFirst = exports.TermToTypes = exports.EnumIsFlag = exports.defaultReferences = void 0;
var VocabularyReferences_1 = require("@sap-ux/vocabularies-types/vocabularies/VocabularyReferences");
Object.defineProperty(exports, "defaultReferences", { enumerable: true, get: function () { return VocabularyReferences_1.VocabularyReferences; } });
exports.addGetByValue = exports.createIndexedFind = exports.lazy = exports.Decimal = exports.isComplexTypeDefinition = exports.unalias = exports.alias = exports.substringBeforeLast = exports.substringBeforeFirst = exports.splitAtLast = exports.splitAtFirst = exports.defaultReferences = exports.TermToTypes = exports.EnumIsFlag = void 0;
var EnumIsFlag_1 = require("@sap-ux/vocabularies-types/vocabularies/EnumIsFlag");

@@ -10,2 +8,4 @@ Object.defineProperty(exports, "EnumIsFlag", { enumerable: true, get: function () { return EnumIsFlag_1.EnumIsFlag; } });

Object.defineProperty(exports, "TermToTypes", { enumerable: true, get: function () { return TermToTypes_1.TermToTypes; } });
var VocabularyReferences_1 = require("@sap-ux/vocabularies-types/vocabularies/VocabularyReferences");
Object.defineProperty(exports, "defaultReferences", { enumerable: true, get: function () { return VocabularyReferences_1.VocabularyReferences; } });
function splitAt(string, index) {

@@ -94,9 +94,13 @@ return index < 0 ? [string, ''] : [string.substring(0, index), string.substring(index + 1)];

/**
* Transform an aliased string representation annotation to the unaliased version.
* Transform an aliased string to its unaliased version given a set of references.
*
* @param references currentReferences for the project
* @param aliasedValue the aliased value
* @returns the unaliased string representing the same
* @param references The references to use for unaliasing.
* @param aliasedValue The aliased value
* @returns The equal unaliased string.
*/
function unalias(references, aliasedValue) {
var _a, _b;
if (!aliasedValue) {
return aliasedValue;
}
if (!references.referenceMap) {

@@ -108,18 +112,21 @@ references.referenceMap = references.reduce((map, ref) => {

}
if (!aliasedValue) {
return aliasedValue;
const separators = ['@', '/', '('];
const unaliased = [];
let start = 0;
for (let end = 0, maybeAlias = true; end < aliasedValue.length; end++) {
const char = aliasedValue[end];
if (maybeAlias && char === '.') {
const alias = aliasedValue.substring(start, end);
unaliased.push((_b = (_a = references.referenceMap[alias]) === null || _a === void 0 ? void 0 : _a.namespace) !== null && _b !== void 0 ? _b : alias);
start = end;
maybeAlias = false;
}
if (separators.includes(char)) {
unaliased.push(aliasedValue.substring(start, end + 1));
start = end + 1;
maybeAlias = true;
}
}
const [vocAlias, value] = splitAtFirst(aliasedValue, '.');
const reference = references.referenceMap[vocAlias];
if (reference) {
return `${reference.namespace}.${value}`;
}
else if (aliasedValue.includes('@')) {
// Try to see if it's an annotation Path like to_SalesOrder/@UI.LineItem
const [preAlias, postAlias] = splitAtFirst(aliasedValue, '@');
return `${preAlias}@${unalias(references, postAlias)}`;
}
else {
return aliasedValue;
}
unaliased.push(aliasedValue.substring(start));
return unaliased.join('');
}

@@ -126,0 +133,0 @@ exports.unalias = unalias;

{
"name": "@sap-ux/annotation-converter",
"version": "0.6.7",
"version": "0.6.8",
"description": "SAP Fiori OData - Annotation converter",

@@ -25,3 +25,3 @@ "repository": {

"clean": "rimraf dist",
"format": "prettier --write '**/*.{js,json,ts,yaml,yml}' --ignore-path ../../.prettierignore",
"format": "prettier **/* --write --ignore-unknown --ignore-path ../../.prettierignore",
"lint": "eslint . --ext .ts",

@@ -28,0 +28,0 @@ "lint:fix": "eslint . --ext .ts --fix",

@@ -6,3 +6,2 @@ import type {

Annotation,
AnnotationList,
AnnotationRecord,

@@ -35,3 +34,2 @@ ArrayWithIndex,

RawV4NavigationProperty,
Reference,
RemoveAnnotationAndType,

@@ -42,2 +40,3 @@ ResolutionTarget,

} from '@sap-ux/vocabularies-types';
import { VocabularyReferences } from '@sap-ux/vocabularies-types/vocabularies/VocabularyReferences';
import {

@@ -56,3 +55,2 @@ addGetByValue,

} from './utils';
import { VocabularyReferences } from '@sap-ux/vocabularies-types/vocabularies/VocabularyReferences';

@@ -200,3 +198,3 @@ /**

if (segment === '' || segment === thisElement.fullyQualifiedName) {
if (segment === '' || converter.unalias(segment) === thisElement.fullyQualifiedName) {
return current;

@@ -265,3 +263,4 @@ }

const action = thisElement.actions[segment];
const actionName = substringBeforeFirst(converter.unalias(segment), '(');
const action = thisElement.actions[actionName];
if (action) {

@@ -606,31 +605,11 @@ current.target = action;

) {
const annotationTerm: any = {
const record: any = {
$Type: parseRecordType(converter, currentTerm, currentTarget, currentProperty, annotationRecord),
fullyQualifiedName: currentFQN,
[ANNOTATION_TARGET]: currentTarget
[ANNOTATION_TARGET]: currentTarget,
__source: currentSource
};
// annotations on the record
lazy(annotationTerm, 'annotations', () => {
// be graceful when resolving annotations on annotations: Sometimes they are referenced directly, sometimes they
// are part of the global annotations list
let annotations;
if (annotationRecord.annotations && annotationRecord.annotations.length > 0) {
annotations = annotationRecord.annotations;
} else {
annotations = converter.rawAnnotationsPerTarget[currentFQN]?.annotations;
}
annotations?.forEach((annotation: any) => {
annotation.target = currentFQN;
annotation.__source = currentSource;
annotation[ANNOTATION_TARGET] = currentTarget;
annotation.fullyQualifiedName = `${currentFQN}@${annotation.term}`;
});
return createAnnotationsObject(converter, annotationTerm, annotations ?? []);
});
const annotationContent = annotationRecord.propertyValues?.reduce((annotationContent, propertyValue) => {
lazy(annotationContent, propertyValue.name, () =>
for (const propertyValue of annotationRecord.propertyValues) {
lazy(record, propertyValue.name, () =>
parseValue(

@@ -646,19 +625,22 @@ converter,

);
}
return annotationContent;
}, annotationTerm);
// annotations on the record
lazy(record, 'annotations', resolveAnnotationsOnAnnotation(converter, annotationRecord, record));
if (isDataFieldWithForAction(annotationContent)) {
lazy(annotationContent, 'ActionTarget', () => {
// try to resolve to a bound action of the annotation target
let actionTarget = currentTarget.actions?.[annotationContent.Action];
if (isDataFieldWithForAction(record)) {
lazy(record, 'ActionTarget', () => {
const actionTargetFQN = converter.unalias(record.Action?.toString());
// (1) Bound action of the annotation target?
let actionTarget = currentTarget.actions?.[actionTargetFQN];
if (!actionTarget) {
// try to find a corresponding unbound action
actionTarget = converter.getConvertedActionImport(annotationContent.Action)?.action;
// (2) ActionImport (= unbound action)?
actionTarget = converter.getConvertedActionImport(actionTargetFQN)?.action;
}
if (!actionTarget) {
// try to find a corresponding bound (!) action
actionTarget = converter.getConvertedAction(annotationContent.Action);
// (3) Bound action of a different EntityType
actionTarget = converter.getConvertedAction(actionTargetFQN);
if (!actionTarget?.isBound) {

@@ -671,3 +653,3 @@ actionTarget = undefined;

converter.logError(
`Unable to resolve the action '${annotationContent.Action}' defined for '${annotationTerm.fullyQualifiedName}'`
`${record.fullyQualifiedName}: Unable to resolve '${record.Action}' ('${actionTargetFQN}')`
);

@@ -678,3 +660,3 @@ }

}
return annotationContent;
return record;
}

@@ -922,3 +904,3 @@

annotation.term = converter.unalias(`${vocAlias}.${vocTerm}`);
annotation.term = converter.unalias(`${vocAlias}.${vocTerm}`, VocabularyReferences);
annotation.qualifier = rawAnnotation.qualifier;

@@ -928,23 +910,3 @@ annotation.__source = (rawAnnotation as any).__source;

try {
lazy(annotation, 'annotations', () => {
const annotationFQN = annotation.fullyQualifiedName;
// be graceful when resolving annotations on annotations: Sometimes they are referenced directly, sometimes they
// are part of the global annotations list
let annotations;
if (rawAnnotation.annotations && rawAnnotation.annotations.length > 0) {
annotations = rawAnnotation.annotations;
} else {
annotations = converter.rawAnnotationsPerTarget[annotationFQN]?.annotations;
}
annotations?.forEach((rawSubAnnotation: any) => {
rawSubAnnotation.target = annotationFQN;
rawSubAnnotation.__source = annotation.__source;
rawSubAnnotation[ANNOTATION_TARGET] = target;
rawSubAnnotation.fullyQualifiedName = `${annotationFQN}@${rawSubAnnotation.term}`;
});
return createAnnotationsObject(converter, annotation, annotations ?? []);
});
lazy(annotation, 'annotations', resolveAnnotationsOnAnnotation(converter, rawAnnotation, annotation));
} catch (e) {

@@ -957,73 +919,62 @@ // not an error: parseRecord() already adds annotations, but the other parseXXX functions don't, so this can happen

function getAnnotationFQN(currentTargetName: string, references: Reference[], annotation: RawAnnotation) {
const annotationFQN = `${currentTargetName}@${unalias(references, annotation.term)}`;
if (annotation.qualifier) {
return `${annotationFQN}#${annotation.qualifier}`;
} else {
return annotationFQN;
}
}
/**
* Merge annotation from different source together by overwriting at the term level.
*
* @param rawMetadata
* @param converter
* @returns the resulting merged annotations
*/
function mergeAnnotations(rawMetadata: RawMetadata): Record<string, AnnotationList> {
const annotationListPerTarget: Record<string, AnnotationList> = {};
Object.keys(rawMetadata.schema.annotations).forEach((annotationSource) => {
rawMetadata.schema.annotations[annotationSource].forEach((annotationList: AnnotationList) => {
const currentTargetName = unalias(rawMetadata.references, annotationList.target) as string;
(annotationList as any).__source = annotationSource;
if (!annotationListPerTarget[currentTargetName]) {
annotationListPerTarget[currentTargetName] = {
annotations: annotationList.annotations.map((annotation: RawAnnotation) => {
(annotation as Annotation).fullyQualifiedName = getAnnotationFQN(
currentTargetName,
rawMetadata.references,
annotation
);
(annotation as any).__source = annotationSource;
function mergeAnnotations(converter: Converter): Record<string, Annotation[]> {
return Object.keys(converter.rawSchema.annotations).reduceRight((annotationsPerTarget, annotationSource) => {
for (const { target: rawTarget, annotations: rawAnnotations } of converter.rawSchema.annotations[
annotationSource
]) {
const target = converter.unalias(rawTarget);
if (!annotationsPerTarget[target]) {
annotationsPerTarget[target] = [];
}
annotationsPerTarget[target].push(
...rawAnnotations
.filter(
(rawAnnotation) =>
!annotationsPerTarget[target].some(
(existingAnnotation) =>
existingAnnotation.term === rawAnnotation.term &&
existingAnnotation.qualifier === rawAnnotation.qualifier
)
)
.map((rawAnnotation) => {
let annotationFQN = `${target}@${converter.unalias(rawAnnotation.term)}`;
if (rawAnnotation.qualifier) {
annotationFQN = `${annotationFQN}#${rawAnnotation.qualifier}`;
}
const annotation = rawAnnotation as Annotation & { __source: string };
annotation.fullyQualifiedName = annotationFQN;
annotation.__source = annotationSource;
return annotation;
}),
target: currentTargetName
};
(annotationListPerTarget[currentTargetName] as any).__source = annotationSource;
} else {
annotationList.annotations.forEach((annotation: RawAnnotation) => {
const findIndex = annotationListPerTarget[currentTargetName].annotations.findIndex(
(referenceAnnotation: RawAnnotation) => {
return (
referenceAnnotation.term === annotation.term &&
referenceAnnotation.qualifier === annotation.qualifier
);
}
);
(annotation as any).__source = annotationSource;
(annotation as Annotation).fullyQualifiedName = getAnnotationFQN(
currentTargetName,
rawMetadata.references,
annotation
);
if (findIndex !== -1) {
annotationListPerTarget[currentTargetName].annotations.splice(findIndex, 1, annotation);
} else {
annotationListPerTarget[currentTargetName].annotations.push(annotation);
}
});
}
});
});
return annotationListPerTarget;
})
);
}
return annotationsPerTarget;
}, {} as Record<string, Annotation[]>);
}
class Converter {
private _rawAnnotationsPerTarget: Record<FullyQualifiedName, AnnotationList>;
get rawAnnotationsPerTarget(): Record<FullyQualifiedName, AnnotationList> {
if (this._rawAnnotationsPerTarget === undefined) {
this._rawAnnotationsPerTarget = mergeAnnotations(this.rawMetadata);
private annotationyByTarget: Record<FullyQualifiedName, Annotation[]>;
/**
* Get preprocessed annotations on the specified target.
*
* @param target The annotation target
* @returns An array of annotations
*/
getAnnotations(target: FullyQualifiedName): Annotation[] {
if (this.annotationyByTarget === undefined) {
this.annotationyByTarget = mergeAnnotations(this);
}
return this._rawAnnotationsPerTarget;
return this.annotationyByTarget[target] ?? [];
}

@@ -1149,3 +1100,3 @@

toDefaultAlias(value: string) {
toDefaultAlias(value: string | undefined) {
const unaliased = unalias(this.rawMetadata.references, value) ?? '';

@@ -1155,4 +1106,4 @@ return alias(VocabularyReferences, unaliased);

unalias(value: string | undefined) {
return unalias(this.rawMetadata.references, value) ?? '';
unalias(value: string | undefined, references = this.rawMetadata.references) {
return unalias(references, value) ?? '';
}

@@ -1210,8 +1161,34 @@ }

rawAnnotationTarget,
nestedAnnotations ??
converter.rawAnnotationsPerTarget[rawAnnotationTarget.fullyQualifiedName]?.annotations ??
[]
nestedAnnotations ?? converter.getAnnotations(rawAnnotationTarget.fullyQualifiedName)
);
}
function resolveAnnotationsOnAnnotation(
converter: Converter,
annotationRecord: AnnotationRecord | RawAnnotation,
annotationTerm: any
) {
return () => {
const currentFQN = annotationTerm.fullyQualifiedName;
// be graceful when resolving annotations on annotations: Sometimes they are referenced directly, sometimes they
// are part of the global annotations list
let annotations;
if (annotationRecord.annotations && annotationRecord.annotations.length > 0) {
annotations = annotationRecord.annotations;
} else {
annotations = converter.getAnnotations(currentFQN);
}
annotations?.forEach((annotation: any) => {
annotation.target = currentFQN;
annotation.__source = annotationTerm.__source;
annotation[ANNOTATION_TARGET] = annotationTerm[ANNOTATION_TARGET];
annotation.fullyQualifiedName = `${currentFQN}@${annotation.term}`;
});
return createAnnotationsObject(converter, annotationTerm, annotations ?? []);
};
}
function createAnnotationsObject(converter: Converter, target: any, rawAnnotations: RawAnnotation[]) {

@@ -1475,20 +1452,20 @@ return rawAnnotations.reduce((vocabularyAliases, annotation) => {

lazy(convertedAction, 'annotations', () => {
// this.is.the.action(on.this.type) --> action: 'this.is.the.action', overload: 'on.this.type'
// this.is.the.action() --> action: 'this.is.the.action', overload: undefined
// this.is.the.action --> action: 'this.is.the.action', overload: undefined
const actionAndOverload = rawAction.fullyQualifiedName.match(/(?<action>[^()]+)(?:\((?<overload>.*)\))?/);
const action = substringBeforeFirst(rawAction.fullyQualifiedName, '(');
let rawAnnotations: RawAnnotation[] = [];
if (actionAndOverload) {
if (actionAndOverload.groups?.overload) {
rawAnnotations = converter.rawAnnotationsPerTarget[rawAction.fullyQualifiedName]?.annotations ?? [];
} else {
rawAnnotations =
converter.rawAnnotationsPerTarget[`${actionAndOverload.groups?.action}()`]?.annotations ?? [];
}
// if the action is unbound (e.g. "myAction"), the annotation target is "myAction()"
const annotationTargetFQN = rawAction.isBound
? rawAction.fullyQualifiedName
: `${rawAction.fullyQualifiedName}()`;
if (actionAndOverload.groups?.action && actionAndOverload.groups?.action !== rawAction.fullyQualifiedName) {
const baseAnnotations =
converter.rawAnnotationsPerTarget[actionAndOverload.groups?.action]?.annotations ?? [];
rawAnnotations = rawAnnotations.concat(baseAnnotations);
const rawAnnotations = converter.getAnnotations(annotationTargetFQN);
const baseAnnotations = converter.getAnnotations(action);
for (const baseAnnotation of baseAnnotations) {
if (
!rawAnnotations.some(
(annotation) =>
annotation.term === baseAnnotation.term && annotation.qualifier === baseAnnotation.qualifier
)
) {
rawAnnotations.push(baseAnnotation);
}

@@ -1495,0 +1472,0 @@ }

export * from './converter';
export * from './utils';
export * from './writeback';
export * from './utils';

@@ -1,5 +0,6 @@

import type { Index, ComplexType, Reference, TypeDefinition, ArrayWithIndex } from '@sap-ux/vocabularies-types';
export { VocabularyReferences as defaultReferences } from '@sap-ux/vocabularies-types/vocabularies/VocabularyReferences';
import type { ArrayWithIndex, ComplexType, Index, Reference, TypeDefinition } from '@sap-ux/vocabularies-types';
export { EnumIsFlag } from '@sap-ux/vocabularies-types/vocabularies/EnumIsFlag';
export { TermToTypes } from '@sap-ux/vocabularies-types/vocabularies/TermToTypes';
export { VocabularyReferences as defaultReferences } from '@sap-ux/vocabularies-types/vocabularies/VocabularyReferences';

@@ -94,9 +95,13 @@ export type ReferencesWithMap = Reference[] & {

/**
* Transform an aliased string representation annotation to the unaliased version.
* Transform an aliased string to its unaliased version given a set of references.
*
* @param references currentReferences for the project
* @param aliasedValue the aliased value
* @returns the unaliased string representing the same
* @param references The references to use for unaliasing.
* @param aliasedValue The aliased value
* @returns The equal unaliased string.
*/
export function unalias(references: ReferencesWithMap, aliasedValue: string | undefined): string | undefined {
if (!aliasedValue) {
return aliasedValue;
}
if (!references.referenceMap) {

@@ -108,16 +113,23 @@ references.referenceMap = references.reduce((map: Record<string, Reference>, ref) => {

}
if (!aliasedValue) {
return aliasedValue;
const separators = ['@', '/', '('];
const unaliased: string[] = [];
let start = 0;
for (let end = 0, maybeAlias = true; end < aliasedValue.length; end++) {
const char = aliasedValue[end];
if (maybeAlias && char === '.') {
const alias = aliasedValue.substring(start, end);
unaliased.push(references.referenceMap[alias]?.namespace ?? alias);
start = end;
maybeAlias = false;
}
if (separators.includes(char)) {
unaliased.push(aliasedValue.substring(start, end + 1));
start = end + 1;
maybeAlias = true;
}
}
const [vocAlias, value] = splitAtFirst(aliasedValue, '.');
const reference = references.referenceMap[vocAlias];
if (reference) {
return `${reference.namespace}.${value}`;
} else if (aliasedValue.includes('@')) {
// Try to see if it's an annotation Path like to_SalesOrder/@UI.LineItem
const [preAlias, postAlias] = splitAtFirst(aliasedValue, '@');
return `${preAlias}@${unalias(references, postAlias)}`;
} else {
return aliasedValue;
}
unaliased.push(aliasedValue.substring(start));
return unaliased.join('');
}

@@ -124,0 +136,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc