Skip to content

Commit

Permalink
@cov2ap.analytics.skipForKey
Browse files Browse the repository at this point in the history
  • Loading branch information
oklemenz2 committed Aug 9, 2023
1 parent b434d15 commit f08f583
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 44 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Version 1.11.6 - 2023-09-xx
## Version 1.11.6 - 2023-08-10

### Changed

- Switch to `better-sqlite3` via `@cap-js/sqlite`
- Suppress analytical conversion via entity annotation `@cov2ap.analytics.skipForKey`, if all dimension key elements are requested

## Version 1.11.5 - 2023-08-02

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ The following OData V2 adapter for CDS specific annotations are supported:
**Entity Level**:

- `@cov2ap.analytics: false`: Suppress analytics conversion for the annotated entity, if set to `false`.
- `@cov2ap.analytics.skipForKey`: Suppress analytical conversion for the annotated entity, if all dimension key elements are requested
- `@cov2ap.deltaResponse: 'timestamp'`: Delta response '\_\_delta' is added to response data of annotated entity with current timestamp information.
- `@cov2ap.isoTime`: Values of type cds.Time (Edm.Time) are represented in ISO 8601 format for annotated entity.
- `@cov2ap.isoDate`: Values of type cds.Date (Edm.DateTime) are represented in ISO 8601 format for annotated entity.
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 67 additions & 37 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2123,24 +2123,17 @@ function cov2ap(options = {}) {

function convertAnalytics(url, req) {
const definition = req.context && req.context.definition;
if (
!(
definition &&
definition.kind === "entity" &&
definition["@cov2ap.analytics"] !== false &&
url.query["$select"] &&
(definition["@cov2ap.analytics"] === true ||
definition["@Analytics"] ||
definition["@Analytics.AnalyticalContext"] ||
definition["@Analytics.query"] ||
definition["@AnalyticalContext"] ||
definition["@Aggregation.ApplySupported.PropertyRestrictions"] ||
definition["@sap.semantics"] === "aggregate")
)
) {
if (!(isAnalyticsEntity(definition) && url.query["$select"])) {
return;
}
const elements = req.context.definitionElements;
const keyDimensions = [];
for (const name of structureKeys(elements)) {
const element = elements[name];
if (isDimensionElement(element) && element.key) {
keyDimensions.push(element);
}
}
const measures = [];
const dimensions = [];
const selects = url.query["$select"].split(",");
Expand All @@ -2154,20 +2147,22 @@ function cov2ap(options = {}) {
selects.forEach((select) => {
const element = elements[select];
if (element) {
if (
element["@Analytics.AnalyticalContext.Measure"] ||
element["@AnalyticalContext.Measure"] ||
element["@Analytics.Measure"] ||
element["@sap.aggregation.role"] === "measure"
) {
if (isMeasureElement(element)) {
measures.push(element);
} else {
// element["@Analytics.AnalyticalContext.Dimension"] || element["@AnalyticalContext.Dimension"] || element["@Analytics.Dimension"] || element["@sap.aggregation.role"] === "dimension"
// isDimensionElement(element)
dimensions.push(element);
}
}
});

if (
definition["@cov2ap.analytics.skipForKey"] &&
keyDimensions.every((keyDimension) => dimensions.includes(keyDimension))
) {
return;
}

if (dimensions.length > 0 || measures.length > 0) {
url.query["$apply"] = "";
if (dimensions.length) {
Expand All @@ -2184,8 +2179,7 @@ function cov2ap(options = {}) {
}
url.query["$apply"] += `aggregate(${measures
.map((measure) => {
const aggregation =
measure["@Aggregation.default"] || measure["@Aggregation.Default"] || measure["@DefaultAggregation"];
const aggregation = aggregationDefault(measure);
const aggregationName = aggregation ? aggregation["#"] || aggregation : DefaultAggregation;
const aggregationFunction = aggregationName ? AggregationMap[aggregationName.toUpperCase()] : undefined;
if (!aggregationFunction) {
Expand All @@ -2197,12 +2191,7 @@ function cov2ap(options = {}) {
if (aggregationFunction.startsWith("$")) {
return `${aggregationFunction} as ${AggregationPrefix}${measure.name}`;
} else {
const referenceElement =
measure["@Aggregation.referenceElement"] ||
measure["@Aggregation.ReferenceElement"] ||
measure["@Aggregation.reference"] ||
measure["@Aggregation.Reference"];
return `${referenceElement || measure.name} with ${aggregationFunction} as ${AggregationPrefix}${
return `${referenceElement(measure) || measure.name} with ${aggregationFunction} as ${AggregationPrefix}${
measure.name
}`;
}
Expand All @@ -2229,13 +2218,7 @@ function cov2ap(options = {}) {
.map((orderBy) => {
let [name, order] = orderBy.split(" ");
const element = elements[name];
if (
element &&
(element["@Analytics.AnalyticalContext.Measure"] ||
element["@AnalyticalContext.Measure"] ||
element["@Analytics.Measure"] ||
element["@sap.aggregation.role"] === "measure")
) {
if (element && isMeasureElement(element)) {
name = `${AggregationPrefix}${element.name}`;
}
return name + (order ? ` ${order}` : "");
Expand All @@ -2257,6 +2240,53 @@ function cov2ap(options = {}) {
}
}

function isAnalyticsEntity(entity) {
return (
entity &&
entity.kind === "entity" &&
entity["@cov2ap.analytics"] !== false &&
(entity["@cov2ap.analytics"] ||
entity["@cov2ap.analytics.skipForKey"] ||
entity["@Analytics"] ||
entity["@Analytics.AnalyticalContext"] ||
entity["@Analytics.query"] ||
entity["@AnalyticalContext"] ||
entity["@Aggregation.ApplySupported.PropertyRestrictions"] ||
entity["@sap.semantics"] === "aggregate")
);
}

function isDimensionElement(element) {
return (
element["@Analytics.AnalyticalContext.Dimension"] ||
element["@AnalyticalContext.Dimension"] ||
element["@Analytics.Dimension"] ||
element["@sap.aggregation.role"] === "dimension"
);
}

function isMeasureElement(element) {
return (
element["@Analytics.AnalyticalContext.Measure"] ||
element["@AnalyticalContext.Measure"] ||
element["@Analytics.Measure"] ||
element["@sap.aggregation.role"] === "measure"
);
}

function aggregationDefault(element) {
return element["@Aggregation.default"] || element["@Aggregation.Default"] || element["@DefaultAggregation"];
}

function referenceElement(element) {
return (
element["@Aggregation.referenceElement"] ||
element["@Aggregation.ReferenceElement"] ||
element["@Aggregation.reference"] ||
element["@Aggregation.Reference"]
);
}

function convertValue(url, req) {
if (url.contextPath.endsWith("/$value")) {
url.contextPath = url.contextPath.substr(0, url.contextPath.length - "/$value".length);
Expand Down
26 changes: 26 additions & 0 deletions test/__snapshots__/analytics-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ exports[`analytics GET $metadata 1`] = `
<Parameter Name="ID__" Type="Edm.String" Nullable="false" Mode="In" sap:sortable="false" sap:filterable="false"/>
<Parameter Name="number" Type="Edm.Int32" Mode="In" Nullable="true"/>
</FunctionImport>
<EntitySet Name="HeaderSkipKey" EntityType="test.AnalyticsService.HeaderSkipKey"/>
<EntitySet Name="HeaderLine" EntityType="test.AnalyticsService.HeaderLine"/>
<EntitySet Name="Country_texts" EntityType="test.AnalyticsService.Country_texts"/>
<EntitySet Name="Currency_texts" EntityType="test.AnalyticsService.Currency_texts"/>
Expand Down Expand Up @@ -160,6 +161,19 @@ exports[`analytics GET $metadata 1`] = `
<Property Name="price" Type="Edm.Decimal" sap:aggregation-role="measure" sap:variable-scale="true"/>
<Property Name="description" Type="Edm.String"/>
</EntityType>
<EntityType Name="HeaderSkipKey">
<Key>
<PropertyRef Name="ID"/>
<PropertyRef Name="country"/>
<PropertyRef Name="currency"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<Property Name="description" Type="Edm.String"/>
<Property Name="country" Type="Edm.String" Nullable="false"/>
<Property Name="currency" Type="Edm.String" Nullable="false"/>
<Property Name="stock" Type="Edm.Int32"/>
<Property Name="price" Type="Edm.Decimal" Scale="2" Precision="12"/>
</EntityType>
<EntityType Name="HeaderLine">
<Key>
<PropertyRef Name="ID"/>
Expand Down Expand Up @@ -729,6 +743,18 @@ exports[`analytics GET $metadata 1`] = `
<Annotations Target="test.AnalyticsService.Book/price" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Analytics.Measure" Bool="true"/>
</Annotations>
<Annotations Target="test.AnalyticsService.HeaderSkipKey/country" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Analytics.Dimension" Bool="true"/>
</Annotations>
<Annotations Target="test.AnalyticsService.HeaderSkipKey/currency" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Analytics.Dimension" Bool="true"/>
</Annotations>
<Annotations Target="test.AnalyticsService.HeaderSkipKey/stock" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Analytics.Measure" Bool="true"/>
</Annotations>
<Annotations Target="test.AnalyticsService.HeaderSkipKey/price" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Analytics.Measure" Bool="true"/>
</Annotations>
<Annotations Target="test.AnalyticsService.Country_texts/locale" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Common.Label" String="Language Code"/>
</Annotations>
Expand Down
15 changes: 15 additions & 0 deletions test/_env/srv/analytics.cds
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,19 @@ service AnalyticsService {
} actions {
action order(number: Integer) returns Book;
};

@cov2ap.analytics.skipForKey
entity HeaderSkipKey as projection on test.Header {
key ID,
description,
@Analytics.Dimension
key country,
@Analytics.Dimension
key currency,
@Analytics.Measure
stock,
@Analytics.Measure
@Aggregation.default : #AVG
price,
};
}
30 changes: 30 additions & 0 deletions test/analytics-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -949,4 +949,34 @@ describe("analytics", () => {
},
});
});

it("Skip analytics if all dimension key elements are requested", async () => {
let response = await util.callRead(request, "/odata/v2/analytics/HeaderSkipKey?$select=country,currency,stock");
expect(response.body).toBeDefined();
expect(response.body.d).toBeDefined();
expect(response.body.d.results).toBeDefined();
// no aggregation should have happened
expect(response.body.d.results.length).toEqual(7);

response = await util.callRead(request, "/odata/v2/analytics/HeaderSkipKey?$select=currency,stock");
expect(response.body).toBeDefined();
expect(response.body.d).toBeDefined();
expect(response.body.d.results).toBeDefined();
// aggregation should have happened
expect(response.body.d.results.length).toEqual(5);

response = await util.callRead(request, "/odata/v2/analytics/HeaderSkipKey?$select=ID,country,currency,stock");
expect(response.body).toBeDefined();
expect(response.body.d).toBeDefined();
expect(response.body.d.results).toBeDefined();
// no aggregation should have happened
expect(response.body.d.results.length).toEqual(7);

response = await util.callRead(request, "/odata/v2/analytics/HeaderSkipKey?$select=stock");
expect(response.body).toBeDefined();
expect(response.body.d).toBeDefined();
expect(response.body.d.results).toBeDefined();
// aggregation should have happened
expect(response.body.d.results.length).toEqual(1);
});
});

0 comments on commit f08f583

Please sign in to comment.