@sap-ux/annotation-converter
Advanced tools
Comparing version 0.6.16 to 0.7.0
# @sap-ux/annotation-converter | ||
## 0.7.0 | ||
### Minor Changes | ||
- e70d625: - NavigationPropertyBindings are now resolved by the annotation converter. This is a breaking change for consumers of types `RawSingleton` or `RawEntitySet` from package @sap-ux/vocabularies-types (the type of property `navigationPropertyBinding` changed). | ||
- Annotations of action parameters are now also resolved for unbound actions and unbound functions. The fully-qualified name of unbound actions and unbound functions changed - they now always include their overloads. E.g., in case of unbound actions: old `myAction`, new: `myAction()` - `()` denotes the "unbound overload". | ||
### Patch Changes | ||
- Updated dependencies [e70d625] | ||
- @sap-ux/vocabularies-types@0.8.0 | ||
## 0.6.16 | ||
@@ -4,0 +16,0 @@ |
@@ -463,13 +463,13 @@ "use strict"; | ||
(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()); | ||
var _a, _b; | ||
const actionName = 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]; | ||
let actionTarget = currentTarget.actions[actionName]; | ||
if (!actionTarget) { | ||
// (2) ActionImport (= unbound action)? | ||
actionTarget = (_c = converter.getConvertedActionImport(actionTargetFQN)) === null || _c === void 0 ? void 0 : _c.action; | ||
actionTarget = (_b = converter.getConvertedActionImport(actionName)) === null || _b === void 0 ? void 0 : _b.action; | ||
} | ||
if (!actionTarget) { | ||
// (3) Bound action of a different EntityType | ||
actionTarget = converter.getConvertedAction(actionTargetFQN); | ||
// (3) Bound action of a different EntityType (the actionName is fully qualified in this case) | ||
actionTarget = converter.getConvertedAction(actionName); | ||
if (!(actionTarget === null || actionTarget === void 0 ? void 0 : actionTarget.isBound)) { | ||
@@ -480,3 +480,3 @@ actionTarget = undefined; | ||
if (!actionTarget) { | ||
converter.logError(`${record.fullyQualifiedName}: Unable to resolve '${record.Action}' ('${actionTargetFQN}')`); | ||
converter.logError(`${record.fullyQualifiedName}: Unable to resolve '${record.Action}' ('${actionName}')`); | ||
} | ||
@@ -728,18 +728,9 @@ return actionTarget; | ||
} | ||
function resolveNavigationPropertyBindings(converter, rawNavigationPropertyBindings, rawElement) { | ||
return () => Object.keys(rawNavigationPropertyBindings).reduce((navigationPropertyBindings, bindingName) => { | ||
const rawBindingTarget = rawNavigationPropertyBindings[bindingName]; | ||
(0, utils_1.lazy)(navigationPropertyBindings, bindingName, () => { | ||
let resolvedBindingTarget; | ||
if (rawBindingTarget._type === 'Singleton') { | ||
resolvedBindingTarget = converter.getConvertedSingleton(rawBindingTarget.fullyQualifiedName); | ||
} | ||
else { | ||
resolvedBindingTarget = converter.getConvertedEntitySet(rawBindingTarget.fullyQualifiedName); | ||
} | ||
if (!resolvedBindingTarget) { | ||
converter.logError(`${rawElement._type} '${rawElement.fullyQualifiedName}': Failed to resolve NavigationPropertyBinding ${bindingName}`); | ||
resolvedBindingTarget = {}; | ||
} | ||
return resolvedBindingTarget; | ||
function resolveNavigationPropertyBindings(converter, rawNavigationPropertyBindings) { | ||
return () => Object.keys(rawNavigationPropertyBindings).reduce((navigationPropertyBindings, bindingPath) => { | ||
const rawBindingTarget = rawNavigationPropertyBindings[bindingPath]; | ||
(0, utils_1.lazy)(navigationPropertyBindings, bindingPath, () => { | ||
var _a; | ||
// the NavigationPropertyBinding will lead to either an EntitySet or a Singleton, it cannot be undefined | ||
return (((_a = converter.getConvertedEntitySet(rawBindingTarget)) !== null && _a !== void 0 ? _a : converter.getConvertedSingleton(rawBindingTarget))); | ||
}); | ||
@@ -812,5 +803,5 @@ return navigationPropertyBindings; | ||
(0, utils_1.lazy)(convertedSingleton, 'entityType', resolveEntityType(converter, rawSingleton.entityTypeName)); | ||
(0, utils_1.lazy)(convertedSingleton, 'annotations', resolveAnnotations(converter, rawSingleton)); | ||
(0, utils_1.lazy)(convertedSingleton, 'annotations', resolveAnnotations(converter, convertedSingleton)); | ||
const _rawNavigationPropertyBindings = rawSingleton.navigationPropertyBinding; | ||
(0, utils_1.lazy)(convertedSingleton, 'navigationPropertyBinding', resolveNavigationPropertyBindings(converter, _rawNavigationPropertyBindings, rawSingleton)); | ||
(0, utils_1.lazy)(convertedSingleton, 'navigationPropertyBinding', resolveNavigationPropertyBindings(converter, _rawNavigationPropertyBindings)); | ||
return convertedSingleton; | ||
@@ -828,5 +819,5 @@ } | ||
(0, utils_1.lazy)(convertedEntitySet, 'entityType', resolveEntityType(converter, rawEntitySet.entityTypeName)); | ||
(0, utils_1.lazy)(convertedEntitySet, 'annotations', resolveAnnotations(converter, rawEntitySet)); | ||
(0, utils_1.lazy)(convertedEntitySet, 'annotations', resolveAnnotations(converter, convertedEntitySet)); | ||
const _rawNavigationPropertyBindings = rawEntitySet.navigationPropertyBinding; | ||
(0, utils_1.lazy)(convertedEntitySet, 'navigationPropertyBinding', resolveNavigationPropertyBindings(converter, _rawNavigationPropertyBindings, rawEntitySet)); | ||
(0, utils_1.lazy)(convertedEntitySet, 'navigationPropertyBinding', resolveNavigationPropertyBindings(converter, _rawNavigationPropertyBindings)); | ||
return convertedEntitySet; | ||
@@ -919,3 +910,12 @@ } | ||
(0, utils_1.lazy)(convertedActionImport, 'annotations', resolveAnnotations(converter, rawActionImport)); | ||
(0, utils_1.lazy)(convertedActionImport, 'action', () => converter.getConvertedAction(rawActionImport.actionName)); | ||
(0, utils_1.lazy)(convertedActionImport, 'action', () => { | ||
const rawActions = converter.rawSchema.actions.filter((rawAction) => !rawAction.isBound && rawAction.fullyQualifiedName.startsWith(rawActionImport.actionName)); | ||
// this always resolves to a unique unbound action, but resolution of unbound functions can be ambiguous: | ||
// unbound function FQNs have overloads depending on all of their parameters | ||
if (rawActions.length > 1) { | ||
converter.logError(`Ambiguous reference in action import: ${rawActionImport.fullyQualifiedName}`); | ||
} | ||
// return the first matching action or function | ||
return converter.getConvertedAction(rawActions[0].fullyQualifiedName); | ||
}); | ||
return convertedActionImport; | ||
@@ -940,15 +940,26 @@ } | ||
(0, utils_1.lazy)(convertedAction, 'annotations', () => { | ||
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); | ||
/* | ||
Annotation resolution rule for actions: | ||
(1) annotation target: the specific unbound or bound overload, e.g. | ||
- for actions: "x.y.z.unboundAction()" / "x.y.z.boundAction(x.y.z.Entity)" | ||
- for functions: "x.y.z.unboundFunction(Edm.String)" / "x.y.z.unboundFunction(x.y.z.Entity,Edm.String)" | ||
(2) annotation target: unspecified overload, e.g. | ||
- for actions: "x.y.z.unboundAction" / "x.y.z.boundAction" | ||
- for functions: "x.y.z.unboundFunction" / "x.y.z.unboundFunction" | ||
When resolving (1) takes precedence over (2). That is, annotations on the specific overload overwrite | ||
annotations on the unspecific overload, on term/qualifier level. | ||
*/ | ||
const unspecificOverloadTarget = (0, utils_1.substringBeforeFirst)(rawAction.fullyQualifiedName, '('); | ||
const specificOverloadTarget = rawAction.fullyQualifiedName; | ||
const effectiveAnnotations = converter.getAnnotations(specificOverloadTarget); | ||
const unspecificAnnotations = converter.getAnnotations(unspecificOverloadTarget); | ||
for (const unspecificAnnotation of unspecificAnnotations) { | ||
if (!effectiveAnnotations.some((annotation) => annotation.term === unspecificAnnotation.term && | ||
annotation.qualifier === unspecificAnnotation.qualifier)) { | ||
effectiveAnnotations.push(unspecificAnnotation); | ||
} | ||
} | ||
return createAnnotationsObject(converter, rawAction, rawAnnotations); | ||
return createAnnotationsObject(converter, rawAction, effectiveAnnotations); | ||
}); | ||
@@ -970,3 +981,17 @@ return convertedAction; | ||
}); | ||
(0, utils_1.lazy)(convertedActionParameter, 'annotations', resolveAnnotations(converter, rawActionParameter)); | ||
(0, utils_1.lazy)(convertedActionParameter, 'annotations', () => { | ||
// annotations on action parameters are resolved following the rules for actions | ||
const unspecificOverloadTarget = rawActionParameter.fullyQualifiedName.substring(0, rawActionParameter.fullyQualifiedName.indexOf('(')) + | ||
rawActionParameter.fullyQualifiedName.substring(rawActionParameter.fullyQualifiedName.indexOf(')') + 1); | ||
const specificOverloadTarget = rawActionParameter.fullyQualifiedName; | ||
const effectiveAnnotations = converter.getAnnotations(specificOverloadTarget); | ||
const unspecificAnnotations = converter.getAnnotations(unspecificOverloadTarget); | ||
for (const unspecificAnnotation of unspecificAnnotations) { | ||
if (!effectiveAnnotations.some((annotation) => annotation.term === unspecificAnnotation.term && | ||
annotation.qualifier === unspecificAnnotation.qualifier)) { | ||
effectiveAnnotations.push(unspecificAnnotation); | ||
} | ||
} | ||
return createAnnotationsObject(converter, rawActionParameter, effectiveAnnotations); | ||
}); | ||
return convertedActionParameter; | ||
@@ -973,0 +998,0 @@ } |
{ | ||
"name": "@sap-ux/annotation-converter", | ||
"version": "0.6.16", | ||
"version": "0.7.0", | ||
"description": "SAP Fiori OData - Annotation converter", | ||
@@ -17,6 +17,6 @@ "repository": { | ||
"dependencies": { | ||
"@sap-ux/vocabularies-types": "0.7.6" | ||
"@sap-ux/vocabularies-types": "0.8.0" | ||
}, | ||
"devDependencies": { | ||
"@sap-ux/edmx-parser": "0.5.16" | ||
"@sap-ux/edmx-parser": "0.6.0" | ||
}, | ||
@@ -23,0 +23,0 @@ "scripts": { |
@@ -30,2 +30,3 @@ import type { | ||
RawMetadata, | ||
RawNavigationPropertyBinding, | ||
RawProperty, | ||
@@ -709,15 +710,15 @@ RawSchema, | ||
lazy(record, 'ActionTarget', () => { | ||
const actionTargetFQN = converter.unalias(record.Action?.toString()); | ||
const actionName = converter.unalias(record.Action?.toString()); | ||
// (1) Bound action of the annotation target? | ||
let actionTarget = currentTarget.actions?.[actionTargetFQN]; | ||
let actionTarget = currentTarget.actions[actionName]; | ||
if (!actionTarget) { | ||
// (2) ActionImport (= unbound action)? | ||
actionTarget = converter.getConvertedActionImport(actionTargetFQN)?.action; | ||
actionTarget = converter.getConvertedActionImport(actionName)?.action; | ||
} | ||
if (!actionTarget) { | ||
// (3) Bound action of a different EntityType | ||
actionTarget = converter.getConvertedAction(actionTargetFQN); | ||
// (3) Bound action of a different EntityType (the actionName is fully qualified in this case) | ||
actionTarget = converter.getConvertedAction(actionName); | ||
if (!actionTarget?.isBound) { | ||
@@ -730,3 +731,3 @@ actionTarget = undefined; | ||
converter.logError( | ||
`${record.fullyQualifiedName}: Unable to resolve '${record.Action}' ('${actionTargetFQN}')` | ||
`${record.fullyQualifiedName}: Unable to resolve '${record.Action}' ('${actionName}')` | ||
); | ||
@@ -1108,24 +1109,16 @@ } | ||
converter: Converter, | ||
rawNavigationPropertyBindings: Singleton['navigationPropertyBinding'] | EntitySet['navigationPropertyBinding'], | ||
rawElement: RawSingleton | RawEntitySet | ||
rawNavigationPropertyBindings: RawNavigationPropertyBinding | ||
) { | ||
return () => | ||
Object.keys(rawNavigationPropertyBindings).reduce((navigationPropertyBindings, bindingName) => { | ||
const rawBindingTarget = rawNavigationPropertyBindings[bindingName]; | ||
Object.keys(rawNavigationPropertyBindings).reduce((navigationPropertyBindings, bindingPath) => { | ||
const rawBindingTarget = rawNavigationPropertyBindings[bindingPath]; | ||
lazy(navigationPropertyBindings, bindingName, () => { | ||
let resolvedBindingTarget; | ||
if (rawBindingTarget._type === 'Singleton') { | ||
resolvedBindingTarget = converter.getConvertedSingleton(rawBindingTarget.fullyQualifiedName); | ||
} else { | ||
resolvedBindingTarget = converter.getConvertedEntitySet(rawBindingTarget.fullyQualifiedName); | ||
} | ||
if (!resolvedBindingTarget) { | ||
converter.logError( | ||
`${rawElement._type} '${rawElement.fullyQualifiedName}': Failed to resolve NavigationPropertyBinding ${bindingName}` | ||
); | ||
resolvedBindingTarget = {} as any; | ||
} | ||
return resolvedBindingTarget; | ||
}); | ||
lazy( | ||
navigationPropertyBindings, | ||
bindingPath, | ||
() => | ||
// the NavigationPropertyBinding will lead to either an EntitySet or a Singleton, it cannot be undefined | ||
(converter.getConvertedEntitySet(rawBindingTarget) ?? | ||
converter.getConvertedSingleton(rawBindingTarget))! | ||
); | ||
return navigationPropertyBindings; | ||
@@ -1229,6 +1222,6 @@ }, {} as EntitySet['navigationPropertyBinding'] | Singleton['navigationPropertyBinding']); | ||
function convertSingleton(converter: Converter, rawSingleton: RawSingleton): Singleton { | ||
const convertedSingleton = rawSingleton as Singleton; | ||
const convertedSingleton = rawSingleton as unknown as Singleton; | ||
lazy(convertedSingleton, 'entityType', resolveEntityType(converter, rawSingleton.entityTypeName)); | ||
lazy(convertedSingleton, 'annotations', resolveAnnotations(converter, rawSingleton as Singleton)); | ||
lazy(convertedSingleton, 'annotations', resolveAnnotations(converter, convertedSingleton)); | ||
@@ -1239,7 +1232,3 @@ const _rawNavigationPropertyBindings = rawSingleton.navigationPropertyBinding; | ||
'navigationPropertyBinding', | ||
resolveNavigationPropertyBindings( | ||
converter, | ||
_rawNavigationPropertyBindings as Singleton['navigationPropertyBinding'], | ||
rawSingleton | ||
) | ||
resolveNavigationPropertyBindings(converter, _rawNavigationPropertyBindings) | ||
); | ||
@@ -1258,6 +1247,6 @@ | ||
function convertEntitySet(converter: Converter, rawEntitySet: RawEntitySet): EntitySet { | ||
const convertedEntitySet = rawEntitySet as EntitySet; | ||
const convertedEntitySet = rawEntitySet as unknown as EntitySet; | ||
lazy(convertedEntitySet, 'entityType', resolveEntityType(converter, rawEntitySet.entityTypeName)); | ||
lazy(convertedEntitySet, 'annotations', resolveAnnotations(converter, rawEntitySet as EntitySet)); | ||
lazy(convertedEntitySet, 'annotations', resolveAnnotations(converter, convertedEntitySet)); | ||
@@ -1268,7 +1257,3 @@ const _rawNavigationPropertyBindings = rawEntitySet.navigationPropertyBinding; | ||
'navigationPropertyBinding', | ||
resolveNavigationPropertyBindings( | ||
converter, | ||
_rawNavigationPropertyBindings as EntitySet['navigationPropertyBinding'], | ||
rawEntitySet | ||
) | ||
resolveNavigationPropertyBindings(converter, _rawNavigationPropertyBindings) | ||
); | ||
@@ -1399,4 +1384,17 @@ | ||
lazy(convertedActionImport, 'action', () => converter.getConvertedAction(rawActionImport.actionName)); | ||
lazy(convertedActionImport, 'action', () => { | ||
const rawActions = converter.rawSchema.actions.filter( | ||
(rawAction) => !rawAction.isBound && rawAction.fullyQualifiedName.startsWith(rawActionImport.actionName) | ||
); | ||
// this always resolves to a unique unbound action, but resolution of unbound functions can be ambiguous: | ||
// unbound function FQNs have overloads depending on all of their parameters | ||
if (rawActions.length > 1) { | ||
converter.logError(`Ambiguous reference in action import: ${rawActionImport.fullyQualifiedName}`); | ||
} | ||
// return the first matching action or function | ||
return converter.getConvertedAction(rawActions[0].fullyQualifiedName)!; | ||
}); | ||
return convertedActionImport; | ||
@@ -1426,24 +1424,35 @@ } | ||
lazy(convertedAction, 'annotations', () => { | ||
const action = substringBeforeFirst(rawAction.fullyQualifiedName, '('); | ||
/* | ||
Annotation resolution rule for actions: | ||
// if the action is unbound (e.g. "myAction"), the annotation target is "myAction()" | ||
const annotationTargetFQN = rawAction.isBound | ||
? rawAction.fullyQualifiedName | ||
: `${rawAction.fullyQualifiedName}()`; | ||
(1) annotation target: the specific unbound or bound overload, e.g. | ||
- for actions: "x.y.z.unboundAction()" / "x.y.z.boundAction(x.y.z.Entity)" | ||
- for functions: "x.y.z.unboundFunction(Edm.String)" / "x.y.z.unboundFunction(x.y.z.Entity,Edm.String)" | ||
(2) annotation target: unspecified overload, e.g. | ||
- for actions: "x.y.z.unboundAction" / "x.y.z.boundAction" | ||
- for functions: "x.y.z.unboundFunction" / "x.y.z.unboundFunction" | ||
const rawAnnotations = converter.getAnnotations(annotationTargetFQN); | ||
const baseAnnotations = converter.getAnnotations(action); | ||
When resolving (1) takes precedence over (2). That is, annotations on the specific overload overwrite | ||
annotations on the unspecific overload, on term/qualifier level. | ||
*/ | ||
for (const baseAnnotation of baseAnnotations) { | ||
const unspecificOverloadTarget = substringBeforeFirst(rawAction.fullyQualifiedName, '('); | ||
const specificOverloadTarget = rawAction.fullyQualifiedName; | ||
const effectiveAnnotations = converter.getAnnotations(specificOverloadTarget); | ||
const unspecificAnnotations = converter.getAnnotations(unspecificOverloadTarget); | ||
for (const unspecificAnnotation of unspecificAnnotations) { | ||
if ( | ||
!rawAnnotations.some( | ||
!effectiveAnnotations.some( | ||
(annotation) => | ||
annotation.term === baseAnnotation.term && annotation.qualifier === baseAnnotation.qualifier | ||
annotation.term === unspecificAnnotation.term && | ||
annotation.qualifier === unspecificAnnotation.qualifier | ||
) | ||
) { | ||
rawAnnotations.push(baseAnnotation); | ||
effectiveAnnotations.push(unspecificAnnotation); | ||
} | ||
} | ||
return createAnnotationsObject(converter, rawAction, rawAnnotations); | ||
return createAnnotationsObject(converter, rawAction, effectiveAnnotations); | ||
}); | ||
@@ -1476,4 +1485,27 @@ | ||
lazy(convertedActionParameter, 'annotations', resolveAnnotations(converter, rawActionParameter)); | ||
lazy(convertedActionParameter, 'annotations', () => { | ||
// annotations on action parameters are resolved following the rules for actions | ||
const unspecificOverloadTarget = | ||
rawActionParameter.fullyQualifiedName.substring(0, rawActionParameter.fullyQualifiedName.indexOf('(')) + | ||
rawActionParameter.fullyQualifiedName.substring(rawActionParameter.fullyQualifiedName.indexOf(')') + 1); | ||
const specificOverloadTarget = rawActionParameter.fullyQualifiedName; | ||
const effectiveAnnotations = converter.getAnnotations(specificOverloadTarget); | ||
const unspecificAnnotations = converter.getAnnotations(unspecificOverloadTarget); | ||
for (const unspecificAnnotation of unspecificAnnotations) { | ||
if ( | ||
!effectiveAnnotations.some( | ||
(annotation) => | ||
annotation.term === unspecificAnnotation.term && | ||
annotation.qualifier === unspecificAnnotation.qualifier | ||
) | ||
) { | ||
effectiveAnnotations.push(unspecificAnnotation); | ||
} | ||
} | ||
return createAnnotationsObject(converter, rawActionParameter, effectiveAnnotations); | ||
}); | ||
return convertedActionParameter; | ||
@@ -1480,0 +1512,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
228786
3713
+ Added@sap-ux/vocabularies-types@0.8.0(transitive)
- Removed@sap-ux/vocabularies-types@0.7.6(transitive)