Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/_i18n/i18n.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ArrivalTime=Arrival Time
BeginDate=Starting Date
Booking=Booking
BookingDate=Booking Date
Status.code=Status
BookingFee=Booking Fee
BookingID=Booking ID
BookingSupplement=Booking Supplement
Expand Down Expand Up @@ -50,4 +51,5 @@ Travel=Travel
TravelAgency=Travel Agency
Travels=Travels
TravelStatus=Travel Status
Status=Booking Status
WebAddress=Web Address
1 change: 1 addition & 0 deletions app/_i18n/i18n_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ Travel=Reise
TravelAgency=Veranstalter
Travels=Reisen
TravelStatus=Reisestatus
Status=Buchungsstatus
WebAddress=Web-Adresse
1 change: 1 addition & 0 deletions app/_i18n/i18n_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ Travel=Travel
TravelAgency=Travel Agency
Travels=Travels
TravelStatus=Travel Status
Status=Booking Status
WebAddress=Web Address
1 change: 1 addition & 0 deletions app/_i18n/i18n_fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ Travel=Voyage
TravelAgency=Agence de voyage
Travels=Voyages
TravelStatus=Statut du voyage
Status=Statut de la réservation
WebAddress=Adresse e-mail
14 changes: 14 additions & 0 deletions app/travels/layouts.cds
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ annotate TravelService.Bookings with @UI : {
{ Value : Flight.destination, Label : '{i18n>Destination}', @HTML5.CssDefaults: {width:'19em'}, @Common.FieldControl: #ReadOnly },
{ Value : Flight.airline, Label : '{i18n>Airline}', @HTML5.CssDefaults: {width:'11em'}, @Common.FieldControl: #ReadOnly },
{ Value : BookingDate, Label : '{i18n>BookingDate}' },
{ Value : Status.code, Label : '{i18n>Status}', @UI.Importance : #High, @HTML5.CssDefaults: {width:'10em'},
Criticality : (
Status.code == #Confirmed ? 3 :
Status.code == #Failed ? 1 :
0
),
},
],

Facets : [{
Expand Down Expand Up @@ -140,6 +147,13 @@ annotate TravelService.Bookings with @UI : {
{ Value : Pos },
{ Value : BookingDate },
{ Value : Travel.Customer.ID },
{ Value : Status.code, Label : '{i18n>Status}',
Criticality : (
Status.code == #Confirmed ? 3 :
Status.code == #Failed ? 1 :
0
),
},
]},

FieldGroup #Flight: {Data: [
Expand Down
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
"version": "1.0.1",
"description": "CAP XTravels sample application",
"scripts": {
"start": "cds watch --port 4004",
"reset": "cds deploy && cds watch --port 4004",
"lint": "npx eslint .",
"start": "cds-serve",
"test": "cds test"
},
"dependencies": {
Expand All @@ -19,7 +20,16 @@
},
"cds": {
"requires": {
"messaging": true
"messaging": true,
"queue": {
"maxAttempts": 6
}
},
"log": {
"levels": {
"odata": "error",
"cds": "debug"
}
}
},
"license": "Apache-2.0"
Expand Down
8 changes: 6 additions & 2 deletions srv/data-federation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const cds = require ('@sap/cds')
const feed = []
const LOG = cds.log()

// Collect all entities to be federated, and prepare replica tables
cds.on ('loaded', csn => {
Expand All @@ -18,7 +19,10 @@ cds.on ('loaded', csn => {
cds.once ('served', () => Promise.all (feed.map (async each => {
const srv = await cds.connect.to (each.remote)
srv._once ??=!! srv.on ('replicate', replicate)
await srv.schedule ('replicate', each) .as (`replicate ${each.entity}`) .every ('10 minutes')
await srv
.schedule ('replicate', each)
.as (`replicate ${each.entity}`)
.every ('10 minutes')
})))

// Event handler for replicating single entities
Expand All @@ -27,7 +31,7 @@ async function replicate (req) {
let { latest } = await SELECT.one `max(modifiedAt) as latest` .from (entity)
let rows = await remote.read (entity) .where `modifiedAt > ${latest||0}`
if (rows.length) await UPSERT (rows) .into (entity); else return
console.log ('Replicated', rows.length, 'entries', { for: entity, via: this.kind }) // eslint-disable-line no-console
LOG.info('Replicated', rows.length, 'entries', { for: entity, via: this.kind }) // eslint-disable-line no-console
}

// Helpers to identify remote services, and check whether they are connected
Expand Down
9 changes: 7 additions & 2 deletions srv/travel-service/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class TravelService extends cds.ApplicationService {
* Integrates with the XFlights service to keep Flights data in sync on both sides.
*/
async service_integration() {
const LOG = cds.log()

const s4 = await cds.connect.to ('sap.capire.s4.business-partner')
const xflights = await cds.connect.to ('FlightsService')
Expand All @@ -33,25 +34,29 @@ class TravelService extends cds.ApplicationService {
return Promise.all (Bookings.map (booking => {
let { Flight_ID: flight, Flight_date: date, Travel_ID, Pos } = booking
// Transport Travel_ID, Pos to callback via headers
LOG.info(`[${1}] Emit BookingCreated for Flight ${flight}`)
return yfligths.emit ('BookingCreated', { flight, date }, { Travel_ID, Pos })
}))
})

// Set booking status in callback of outboxed BookingCreated event
xflights.after('BookingCreated/#succeeded', async function(_, req) {
const { Travel_ID, Pos } = req.headers
LOG.info(`[${4}] Booking Succeeded: Update Booking Status for ${Travel_ID} to 'C'`)
await UPDATE(Bookings, { Travel_ID, Pos }).set({ Status_code: 'C' })
})
xflights.after('BookingCreated/#failed', async function(err, req) {
const { Travel_ID, Pos } = req.headers
LOG.info(`[${4}] Booking Failed: Update Booking Status for ${Travel_ID} to 'F'`)
await UPDATE(Bookings, { Travel_ID, Pos }).set({ Status_code: 'F' })
})

// Update local Flights data whenever occupied seats change in XFlights
if (Flights['@cds.persistence.table']) xflights.on ('FlightsUpdated', async function(msg) {
const { flight:ID, date } = msg.data
const free_seats = await this.read(Flights, { ID, date }).columns('free_seats')
await UPDATE (Flights, { ID, date }) .with (free_seats)
const { free_seats } = await this.read(Flights, { ID, date }).columns('free_seats')
LOG.info(`[${5}] Update Flight ${ID} free seats to ${free_seats}`)
await UPDATE (Flights, { ID, date }) .with ({ free_seats })
})
}

Expand Down
30 changes: 27 additions & 3 deletions test/saga.http
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
@service = http://localhost:4004/odata/v4/travel

###

GET {{service}}/Flights(ID='GA0322',date='2023-08-05')
GET {{service}}/Flights(ID='GA0322',date='2023-08-05')
Authorization: Basic alice:

###

POST {{service}}/Travels
Authorization: Basic alice:
Content-Type: application/json
Expand All @@ -28,3 +26,29 @@ Content-Type: application/json
}
]
}


### Fully Booked

POST {{service}}/Travels
Authorization: Basic alice:
Content-Type: application/json

{
"ID": 4190,
"Description": "Test Travel Fully Booked",
"BeginDate": "2020-01-01",
"EndDate": "2030-12-31",
"Agency_ID": "070007",
"Customer_ID": "000001",
"Currency_code": "EUR",
"Bookings": [
{
"Pos": 1,
"Flight_ID": "GA0322",
"Flight_date": "2023-08-05",
"BookingDate": "2022-08-05",
"Currency_code": "EUR"
}
]
}