
List Report: Summary of all Countries world-wide and their corresponding Covid-19 cases sorted by new confirmed cases per default.
Object Page: Details of the country with key figures and a visualized historic data set.
GET /summary
: A summary of new and total cases per country, updated daily.GET /total/country
: Returns all cases by case type for a country.Data Model for Covid-19 Service
namespace srv;
using {db} from '../db/Covid';
service CovidService {
entity Countries as projection on db.Countries;
entity CountryHistoryDetails as projection on db.CountryHistoryDetails;
}
on.READ
event, call the REST service and map it to the defined data structure. This way we ensure to get the most up-to-date data. But on the other hand we also have to take care of applying and implementing OData functionality after retrieving the data, such as counting, sorting, filtering, selecting, aggregating, etc.before.READ
event, call the REST service and write the data into the DB. This way, we do not need to replicate the OData functionality and have implicitly build a caching mechanism.const cds = require("@sap/cds");
module.exports = cds.service.impl(function () {
this.before("READ", "Countries", async (req, next) => {
const { Countries } = this.entities;
// delete all countries
await DELETE.from(Countries);
// fetch daily summary from covid API
const Covid19Api = await cds.connect.to("Covid19Api");
var countries = await Covid19Api.run(req.query);
// insert summary into Countries table
await INSERT.into(Countries).entries(countries);
return;
});
Covid19API
..cdsrc.json
named Covid19API
. The URL property points to the root path of the API endpoint.{
"requires": {
"Covid19Api": {
"kind": "rest",
"impl": "srv/external/Covid19Api.js",
"credentials": {
"url": "https://api.covid19api.com"
}
}
}
}
Covid19Api
which extends cds.RemoteService
. In the init
method we need to add the required handlers:this.reject
: All events except READ are rejectedthis.before
: Responsible for translating the application service query (OData) to a query that the REST service understands.this.on
: Responsible for executing the REST call and translating the result back to the application service model.const cds = require("@sap/cds");
class Covid19Api extends cds.RemoteService {
async init() {
this.reject(["CREATE", "UPDATE", "DELETE"], "*");
this.before("READ", "*", async (req) => {
if (req.target.name === "srv.CovidService.Countries") {
req.myQuery = req.query;
req.query = "GET /summary";
}
if (req.target.name === "srv.CovidService.CountryHistoryDetails") {
...
});
this.on("READ", "*", async (req, next) => {
if (req.target.name === "srv.CovidService.Countries") {
const response = await next(req);
var items = parseResponseCountries(response);
return items;
}
if (req.target.name === "srv.CovidService.CountryHistoryDetails") {
...
}
});
super.init();
}
}
function parseResponseCountries(response) {
var countries = [];
response.Countries.forEach((c) => {
var i = new Object();
i.Country = c.Country;
i.Slug = c.Slug;
i.CountryCode = c.CountryCode;
i.NewConfirmed = c.NewConfirmed;
i.TotalConfirmed = c.TotalConfirmed;
i.NewDeaths = c.NewDeaths;
i.TotalDeaths = c.TotalDeaths;
i.NewRecovered = c.NewRecovered;
i.TotalRecovered = c.TotalRecovered;
i.Date = c.Date;
countries.push(i);
});
return countries;
}
module.exports = Covid19Api;
LineItem
:annotate CovidService.Countries with @(
UI : {
LineItem : [
{Value : Country},
{Value : NewConfirmed},
{Value : TotalConfirmed},
{Value : NewDeaths},
{Value : TotalDeaths}
]
});
HeaderInfo
:annotate CovidService.Countries with @(
UI : {
HeaderInfo : {
TypeName : 'Country',
TypeNamePlural : 'Countries',
}
});
PresentationVariant
:annotate CovidService.Countries with @(
UI : {
PresentationVariant : {
SortOrder : [
{
Property : NewConfirmed,
Descending : true
},
],
Visualizations : [ ![@UI.LineItem] ]
}
});
SelectionFields
:annotate CovidService.Countries with @(
UI : {
SelectionFields : [
Country
]
});
HeaderFacets
in conjunction with DataPoints
:annotate CovidService.Countries with @(
UI : {
DataPoint#TotalConfirmed : {
$Type : 'UI.DataPointType',
Value : TotalConfirmed,
Title : 'Total Confirmed',
}
HeaderFacets : [
{
$Type : 'UI.ReferenceFacet',
Target : '@UI.DataPoint#TotalConfirmed'
}
]
});
Facets
and Chart
:annotate CovidService.Countries with @(
UI : {
Facets : [
{
$Type : 'UI.ReferenceFacet',
Target : 'CountryHistoryDetails/@UI.Chart',
Label : 'Total Numbers Chart',
}
]
});
annotate CovidService.CountryHistoryDetails with @(
UI : {
Chart : {
$Type : 'UI.ChartDefinitionType',
ChartType : #Line,
Dimensions : [
Date
],
Measures : [
deaths, confirmed
],
Title : 'Total Numbers Chart',
},
});
annotate CovidService.CountryHistoryDetails with @(
Analytics.AggregatedProperties : [
{
Name : 'deaths',
AggregationMethod : 'sum',
AggregatableProperty : 'Deaths',
![@Common.Label] : 'Deaths'
},
{
Name : 'confirmed',
AggregationMethod : 'sum',
AggregatableProperty : 'Confirmed',
![@Common.Label] : 'Confirmed'
}
]
);
annotate CovidService.Countries with @(
UI : {
Facets : [
{
$Type : 'UI.ReferenceFacet',
Target : 'CountryHistoryDetails/@UI.LineItem',
Label : 'Total Numbers Table',
}
]
});
annotate CovidService.CountryHistoryDetails with @(
UI : {
LineItem : [
{Value : Date},
{Value : Confirmed},
{Value : Deaths},
{Value : Active},
{Value : Recovered}
]
});
cds run
we can use the build-in Fiori Preview option of the Service srv.CovidService/Countries
:cds.RemoteService
API we can use remote REST services as data sources for our application service in a CAP app. We can even use the DB to reuse the OData functionality and for caching.You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
16 | |
14 | |
13 | |
9 | |
9 | |
9 | |
8 | |
7 | |
7 | |
7 |