From 555792a1ce75cb840eb15e2e33347239306ddc8b Mon Sep 17 00:00:00 2001
From: GeraldKrause <43336693+GeraldKrause@users.noreply.github.com>
Date: Fri, 19 Jul 2024 08:48:17 +0200
Subject: [PATCH] Support annotating common expressions as sort order of entity
sets (#333)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This proposal addresses the requirement to use a common expression in
the sort order annotated to an entity set. For this purpose, this
proposal extends the structure type of term `Common.SortOrder`.
**Rationale for this extension**: In the concrete use case of this
requirement, EDMX is used to as an _interface definition language_
(a.k.a. _service provider interface (SPI)_) to describe the sort
behavior a service implementation compliant with the API definition must
offer for the annotated entity set. Allowing the use of an expression
for a sort item mirrors the expressiveness of the protocol ABNF and
leads to a self-contained API description that can be confidently
published on the SAP Business Accelerator Hub (that is, no need for an
out-of-band textual description).
**Discussion needed**: The type of term `Common.SortOrder` is reused in
`UI.PresentationVariant`. If there are concerns that this proposal adds
to much freedom for services and makes it hard for UIs to interpret
`Common.SortOrder` annotations because they lack a common expression
parser, an **alternative design** would be to keep the term
`Common.SortOrder` as it is today and create a new term
`Common.SortOrderExpressions` with a type derived from
`Common.SortOrderType` adding the expression to this structure.
Example from the use case of the SAP Build Work Zone product: Sort
instances of the `entities` entity set such that instances with
`identification/entityType = "Site"` are returned before all other
instances
```xml
identification/entityType
Site
…
```
---------
Co-authored-by: Ralf Handl
Co-authored-by: Heiko Theißen
---
examples/Common.SortOrder-sample.json | 25 +++++++++++++++++++++++
examples/Common.SortOrder-sample.xml | 29 +++++++++++++++++++++++++++
vocabularies/Common.json | 8 +++++++-
vocabularies/Common.md | 29 ++++++++++++++-------------
vocabularies/Common.xml | 6 +++++-
5 files changed, 81 insertions(+), 16 deletions(-)
create mode 100644 examples/Common.SortOrder-sample.json
create mode 100644 examples/Common.SortOrder-sample.xml
diff --git a/examples/Common.SortOrder-sample.json b/examples/Common.SortOrder-sample.json
new file mode 100644
index 000000000..44b2a28c9
--- /dev/null
+++ b/examples/Common.SortOrder-sample.json
@@ -0,0 +1,25 @@
+{
+ "$Version": "4.0",
+ "$Reference": {
+ "https://sap.github.io/odata-vocabularies/vocabularies/Common.json": {
+ "$Include": [{ "$Namespace": "com.sap.vocabularies.Common.v1", "$Alias": "Common" }]
+ }
+ },
+ "sortorder.sample": {
+ "WorkerTimeSheet": {
+ "$Kind": "EntityType",
+ "ClockInDateTime": { "$Type": "Edm.DateTimeOffset", "$Nullable": true, "$Precision": 0 },
+ "ClockOutDateTime": { "$Type": "Edm.DateTimeOffset", "$Nullable": true, "$Precision": 0 }
+ },
+ "$Annotations": {
+ "timezone.sample.WorkerTimeSheet": {
+ "@Common.SortOrder#HeroesOfLabor": [
+ {
+ "Expression": { "$Sub": [{ "$Path": "ClockOutDateTime" }, { "$Path": "ClockInDateTime" }] },
+ "Descending": true
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/examples/Common.SortOrder-sample.xml b/examples/Common.SortOrder-sample.xml
new file mode 100644
index 000000000..f069bd37d
--- /dev/null
+++ b/examples/Common.SortOrder-sample.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ClockOutDateTime
+ ClockInDateTime
+
+
+
+
+
+
+
+
+
+
diff --git a/vocabularies/Common.json b/vocabularies/Common.json
index b0ea507fe..548852c06 100644
--- a/vocabularies/Common.json
+++ b/vocabularies/Common.json
@@ -1175,7 +1175,7 @@
},
"SortOrderType": {
"$Kind": "ComplexType",
- "@Core.Description": "Exactly one of `Property` and `DynamicProperty` must be present",
+ "@Core.Description": "Exactly one of `Property`, `DynamicProperty` and `Expression` must be present",
"Property": {
"$Type": "Edm.PropertyPath",
"$Nullable": true,
@@ -1190,6 +1190,12 @@
"@Core.LongDescription": "If the annotation referenced by the annotation path does not apply to the same collection of entities\n as the one being sorted according to the [`UI.PresentationVariant`](UI.md#PresentationVariant) or `Common.SortOrder` annotation,\n this instance of `UI.PresentationVariant/SortOrder` or `Common.SortOrder` MUST be silently ignored.",
"@Validation.AllowedTerms": ["Analytics.AggregatedProperty", "Aggregation.CustomAggregate"]
},
+ "Expression": {
+ "$Type": "Edm.PrimitiveType",
+ "$Nullable": true,
+ "@Common.Experimental": true,
+ "@Core.Description": "Dynamic expression whose primitive result value is used to sort the instances"
+ },
"Descending": {
"$Type": "Edm.Boolean",
"$Nullable": true,
diff --git a/vocabularies/Common.md b/vocabularies/Common.md
index 2cecd992f..c2469172c 100644
--- a/vocabularies/Common.md
+++ b/vocabularies/Common.md
@@ -101,16 +101,16 @@ Term|Type|Description
[FilterDefaultValueHigh](Common.xml#L1285) *([Experimental](Common.md#Experimental))*|PrimitiveType?|A default upper limit for the property to be used in 'less than or equal' filter expressions.
[DerivedFilterDefaultValue](Common.xml#L1290) *([Experimental](Common.md#Experimental))*|String|Function import to derive a default value for the property from a given context in order to use it in filter expressions.
Function import has two parameters of complex types:
- `parameters`, a structure resembling the entity type the parameter entity set related to the entity set of the annotated property
- `properties`, a structure resembling the type of the entity set of the annotated property
The return type must be of the same type as the annotated property.
Arguments passed to the function import are used as context for deriving the default value. The function import returns this default value, or null in case such a value could not be determined.
[SortOrder](Common.xml#L1314)|\[[SortOrderType](#SortOrderType)\]|List of sort criteria
The items of the annotated entity set or the items of the collection of the annotated entity type are sorted by the first entry of the SortOrder collection. Items with same value for this first sort criteria are sorted by the second entry of the SortOrder collection, and so on.
-[RecursiveHierarchy](Common.xml#L1370) *(Deprecated)*|[RecursiveHierarchyType](#RecursiveHierarchyType)|Use terms [Aggregation.RecursiveHierarchy](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Aggregation.V1.md#RecursiveHierarchy) and [Hierarchy.RecursiveHierarchy](https://github.com/SAP/odata-vocabularies/blob/main/vocabularies/Hierarchy.md#RecursiveHierarchy) instead
-[CreatedAt](Common.xml#L1418)|DateTimeOffset?|Creation timestamp
-[CreatedBy](Common.xml#L1422)|[UserID?](#UserID)|First editor
-[ChangedAt](Common.xml#L1426)|DateTimeOffset?|Last modification timestamp
-[ChangedBy](Common.xml#L1430)|[UserID?](#UserID)|Last editor
-[OriginalProtocolVersion](Common.xml#L1442)|String|Original protocol version of a converted (V4) CSDL document, allowed values `2.0` and `3.0`
-[ApplyMultiUnitBehaviorForSortingAndFiltering](Common.xml#L1447) *([Experimental](Common.md#Experimental))*|[Tag](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Core.V1.md#Tag)|Sorting and filtering of amounts in multiple currencies needs special consideration
TODO: add link to UX documentation on https://experience.sap.com/fiori-design/
-[mediaUploadLink](Common.xml#L1453) *([Experimental](Common.md#Experimental))*|URL|URL for uploading new media content to a Document Management Service
In contrast to the `@odata.mediaEditLink` this URL allows to upload new media content without directly changing a stream property or media resource. The upload request typically uses HTTP POST with `Content-Type: multipart/form-data` following RFC 7578. The upload request must contain one multipart representing the content of the file. The `name` parameter in the `Content-Disposition` header (as described in RFC 7578) is irrelevant, but the `filename` parameter is expected. If the request succeeds the response will contain a JSON body of `Content-Type: application/json` with a JSON property `readLink`. The newly uploaded media resource can be linked to the stream property by changing the `@odata.mediaReadLink` to the value of this `readLink` in a subsequent PATCH request to the OData entity.
-[PrimitivePropertyPath](Common.xml#L1468) *([Experimental](Common.md#Experimental))*|[Tag](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Core.V1.md#Tag)|A term or term property with this tag whose type is (a collection of) `Edm.PropertyPath` MUST resolve to a primitive structural property
-[WebSocketBaseURL](Common.xml#L1473) *([Experimental](Common.md#Experimental))*|URL|Base URL for WebSocket connections
+[RecursiveHierarchy](Common.xml#L1374) *(Deprecated)*|[RecursiveHierarchyType](#RecursiveHierarchyType)|Use terms [Aggregation.RecursiveHierarchy](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Aggregation.V1.md#RecursiveHierarchy) and [Hierarchy.RecursiveHierarchy](https://github.com/SAP/odata-vocabularies/blob/main/vocabularies/Hierarchy.md#RecursiveHierarchy) instead
+[CreatedAt](Common.xml#L1422)|DateTimeOffset?|Creation timestamp
+[CreatedBy](Common.xml#L1426)|[UserID?](#UserID)|First editor
+[ChangedAt](Common.xml#L1430)|DateTimeOffset?|Last modification timestamp
+[ChangedBy](Common.xml#L1434)|[UserID?](#UserID)|Last editor
+[OriginalProtocolVersion](Common.xml#L1446)|String|Original protocol version of a converted (V4) CSDL document, allowed values `2.0` and `3.0`
+[ApplyMultiUnitBehaviorForSortingAndFiltering](Common.xml#L1451) *([Experimental](Common.md#Experimental))*|[Tag](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Core.V1.md#Tag)|Sorting and filtering of amounts in multiple currencies needs special consideration
TODO: add link to UX documentation on https://experience.sap.com/fiori-design/
+[mediaUploadLink](Common.xml#L1457) *([Experimental](Common.md#Experimental))*|URL|URL for uploading new media content to a Document Management Service
In contrast to the `@odata.mediaEditLink` this URL allows to upload new media content without directly changing a stream property or media resource. The upload request typically uses HTTP POST with `Content-Type: multipart/form-data` following RFC 7578. The upload request must contain one multipart representing the content of the file. The `name` parameter in the `Content-Disposition` header (as described in RFC 7578) is irrelevant, but the `filename` parameter is expected. If the request succeeds the response will contain a JSON body of `Content-Type: application/json` with a JSON property `readLink`. The newly uploaded media resource can be linked to the stream property by changing the `@odata.mediaReadLink` to the value of this `readLink` in a subsequent PATCH request to the OData entity.
+[PrimitivePropertyPath](Common.xml#L1472) *([Experimental](Common.md#Experimental))*|[Tag](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Core.V1.md#Tag)|A term or term property with this tag whose type is (a collection of) `Edm.PropertyPath` MUST resolve to a primitive structural property
+[WebSocketBaseURL](Common.xml#L1477) *([Experimental](Common.md#Experimental))*|URL|Base URL for WebSocket connections
## [TextFormatType](Common.xml#L120)
@@ -423,20 +423,21 @@ All side effects are essentially value changes, differentiation not needed.
## [SortOrderType](Common.xml#L1322)
-Exactly one of `Property` and `DynamicProperty` must be present
+Exactly one of `Property`, `DynamicProperty` and `Expression` must be present
Property|Type|Description
:-------|:---|:----------
[Property](Common.xml#L1324)|PropertyPath?|Sort property
[DynamicProperty](Common.xml#L1336)|AnnotationPath?|Dynamic property introduced by an annotation and used as sort property
If the annotation referenced by the annotation path does not apply to the same collection of entities as the one being sorted according to the [`UI.PresentationVariant`](UI.md#PresentationVariant) or `Common.SortOrder` annotation, this instance of `UI.PresentationVariant/SortOrder` or `Common.SortOrder` MUST be silently ignored.
Allowed terms:- [AggregatedProperty](Analytics.md#AggregatedProperty)
- [CustomAggregate](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Aggregation.V1.md#CustomAggregate)
-[Descending](Common.xml#L1350)|Boolean?|Sort direction, ascending if not specified otherwise
+[Expression](Common.xml#L1350) *([Experimental](Common.md#Experimental))*|PrimitiveType?|Dynamic expression whose primitive result value is used to sort the instances
+[Descending](Common.xml#L1354)|Boolean?|Sort direction, ascending if not specified otherwise
-## [RecursiveHierarchyType](Common.xml#L1383) *(Deprecated)*
+## [RecursiveHierarchyType](Common.xml#L1387) *(Deprecated)*
Use terms [Aggregation.RecursiveHierarchy](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Aggregation.V1.md#RecursiveHierarchy) and [Hierarchy.RecursiveHierarchy](https://github.com/SAP/odata-vocabularies/blob/main/vocabularies/Hierarchy.md#RecursiveHierarchy) instead
-## [UserID](Common.xml#L1434)
+## [UserID](Common.xml#L1438)
**Type:** String
User ID
diff --git a/vocabularies/Common.xml b/vocabularies/Common.xml
index d0bad5f30..e52534711 100644
--- a/vocabularies/Common.xml
+++ b/vocabularies/Common.xml
@@ -1320,7 +1320,7 @@ Upon modification field control logic is invoked so that meta-information like h
-
+
@@ -1347,6 +1347,10 @@ Upon modification field control logic is invoked so that meta-information like h
+
+
+
+