Day 5: Leases and Booking
Objective for Day 5
Implement leasing and booking.
Rationale
With the details of the opportunities and the majority of the response collated in Day 4, Day 5 is about ensuring that leasing and booking actually occurs.
Step 1: Ensure your database has a compatible schema
The Open Booking API includes concepts that likely map onto your existing schema, however your database schema may require slight adjustment in order to be compatible.
Step 2: Understand the StoreBookingEngine booking flow
The StoreBookingEngine
handles creation of the overall Lease or Order, as well as the booking of each OrderItem
, within a transaction.
The diagram below illustrates the abstract methods that are called by the StoreBookingEngine
, noting that:
The
OpportunityStore
used for calls toGetOrderItems
,LeaseOrderItems
andBookOrderItems
for the set ofOrderItem
s of each opportunity type is based on theOpportunityStoreRouting
configured in Day 3.The same
OrderItemContext
is passed untouched fromGetOrderItems
to eitherLeaseOrderItems
orBookOrderItems
, which allows logic to be executed inside or outside of the transaction as required.If an exception occurs after
BeginOrderTransaction
,IDatabaseTransaction.Rollback()
is called.
Step 3: Implement OrderStore
Create a new OrderStore
implementation with stub methods.
Note that all methods except for CustomerCancelOrderItems
will be implemented by the end this Day 5.
Configure the OrderStore
setting of StoreBookingEngineSettings
within EngineConfig.cs
to use this new implementation of SellerStore
:
Step 4: Implement Transactions
The OrderStore
generic type has a parameter for a custom database transaction class, which can be used to wrap an existing transaction type (such as DbContextTransaction
within entity framework), depending on your ORM or database.
Implement a new IDatabaseTransaction
class, ensuring that the class is sealed
, and that Dispose()
is implemented correctly.
Within your new OrderStore
, implement BeginOrderTransaction
to return this new class.
Entity Framework Example
Step 5: Implement Leasing
The objective of this step is to implement CreateLease
, LeaseOrderItems
, and DeleteLease
, while updating BeginOrderTransaction
if necessary.
Use CreateLease
to create an overall lease "container" for a collection of opportunities, if required by your system. CreateLease
receives an immutable flowContext
, which contains useful properties about the lease, and should contain enough data to satisfy most requirements for persisting a lease in a database. A Lease must be returned by CreateLease
in order for LeaseOrderItems
to be called.
To cater for edge cases: CreateLease
also receives a mutable responseOrderQuote
, which is the full OrderQuote
response created so far. Note that the OrderItems
will be overwritten by LeaseOrderItems
, but all other properties may be updated if required, or can be read if useful when creating the lease "container" in the database.
Note that leases are time-bound, and so must be cleaned up if they expire (e.g. on a schedule). This must be handled outside of the StoreBookingEngine
.
Use LeaseOrderItems
to lease the individual opportunities. LeaseOrderItems
follows a similar pattern to GetOrderItems
, using lists of mutable OrderItemContext
as described in Day 4. Note that as per the Open Booking API specification lease errors must not generate exceptions; instead AddError
is useful here, as in Day 4, for adding any OrderItem
level leasing errors to the response.
OrderStore.DeleteLease
is called by the OrderQuote Deletion endpoint.
The Open Booking API specification provides several options for leasing an opportunity to a customer so that it cannot be booked by anyone else while the customer is completing their booking journey. The implementation of each is supported by the StoreBookingEngine
through varying implementations of BeginOrderTransaction
, CreateLease
and DeleteLease
, as described below.
Option 1: Disable leasing
Ensure that
BeginOrderTransaction
returnsnull
, forFlowStage.C1
andFlowStage.C2
.Ensure
CreateLease
always returnsnull
, which will preventLeaseOrderItems
being triggered.LeaseOrderItems
can throwNotImplementedException
.Implement
DeleteLease
to do nothing (as the specification requires a positive response for OrderQuote Deletion, even if no lease exists).
Option 2: Named leasing - lease at C2 only
Use conditional logic to return null
from BeginOrderTransaction
and CreateLease
at FlowStage.C1
.
Implement CreateLease
to create a new lease in your database against the flowContext.OrderId.uuid
.
Implement LeaseOrderItems
for each OpportunityStore
to add the provided list of OrderItemContext
identified by their RequestBookableOpportunityOfferId
to the lease identified by flowContext.OrderId.uuid
.
Implement DeleteLease
to delete the lease based on the provided orderId.uuid
, whilst always ensuring a positive response for OrderQuote Deletion, even if no lease exists.
Note that a Lease
object must be returned from CreateLease
in order for the LeaseOrderItems
within each OpportunityStore
implementation to be executed.
Option 3: Anonymous leasing - lease at C1 and C2
Same as Option 2, without the conditional logic.
Step 6: Run Test Suite for Leases
The appropriate lease tests should pass for C1 and C2.
Anonymous leasing (C1)
The anonymous-leasing feature within the openactive-integration-tests
test suite should pass, if anonymous leasing has been implemented.
Run this test in isolation as follows:
Named leasing (C2)
The named-leasing feature within the openactive-integration-tests
test suite should pass, if anonymous leasing or named leasing has been implemented.
Run this test in isolation as follows:
Step 7: Implement Booking
The objective of this step is to implement CreateOrder
, BookOrderItems
, and DeleteOrder
, storing at least enough data to construct an Orders feed entry.
Use CreateOrder
to create the Order
that will feature in your Orders feed. CreateOrder
receives an immutable flowContext
, which contains useful properties about the Order
, and should contain enough data to satisfy most requirements for persisting an Order
in a database.
To cater for edge cases: CreateOrder
also receives a mutable responseOrder
, which is the full Order
response created so far. Note that the OrderItems
will be overwritten by BookOrderItems
, but all other properties may be updated if required, or can be read if useful when creating the Order
in the database.
Use BookOrderItems
to book the individual opportunities. BookOrderItems
follows a similar pattern to GetOrderItems
, using lists of mutable OrderItemContext
as described in Day 4. Note that to conform with the Open Booking API specification all booking errors must generate an exception; so do not use AddError
. For errors relating to capacity throw OpenBookingException
with OpportunityHasInsufficientCapacityError
or OpportunityCapacityIsReservedByLeaseError
as appropriate, or for other errors in BookOrderItems
throw OpenBookingException
with UnableToProcessOrderItemError
.
OrderItemContext
includes a method SetOrderItemId
which must be used within BookOrderItems
to set the OrderItem
Id
based on any completed bookings.
OrderStore.DeleteOrder
is called by the Order Deletion endpoint, and must soft-delete the Order
(such that it appears as "deleted" within the Orders feed).
Finally, when concurrent transactions are used to write to tables that power RPDE feeds, there is a risk of a delayed item interleaving race condition occurring. Ensure your Orders table updates (and Orders RPDE query in Day 6) implement the prevention strategies outlined here.
Step 8: Run Test Suite for Booking
The free-opportunities and non-free-opportunities features within the openactive-integration-tests
test suite should pass.
Run these tests in isolation as follows:
Last updated