Day 5: Leases and Booking
Last updated
Last updated
Implement leasing and booking.
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.
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.
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 to GetOrderItems
, LeaseOrderItems
and BookOrderItems
for the set of OrderItem
s of each opportunity type is based on the OpportunityStoreRouting
configured in Day 3.
The same OrderItemContext
is passed untouched from GetOrderItems
to either LeaseOrderItems
or BookOrderItems
, which allows logic to be executed inside or outside of the transaction as required.
If an exception occurs after BeginOrderTransaction
, IDatabaseTransaction.Rollback()
is called.
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
:
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.
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.
Ensure that BeginOrderTransaction
returns null
, for FlowStage.C1
and FlowStage.C2
.
Ensure CreateLease
always returns null
, which will prevent LeaseOrderItems
being triggered.
LeaseOrderItems
can throw NotImplementedException
.
Implement DeleteLease
to do nothing (as the specification requires a positive response for OrderQuote Deletion, even if no lease exists).
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.
Same as Option 2, without the conditional logic.
The appropriate lease tests should pass for C1 and C2.
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:
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:
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.
The free-opportunities and non-free-opportunities features within the openactive-integration-tests
test suite should pass.
Run these tests in isolation as follows:
Entity
Description
Order
A table representing the atomic successfully created Order
which is the result of B. A lease flag can also be added to this table to allow it to also represent a leased OrderQuote
(for C1 and C2) or a separate table may be used for this purpose. This table likely also includes the booker (customer
) details. This table is important as it is used to generate the Orders feed (see Day 6 for more information).
OrderItem
A table representing an individual booking of an Opportunity within an Order
. This table likely also includes the guest checkout attendee
details if these are supported. Existing "booking" or "attendee" tables may serve this purpose. Each OrderItem
represents a booked space of a 'bookable' Opportunity and Offer pair.
Opportunity
One or many tables that represent the different types of opportunity, some of which may be bookable.
Offer
A table or other data structure that represents the available Offers within each Opportunity
Seller
A table that represents Sellers, organizations or individuals who organize the events or provide the facilities. Existing "organisation" tables may serve this purpose. This is only required if the booking system is multi-tenancy within the same database (i.e. it supports multiple Sellers).
AuthToken
This is likely to be managed by the authentication library, e.g. as JWT. See Day 8 for more information.
Booking Partner
This is likely to be managed by the authentication library, e.g. as a table of OAuth Clients. See Day 8 for more information.