diff --git a/.github/workflows/openactive-test-suite.yml b/.github/workflows/openactive-test-suite.yml index f72c6074..ad216f26 100644 --- a/.github/workflows/openactive-test-suite.yml +++ b/.github/workflows/openactive-test-suite.yml @@ -84,6 +84,8 @@ jobs: dotnet run --no-launch-profile --project ./server/Examples/BookingSystem.AspNetCore/BookingSystem.AspNetCore.csproj --configuration Release --no-build & env: ASPNETCORE_ENVIRONMENT: ${{ matrix.profile }} + USE_REMOTE_STORAGE: false + DROP_TABLES_ON_RESTART: true - name: Install OpenActive Test Suite run: npm install working-directory: tests diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/BookingSystem.AspNetCore.IdentityServer.csproj b/Examples/BookingSystem.AspNetCore.IdentityServer/BookingSystem.AspNetCore.IdentityServer.csproj index fb12cf38..35b22b0c 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/BookingSystem.AspNetCore.IdentityServer.csproj +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/BookingSystem.AspNetCore.IdentityServer.csproj @@ -11,6 +11,9 @@ + + + diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/BookingPartners/BookingPartnersController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/BookingPartners/BookingPartnersController.cs index 21610d83..2de325c3 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/BookingPartners/BookingPartnersController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/BookingPartners/BookingPartnersController.cs @@ -109,7 +109,7 @@ public async Task Remove([FromForm] string clientId) public async Task Delete([FromForm] string clientId) { await _interaction.RevokeUserConsentAsync(clientId); - await FakeBookingSystem.Database.DeleteBookingPartner(clientId); + await FakeBookingSystem.FakeDatabase.DeleteBookingPartner(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return Redirect("/booking-partners/sys-admin"); diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs index 4ec608f4..46d11464 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs @@ -22,7 +22,7 @@ public async Task> GetAllAsync(PersistedGrantFilter { filter.Validate(); - var grants = await FakeBookingSystem.Database.GetAllGrants(filter.SubjectId, filter.SessionId, filter.ClientId, filter.Type); + var grants = await FakeBookingSystem.FakeDatabase.GetAllGrants(filter.SubjectId, filter.SessionId, filter.ClientId, filter.Type); _logger.LogDebug("{persistedGrantCount} persisted grants found for {@filter}", grants.Count, filter); @@ -44,7 +44,7 @@ public async Task> GetAllAsync(PersistedGrantFilter public async Task GetAsync(string key) { - var grant = await FakeBookingSystem.Database.GetGrant(key); + var grant = await FakeBookingSystem.FakeDatabase.GetGrant(key); _logger.LogDebug("{persistedGrantKey} found in database: {persistedGrantKeyFound}", key, grant != null); @@ -68,19 +68,19 @@ public async Task RemoveAllAsync(PersistedGrantFilter filter) _logger.LogDebug("removing all persisted grants from database for {@filter}", filter); - await FakeBookingSystem.Database.RemoveAllGrants(filter.SubjectId, filter.SessionId, filter.ClientId, filter.Type); + await FakeBookingSystem.FakeDatabase.RemoveAllGrants(filter.SubjectId, filter.SessionId, filter.ClientId, filter.Type); } public async Task RemoveAsync(string key) { _logger.LogDebug("removing {persistedGrantKey} persisted grant from database", key); - await FakeBookingSystem.Database.RemoveGrant(key); + await FakeBookingSystem.FakeDatabase.RemoveGrant(key); } public async Task StoreAsync(PersistedGrant grant) { - if (await FakeBookingSystem.Database.AddGrant(grant.Key, grant.Type, grant.SubjectId, grant.SessionId, grant.ClientId, grant.CreationTime, grant.ConsumedTime, grant.Expiration, grant.Data)) + if (await FakeBookingSystem.FakeDatabase.AddGrant(grant.Key, grant.Type, grant.SubjectId, grant.SessionId, grant.ClientId, grant.CreationTime, grant.ConsumedTime, grant.Expiration, grant.Data)) { _logger.LogDebug("{persistedGrantKey} not found in database, and so was inserted", grant.Key); } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs index 2009a221..c0ff7581 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs @@ -16,19 +16,19 @@ public UserRepository(string jsonLdIdBaseUrl) public Task ValidateCredentials(string username, string password) { - return FakeBookingSystem.Database.ValidateSellerUserCredentials(username, password); + return FakeBookingSystem.FakeDatabase.ValidateSellerUserCredentials(username, password); } public async Task FindBySubjectId(string subjectId) { return long.TryParse(subjectId, out var longSubjectId) - ? GetUserFromSellerUserWithClaims(await FakeBookingSystem.Database.GetSellerUserById(longSubjectId)) + ? GetUserFromSellerUserWithClaims(await FakeBookingSystem.FakeDatabase.GetSellerUserById(longSubjectId)) : null; } public async Task FindByUsername(string username) { - return GetUserFromSellerUser(await FakeBookingSystem.Database.GetSellerUser(username)); + return GetUserFromSellerUser(await FakeBookingSystem.FakeDatabase.GetSellerUser(username)); } // TODO: Make this an extension method diff --git a/Examples/BookingSystem.AspNetCore/BackgroundServices/FakeDataRefresherService.cs b/Examples/BookingSystem.AspNetCore/BackgroundServices/FakeDataRefresherService.cs new file mode 100644 index 00000000..1a75a2d0 --- /dev/null +++ b/Examples/BookingSystem.AspNetCore/BackgroundServices/FakeDataRefresherService.cs @@ -0,0 +1,44 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using OpenActive.FakeDatabase.NET; + + +namespace BookingSystem +{ + // Background task + // More information: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice#implementing-ihostedservice-with-a-custom-hosted-service-class-deriving-from-the-backgroundservice-base-class + public class FakeDataRefresherService : BackgroundService + { + private readonly ILogger _logger; + private readonly AppSettings _settings; + + public FakeDataRefresherService(AppSettings settings, ILogger logger) + { + _settings = settings; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogDebug($"FakeDataRefresherService is starting.."); + + stoppingToken.Register(() => + _logger.LogDebug($"FakeDataRefresherService background task is stopping.")); + + while (!stoppingToken.IsCancellationRequested) + { + await FakeBookingSystem.FakeDatabase.HardDeletedOldSoftDeletedOccurrencesAndSlots(); + _logger.LogDebug($"FakeDataRefresherService hard deleted opportunities that were previously old and soft deleted"); + + await FakeBookingSystem.FakeDatabase.SoftDeletedPastOpportunitiesAndInsertNewAtEdgeOfWindow(); + _logger.LogDebug($"FakeDataRefresherService soft deleted opportunities and inserted new ones at edge of window."); + + _logger.LogDebug($"FakeDataRefresherService is finished"); + await Task.Delay(_settings.DataRefresherInterval, stoppingToken); + } + } + } +} diff --git a/Examples/BookingSystem.AspNetCore/BookingSystem.AspNetCore.csproj b/Examples/BookingSystem.AspNetCore/BookingSystem.AspNetCore.csproj index e02b4905..af70eefe 100644 --- a/Examples/BookingSystem.AspNetCore/BookingSystem.AspNetCore.csproj +++ b/Examples/BookingSystem.AspNetCore/BookingSystem.AspNetCore.csproj @@ -3,9 +3,21 @@ netcoreapp3.1 aspnet-BookingSystem.AspNetCore-443B4F82-A20C-41CE-9924-329A0BCF0D14 + + + all + true + + + + + + + @@ -16,6 +28,7 @@ + 1701;1702;1591 diff --git a/Examples/BookingSystem.AspNetCore/Feeds/FacilitiesFeeds.cs b/Examples/BookingSystem.AspNetCore/Feeds/FacilitiesFeeds.cs index caa10fbb..8a0ead09 100644 --- a/Examples/BookingSystem.AspNetCore/Feeds/FacilitiesFeeds.cs +++ b/Examples/BookingSystem.AspNetCore/Feeds/FacilitiesFeeds.cs @@ -24,7 +24,10 @@ public AcmeFacilityUseRpdeGenerator(AppSettings appSettings) protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + var facilityTypeId = Environment.GetEnvironmentVariable("FACILITY_TYPE_ID") ?? "https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"; + var facilityTypePrefLabel = Environment.GetEnvironmentVariable("FACILITY_TYPE_PREF_LABEL") ?? "Squash Court"; + + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var q = db.From() .Join() @@ -107,8 +110,8 @@ protected override async Task>> GetRpdeItems(long? af FacilityType = new List { new Concept { - Id = new Uri("https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"), - PrefLabel = "Squash Court", + Id = new Uri(facilityTypeId), + PrefLabel = facilityTypePrefLabel, InScheme = new Uri("https://openactive.io/facility-types") } } @@ -133,7 +136,7 @@ public AcmeFacilityUseSlotRpdeGenerator(AppSettings appSettings) protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var query = db.Select() .OrderBy(x => x.Modified) diff --git a/Examples/BookingSystem.AspNetCore/Feeds/OrdersFeed.cs b/Examples/BookingSystem.AspNetCore/Feeds/OrdersFeed.cs index 6fe7d2e2..53784c9e 100644 --- a/Examples/BookingSystem.AspNetCore/Feeds/OrdersFeed.cs +++ b/Examples/BookingSystem.AspNetCore/Feeds/OrdersFeed.cs @@ -27,7 +27,7 @@ protected override async Task> GetRPDEItems(string clientId, long // and update this class to inherit from OrdersRPDEFeedIncrementingUniqueChangeNumber // (to use afterChangeNumber, instead of afterTimestamp and afterId) - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { long afterTimestampLong = afterTimestamp ?? 0; var q = db.From() @@ -126,7 +126,7 @@ public AcmeOrderProposalsFeedRpdeGenerator(AppSettings appSettings) protected override async Task> GetRPDEItems(string clientId, long? afterTimestamp, string afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { long afterTimestampLong = afterTimestamp ?? 0; var q = db.From() diff --git a/Examples/BookingSystem.AspNetCore/Feeds/SessionsFeeds.cs b/Examples/BookingSystem.AspNetCore/Feeds/SessionsFeeds.cs index 5d0e2c1b..8ea2243f 100644 --- a/Examples/BookingSystem.AspNetCore/Feeds/SessionsFeeds.cs +++ b/Examples/BookingSystem.AspNetCore/Feeds/SessionsFeeds.cs @@ -17,7 +17,7 @@ public class AcmeScheduledSessionRpdeGenerator : RpdeFeedModifiedTimestampAndIdL protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var query = db.Select() .OrderBy(x => x.Modified) @@ -74,7 +74,10 @@ public AcmeSessionSeriesRpdeGenerator(AppSettings appSettings) protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + var activityId = Environment.GetEnvironmentVariable("ACTIVITY_ID") ?? "https://openactive.io/activity-list#c07d63a0-8eb9-4602-8bcc-23be6deb8f83"; + var activityPrefLabel = Environment.GetEnvironmentVariable("ACTIVITY_PREF_LABEL") ?? "Jet Skiing"; + + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var q = db.From() .Join() @@ -199,8 +202,8 @@ protected override async Task>> GetRpdeItems(long? { new Concept { - Id = new Uri("https://openactive.io/activity-list#c07d63a0-8eb9-4602-8bcc-23be6deb8f83"), - PrefLabel = "Jet Skiing", + Id = new Uri(activityId), + PrefLabel = activityPrefLabel, InScheme = new Uri("https://openactive.io/activity-list") } } diff --git a/Examples/BookingSystem.AspNetCore/Program.cs b/Examples/BookingSystem.AspNetCore/Program.cs index a6cf93f6..c8aac90d 100644 --- a/Examples/BookingSystem.AspNetCore/Program.cs +++ b/Examples/BookingSystem.AspNetCore/Program.cs @@ -8,13 +8,18 @@ public class Program { public static void Main(string[] args) { - // Initialising fake database (shared with IdentityServer) + var host = CreateWebHostBuilder(args) + .Build(); + FakeBookingSystem.Initialise(); - CreateWebHostBuilder(args).Build().Run(); + + host.Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) - .UseStartup(); + .CaptureStartupErrors(true) + .UseSetting("detailedErrors", "true") + .UseStartup(); } } diff --git a/Examples/BookingSystem.AspNetCore/Properties/launchSettings.json b/Examples/BookingSystem.AspNetCore/Properties/launchSettings.json index 30b0586b..67a8fb1f 100644 --- a/Examples/BookingSystem.AspNetCore/Properties/launchSettings.json +++ b/Examples/BookingSystem.AspNetCore/Properties/launchSettings.json @@ -11,7 +11,11 @@ "applicationUrl": "https://localhost:5001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "ApplicationHostBaseUrl": "https://localhost:5001" + "ApplicationHostBaseUrl": "https://localhost:5001", + "REMOTE_STORAGE_CONNECTION_STRING": "", + "USE_REMOTE_STORAGE": "true", + "DROP_TABLES_ON_RESTART": "true", + "OPPORTUNITY_COUNT": "20" } } } diff --git a/Examples/BookingSystem.AspNetCore/Settings/AppSettings.cs b/Examples/BookingSystem.AspNetCore/Settings/AppSettings.cs index 6ead5201..29694da0 100644 --- a/Examples/BookingSystem.AspNetCore/Settings/AppSettings.cs +++ b/Examples/BookingSystem.AspNetCore/Settings/AppSettings.cs @@ -1,3 +1,5 @@ +using System; + namespace BookingSystem { public class AppSettings @@ -6,6 +8,8 @@ public class AppSettings public string OpenIdIssuerUrl { get; set; } public FeatureSettings FeatureFlags { get; set; } public PaymentSettings Payment { get; set; } + public TimeSpan DataRefresherInterval = TimeSpan.FromHours(6); + } /** diff --git a/Examples/BookingSystem.AspNetCore/Startup.cs b/Examples/BookingSystem.AspNetCore/Startup.cs index da372800..80a68a79 100644 --- a/Examples/BookingSystem.AspNetCore/Startup.cs +++ b/Examples/BookingSystem.AspNetCore/Startup.cs @@ -60,7 +60,8 @@ public void ConfigureServices(IServiceCollection services) services.AddAuthorization(options => { // No authorization checks are performed, this just ensures that the required claims are supplied - options.AddPolicy(OpenActiveScopes.OpenBooking, policy => { + options.AddPolicy(OpenActiveScopes.OpenBooking, policy => + { policy.RequireClaim(OpenActiveCustomClaimNames.ClientId); policy.RequireClaim(OpenActiveCustomClaimNames.SellerId); }); @@ -72,7 +73,13 @@ public void ConfigureServices(IServiceCollection services) .AddControllers() .AddMvcOptions(options => options.InputFormatters.Insert(0, new OpenBookingInputFormatter())); + // Add config as a singleton to pipe it through DI to the booking engine and stores + services.AddSingleton(x => AppSettings); + services.AddSingleton(sp => EngineConfig.CreateStoreBookingEngine(AppSettings)); + + // Add background OrderItem polling + services.AddHostedService(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -93,7 +100,6 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); - app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/Examples/BookingSystem.AspNetCore/Stores/FacilityStore.cs b/Examples/BookingSystem.AspNetCore/Stores/FacilityStore.cs index ef3e865c..2adaf1cb 100644 --- a/Examples/BookingSystem.AspNetCore/Stores/FacilityStore.cs +++ b/Examples/BookingSystem.AspNetCore/Stores/FacilityStore.cs @@ -44,7 +44,7 @@ protected override async Task CreateOpportunityWithinTestDa { case TestOpportunityCriteriaEnumeration.TestOpportunityBookable: case TestOpportunityCriteriaEnumeration.TestOpportunityOfflineBookable: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility", @@ -55,7 +55,7 @@ protected override async Task CreateOpportunityWithinTestDa case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellable: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFree: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableUsingPayment: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility", @@ -64,7 +64,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFree: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility", @@ -76,7 +76,7 @@ protected override async Task CreateOpportunityWithinTestDa case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOutsideValidFromBeforeStartDate: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithinValidFromBeforeStartDate; - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility {(isValid ? "Within" : "Outside")} Window", @@ -90,7 +90,7 @@ protected override async Task CreateOpportunityWithinTestDa case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableOutsideWindow: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableWithinWindow; - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility {(isValid ? "Within" : "Outside")} Cancellation Window", @@ -101,7 +101,7 @@ protected override async Task CreateOpportunityWithinTestDa } break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentOptional: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Prepayment Optional", @@ -111,7 +111,7 @@ protected override async Task CreateOpportunityWithinTestDa prepayment: RequiredStatusType.Optional); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentUnavailable: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Prepayment Unavailable", @@ -121,7 +121,7 @@ protected override async Task CreateOpportunityWithinTestDa prepayment: RequiredStatusType.Unavailable); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentRequired: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Prepayment Required", @@ -131,7 +131,7 @@ protected override async Task CreateOpportunityWithinTestDa prepayment: RequiredStatusType.Required); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNoSpaces: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility No Spaces", @@ -140,7 +140,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFiveSpaces: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility Five Spaces", @@ -149,7 +149,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOneSpace: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility One Space", @@ -158,7 +158,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxNet: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 2, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Tax Net", @@ -167,7 +167,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxGross: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Tax Gross", @@ -176,7 +176,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableSellerTermsOfService: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility With Seller Terms Of Service", @@ -185,7 +185,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAttendeeDetails: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility That Requires Attendee Details", @@ -195,7 +195,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresAttendeeValidation: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAdditionalDetails: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility That Requires Additional Details", @@ -205,7 +205,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresAdditionalDetails: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithNegotiation: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility That Allows Proposal Amendment", @@ -215,7 +215,7 @@ protected override async Task CreateOpportunityWithinTestDa allowProposalAmendment: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNotCancellable: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility Paid That Does Not Allow Full Refund", @@ -225,7 +225,7 @@ protected override async Task CreateOpportunityWithinTestDa allowCustomerCancellationFullRefund: false); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableInPast: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility in the Past", @@ -252,7 +252,7 @@ protected override async Task CreateOpportunityWithinTestDa protected override async Task DeleteTestDataset(string testDatasetIdentifier) { - await FakeBookingSystem.Database.DeleteTestFacilitiesFromDataset(testDatasetIdentifier); + await FakeBookingSystem.FakeDatabase.DeleteTestFacilitiesFromDataset(testDatasetIdentifier); } protected override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, FacilityOpportunity idComponents) @@ -260,19 +260,19 @@ protected override async Task TriggerTestAction(OpenBookingSimulateAction simula switch (simulateAction) { case ChangeOfLogisticsTimeSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateFacilitySlotStartAndEndTimeByPeriodInMins(idComponents.SlotId.Value, 60)) + if (!await FakeBookingSystem.FakeDatabase.UpdateFacilitySlotStartAndEndTimeByPeriodInMins(idComponents.SlotId.Value, 60)) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsNameSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateFacilityUseName(idComponents.SlotId.Value, "Updated Facility Title")) + if (!await FakeBookingSystem.FakeDatabase.UpdateFacilityUseName(idComponents.SlotId.Value, "Updated Facility Title")) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsLocationSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateFacilityUseLocationLatLng(idComponents.SlotId.Value, 0.2m, 0.3m)) + if (!await FakeBookingSystem.FakeDatabase.UpdateFacilityUseLocationLatLng(idComponents.SlotId.Value, 0.2m, 0.3m)) { throw new OpenBookingException(new UnknownOpportunityError()); } @@ -286,6 +286,9 @@ protected override async Task TriggerTestAction(OpenBookingSimulateAction simula // Similar to the RPDE logic, this needs to render and return an new hypothetical OrderItem from the database based on the supplied opportunity IDs protected override async Task GetOrderItems(List> orderItemContexts, StoreBookingFlowContext flowContext, OrderStateContext stateContext) { + var facilityTypeId = Environment.GetEnvironmentVariable("FACILITY_TYPE_ID") ?? "https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"; + var facilityTypePrefLabel = Environment.GetEnvironmentVariable("FACILITY_TYPE_PREF_LABEL") ?? "Squash Court"; + // Note the implementation of this method must also check that this OrderItem is from the Seller specified by context.SellerId (this is not required if using a Single Seller) // Additionally this method must check that there are enough spaces in each entry @@ -294,13 +297,13 @@ protected override async Task GetOrderItems(List { - var getOccurrenceInfoResult = await FakeBookingSystem.Database.GetSlotAndBookedOrderItemInfoBySlotId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); + var getOccurrenceInfoResult = await FakeBookingSystem.FakeDatabase.GetSlotAndBookedOrderItemInfoBySlotId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); var (hasFoundOccurrence, facility, slot, bookedOrderItemInfo) = getOccurrenceInfoResult; if (hasFoundOccurrence == false) { return null; } - var remainingUsesIncludingOtherLeases = await FakeBookingSystem.Database.GetNumberOfOtherLeasesForSlot(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); + var remainingUsesIncludingOtherLeases = await FakeBookingSystem.FakeDatabase.GetNumberOfOtherLeasesForSlot(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); return new { @@ -349,8 +352,8 @@ protected override async Task GetOrderItems(List { new Concept { - Id = new Uri("https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"), - PrefLabel = "Squash Court", + Id = new Uri(facilityTypeId), + PrefLabel = facilityTypePrefLabel, InScheme = new Uri("https://openactive.io/facility-types") } } diff --git a/Examples/BookingSystem.AspNetCore/Stores/OrderStore.cs b/Examples/BookingSystem.AspNetCore/Stores/OrderStore.cs index f7cbde2f..f5b5d5c6 100644 --- a/Examples/BookingSystem.AspNetCore/Stores/OrderStore.cs +++ b/Examples/BookingSystem.AspNetCore/Stores/OrderStore.cs @@ -35,7 +35,7 @@ public override async Task CustomerCancelOrderItems(OrderIdComponents orde { try { - return await FakeBookingSystem.Database.CancelOrderItems( + return await FakeBookingSystem.FakeDatabase.CancelOrderItems( orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, @@ -54,7 +54,7 @@ public override async Task CustomerCancelOrderItems(OrderIdComponents orde /// True if OrderProposal found, False if OrderProposal not found public override async Task CustomerRejectOrderProposal(OrderIdComponents orderId, SimpleIdComponents sellerId) { - return await FakeBookingSystem.Database.RejectOrderProposal(orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, true); + return await FakeBookingSystem.FakeDatabase.RejectOrderProposal(orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, true); } public override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, OrderIdComponents orderId) @@ -66,7 +66,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } - if (!await FakeBookingSystem.Database.AcceptOrderProposal(orderId.uuid)) + if (!await FakeBookingSystem.FakeDatabase.AcceptOrderProposal(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -77,7 +77,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } var version = Guid.NewGuid(); - if (!await FakeBookingSystem.Database.AmendOrderProposal(orderId.uuid, version)) + if (!await FakeBookingSystem.FakeDatabase.AmendOrderProposal(orderId.uuid, version)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -87,7 +87,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } - if (!await FakeBookingSystem.Database.RejectOrderProposal(null, null, orderId.uuid, false)) + if (!await FakeBookingSystem.FakeDatabase.RejectOrderProposal(null, null, orderId.uuid, false)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -97,7 +97,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.CancelOrderItems(null, null, orderId.uuid, null, false, true)) + if (!await FakeBookingSystem.FakeDatabase.CancelOrderItems(null, null, orderId.uuid, null, false, true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -107,7 +107,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.CancelOrderItems(null, null, orderId.uuid, null, false)) + if (!await FakeBookingSystem.FakeDatabase.CancelOrderItems(null, null, orderId.uuid, null, false)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -117,7 +117,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessCode: true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateAccess(orderId.uuid, updateAccessCode: true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -127,7 +127,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessPass: true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateAccess(orderId.uuid, updateAccessPass: true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -137,7 +137,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateOpportunityAttendance(orderId.uuid, true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateOpportunityAttendance(orderId.uuid, true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -147,7 +147,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateOpportunityAttendance(orderId.uuid, false)) + if (!await FakeBookingSystem.FakeDatabase.UpdateOpportunityAttendance(orderId.uuid, false)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -157,7 +157,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.AddCustomerNotice(orderId.uuid)) + if (!await FakeBookingSystem.FakeDatabase.AddCustomerNotice(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -167,7 +167,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.ReplaceOrderOpportunity(orderId.uuid)) + if (!await FakeBookingSystem.FakeDatabase.ReplaceOrderOpportunity(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -177,7 +177,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessChannel: true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateAccess(orderId.uuid, updateAccessChannel: true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -267,7 +267,7 @@ public override ValueTask UpdateLease( public override async Task DeleteLease(OrderIdComponents orderId, SimpleIdComponents sellerId) { // Note if no lease support, simply do nothing here - await FakeBookingSystem.Database.DeleteLease( + await FakeBookingSystem.FakeDatabase.DeleteLease( orderId.ClientId, orderId.uuid, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */ @@ -358,7 +358,7 @@ public override ValueTask UpdateOrderProposal(OrderProposal responseOrderProposa public override async Task DeleteOrder(OrderIdComponents orderId, SimpleIdComponents sellerId) { - var result = await FakeBookingSystem.Database.DeleteOrder( + var result = await FakeBookingSystem.FakeDatabase.DeleteOrder( orderId.ClientId, orderId.uuid, sellerId.IdLong ?? null /* Small hack to allow use of FakeDatabase when in Single Seller mode */); @@ -382,7 +382,7 @@ public override async Task CreateOrderFromOrderProposal(OrderIdComponents // TODO more elegantly extract version UUID from orderProposalVersion (probably much further up the stack?) var version = new Guid(orderProposalVersion.ToString().Split('/').Last()); - var result = await FakeBookingSystem.Database.BookOrderProposal( + var result = await FakeBookingSystem.FakeDatabase.BookOrderProposal( orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, @@ -442,7 +442,7 @@ public static Order RenderOrderFromDatabaseResult(Uri orderId, OrderTable dbOrde public override async Task GetOrderStatus(OrderIdComponents orderId, SimpleIdComponents sellerId, ILegalEntity seller) { - var (getOrderResult, dbOrder, dbOrderItems) = await FakeBookingSystem.Database.GetOrderAndOrderItems( + var (getOrderResult, dbOrder, dbOrderItems) = await FakeBookingSystem.FakeDatabase.GetOrderAndOrderItems( orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid); diff --git a/Examples/BookingSystem.AspNetCore/Stores/OrderTransaction.cs b/Examples/BookingSystem.AspNetCore/Stores/OrderTransaction.cs index 6d433a54..d2759641 100644 --- a/Examples/BookingSystem.AspNetCore/Stores/OrderTransaction.cs +++ b/Examples/BookingSystem.AspNetCore/Stores/OrderTransaction.cs @@ -10,7 +10,7 @@ public sealed class OrderTransaction : IDatabaseTransaction public OrderTransaction() { - FakeDatabaseTransaction = new FakeDatabaseTransaction(FakeBookingSystem.Database); + FakeDatabaseTransaction = new FakeDatabaseTransaction(FakeBookingSystem.FakeDatabase); } public async ValueTask Commit() diff --git a/Examples/BookingSystem.AspNetCore/Stores/SellerStore.cs b/Examples/BookingSystem.AspNetCore/Stores/SellerStore.cs index 5dda02fa..7f8be1bb 100644 --- a/Examples/BookingSystem.AspNetCore/Stores/SellerStore.cs +++ b/Examples/BookingSystem.AspNetCore/Stores/SellerStore.cs @@ -57,7 +57,7 @@ protected override async ValueTask GetSeller(SimpleIdComponents se } // Otherwise it may be looked up based on supplied sellerIdComponents which are extracted from the sellerId. - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var seller = db.SingleById(sellerIdComponents.IdLong); if (seller == null) diff --git a/Examples/BookingSystem.AspNetCore/Stores/SessionStore.cs b/Examples/BookingSystem.AspNetCore/Stores/SessionStore.cs index a71a0976..024d6e4c 100644 --- a/Examples/BookingSystem.AspNetCore/Stores/SessionStore.cs +++ b/Examples/BookingSystem.AspNetCore/Stores/SessionStore.cs @@ -44,7 +44,7 @@ protected override async Task CreateOpportunityWithinTestDat switch (criteria) { case TestOpportunityCriteriaEnumeration.TestOpportunityBookable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Event", @@ -55,7 +55,7 @@ protected override async Task CreateOpportunityWithinTestDat case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellable: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFree: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableUsingPayment: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event", @@ -67,7 +67,7 @@ protected override async Task CreateOpportunityWithinTestDat case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOutsideValidFromBeforeStartDate: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithinValidFromBeforeStartDate; - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event {(isValid ? "Within" : "Outside")} Window", @@ -81,7 +81,7 @@ protected override async Task CreateOpportunityWithinTestDat case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableOutsideWindow: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableWithinWindow; - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event {(isValid ? "Within" : "Outside")} Cancellation Window", @@ -92,7 +92,7 @@ protected override async Task CreateOpportunityWithinTestDat } break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentOptional: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Prepayment Optional", @@ -102,7 +102,7 @@ protected override async Task CreateOpportunityWithinTestDat prepayment: RequiredStatusType.Optional); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentUnavailable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Prepayment Unavailable", @@ -112,7 +112,7 @@ protected override async Task CreateOpportunityWithinTestDat prepayment: RequiredStatusType.Unavailable); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentRequired: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Prepayment Required", @@ -122,7 +122,7 @@ protected override async Task CreateOpportunityWithinTestDat prepayment: RequiredStatusType.Required); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFree: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event", @@ -131,7 +131,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNoSpaces: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event No Spaces", @@ -140,7 +140,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFiveSpaces: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event Five Spaces", @@ -149,7 +149,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOneSpace: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event One Space", @@ -158,7 +158,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxNet: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, 2, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Tax Net", @@ -167,7 +167,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxGross: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Tax Gross", @@ -176,7 +176,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableSellerTermsOfService: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Event With Seller Terms Of Service", @@ -185,7 +185,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAttendeeDetails: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event That Requires Attendee Details", @@ -195,7 +195,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresAttendeeValidation: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAdditionalDetails: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event That Requires Additional Details", @@ -205,7 +205,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresAdditionalDetails: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityOnlineBookable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Virtual Event", @@ -215,7 +215,7 @@ protected override async Task CreateOpportunityWithinTestDat isOnlineOrMixedAttendanceMode: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityOfflineBookable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Offline Event", @@ -225,7 +225,7 @@ protected override async Task CreateOpportunityWithinTestDat isOnlineOrMixedAttendanceMode: false); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithNegotiation: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event That Allows Proposal Amendment", @@ -235,7 +235,7 @@ protected override async Task CreateOpportunityWithinTestDat allowProposalAmendment: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNotCancellable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid That Does Not Allow Full Refund", @@ -245,7 +245,7 @@ protected override async Task CreateOpportunityWithinTestDat allowCustomerCancellationFullRefund: false); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableInPast: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Sesssion in the Past", @@ -272,7 +272,7 @@ protected override async Task CreateOpportunityWithinTestDat protected override async Task DeleteTestDataset(string testDatasetIdentifier) { - await FakeBookingSystem.Database.DeleteTestClassesFromDataset(testDatasetIdentifier); + await FakeBookingSystem.FakeDatabase.DeleteTestClassesFromDataset(testDatasetIdentifier); } protected override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, SessionOpportunity idComponents) @@ -280,19 +280,19 @@ protected override async Task TriggerTestAction(OpenBookingSimulateAction simula switch (simulateAction) { case ChangeOfLogisticsTimeSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateScheduledSessionStartAndEndTimeByPeriodInMins(idComponents.ScheduledSessionId.Value, 60)) + if (!await FakeBookingSystem.FakeDatabase.UpdateScheduledSessionStartAndEndTimeByPeriodInMins(idComponents.ScheduledSessionId.Value, 60)) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsNameSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateClassTitle(idComponents.ScheduledSessionId.Value, "Updated Class Title")) + if (!await FakeBookingSystem.FakeDatabase.UpdateClassTitle(idComponents.ScheduledSessionId.Value, "Updated Class Title")) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsLocationSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateSessionSeriesLocationLatLng(idComponents.ScheduledSessionId.Value, 0.2m, 0.3m)) + if (!await FakeBookingSystem.FakeDatabase.UpdateSessionSeriesLocationLatLng(idComponents.ScheduledSessionId.Value, 0.2m, 0.3m)) { throw new OpenBookingException(new UnknownOpportunityError()); } @@ -314,14 +314,14 @@ protected override async Task GetOrderItems(List { - var getOccurrenceResultAndRows = await FakeBookingSystem.Database.GetOccurrenceAndBookedOrderItemInfoByOccurrenceId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); + var getOccurrenceResultAndRows = await FakeBookingSystem.FakeDatabase.GetOccurrenceAndBookedOrderItemInfoByOccurrenceId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); var (hasFoundOccurrence, @class, occurrence, bookedOrderItemInfo) = getOccurrenceResultAndRows; if (hasFoundOccurrence == false) { return null; } - var remainingUsesIncludingOtherLeases = await FakeBookingSystem.Database.GetNumberOfOtherLeaseForOccurrence(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); + var remainingUsesIncludingOtherLeases = await FakeBookingSystem.FakeDatabase.GetNumberOfOtherLeaseForOccurrence(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); return new { diff --git a/Examples/BookingSystem.AspNetFramework/BookingSystem.AspNetFramework.csproj b/Examples/BookingSystem.AspNetFramework/BookingSystem.AspNetFramework.csproj index c89ec8a1..da7eae36 100644 --- a/Examples/BookingSystem.AspNetFramework/BookingSystem.AspNetFramework.csproj +++ b/Examples/BookingSystem.AspNetFramework/BookingSystem.AspNetFramework.csproj @@ -377,6 +377,19 @@ True ..\..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll + + ..\..\packages\ServiceStack.Interfaces.5.11.0\lib\net45\ServiceStack.Interfaces.dll + + + ..\..\packages\ServiceStack.Text.5.11.0\lib\net45\ServiceStack.Text.dll + + + ..\..\packages\ServiceStack.Common.5.11.0\lib\net45\ServiceStack.Common.dll + + + ..\..\packages\ServiceStack.OrmLite.5.11.0\lib\net45\ServiceStack.OrmLite.dll + + diff --git a/Examples/BookingSystem.AspNetFramework/Feeds/FacilitiesFeeds.cs b/Examples/BookingSystem.AspNetFramework/Feeds/FacilitiesFeeds.cs index caa10fbb..8a0ead09 100644 --- a/Examples/BookingSystem.AspNetFramework/Feeds/FacilitiesFeeds.cs +++ b/Examples/BookingSystem.AspNetFramework/Feeds/FacilitiesFeeds.cs @@ -24,7 +24,10 @@ public AcmeFacilityUseRpdeGenerator(AppSettings appSettings) protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + var facilityTypeId = Environment.GetEnvironmentVariable("FACILITY_TYPE_ID") ?? "https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"; + var facilityTypePrefLabel = Environment.GetEnvironmentVariable("FACILITY_TYPE_PREF_LABEL") ?? "Squash Court"; + + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var q = db.From() .Join() @@ -107,8 +110,8 @@ protected override async Task>> GetRpdeItems(long? af FacilityType = new List { new Concept { - Id = new Uri("https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"), - PrefLabel = "Squash Court", + Id = new Uri(facilityTypeId), + PrefLabel = facilityTypePrefLabel, InScheme = new Uri("https://openactive.io/facility-types") } } @@ -133,7 +136,7 @@ public AcmeFacilityUseSlotRpdeGenerator(AppSettings appSettings) protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var query = db.Select() .OrderBy(x => x.Modified) diff --git a/Examples/BookingSystem.AspNetFramework/Feeds/OrdersFeed.cs b/Examples/BookingSystem.AspNetFramework/Feeds/OrdersFeed.cs index 6fe7d2e2..53784c9e 100644 --- a/Examples/BookingSystem.AspNetFramework/Feeds/OrdersFeed.cs +++ b/Examples/BookingSystem.AspNetFramework/Feeds/OrdersFeed.cs @@ -27,7 +27,7 @@ protected override async Task> GetRPDEItems(string clientId, long // and update this class to inherit from OrdersRPDEFeedIncrementingUniqueChangeNumber // (to use afterChangeNumber, instead of afterTimestamp and afterId) - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { long afterTimestampLong = afterTimestamp ?? 0; var q = db.From() @@ -126,7 +126,7 @@ public AcmeOrderProposalsFeedRpdeGenerator(AppSettings appSettings) protected override async Task> GetRPDEItems(string clientId, long? afterTimestamp, string afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { long afterTimestampLong = afterTimestamp ?? 0; var q = db.From() diff --git a/Examples/BookingSystem.AspNetFramework/Feeds/SessionsFeeds.cs b/Examples/BookingSystem.AspNetFramework/Feeds/SessionsFeeds.cs index 5d0e2c1b..8ea2243f 100644 --- a/Examples/BookingSystem.AspNetFramework/Feeds/SessionsFeeds.cs +++ b/Examples/BookingSystem.AspNetFramework/Feeds/SessionsFeeds.cs @@ -17,7 +17,7 @@ public class AcmeScheduledSessionRpdeGenerator : RpdeFeedModifiedTimestampAndIdL protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var query = db.Select() .OrderBy(x => x.Modified) @@ -74,7 +74,10 @@ public AcmeSessionSeriesRpdeGenerator(AppSettings appSettings) protected override async Task>> GetRpdeItems(long? afterTimestamp, long? afterId) { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + var activityId = Environment.GetEnvironmentVariable("ACTIVITY_ID") ?? "https://openactive.io/activity-list#c07d63a0-8eb9-4602-8bcc-23be6deb8f83"; + var activityPrefLabel = Environment.GetEnvironmentVariable("ACTIVITY_PREF_LABEL") ?? "Jet Skiing"; + + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var q = db.From() .Join() @@ -199,8 +202,8 @@ protected override async Task>> GetRpdeItems(long? { new Concept { - Id = new Uri("https://openactive.io/activity-list#c07d63a0-8eb9-4602-8bcc-23be6deb8f83"), - PrefLabel = "Jet Skiing", + Id = new Uri(activityId), + PrefLabel = activityPrefLabel, InScheme = new Uri("https://openactive.io/activity-list") } } diff --git a/Examples/BookingSystem.AspNetFramework/Stores/FacilityStore.cs b/Examples/BookingSystem.AspNetFramework/Stores/FacilityStore.cs index ef3e865c..2adaf1cb 100644 --- a/Examples/BookingSystem.AspNetFramework/Stores/FacilityStore.cs +++ b/Examples/BookingSystem.AspNetFramework/Stores/FacilityStore.cs @@ -44,7 +44,7 @@ protected override async Task CreateOpportunityWithinTestDa { case TestOpportunityCriteriaEnumeration.TestOpportunityBookable: case TestOpportunityCriteriaEnumeration.TestOpportunityOfflineBookable: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility", @@ -55,7 +55,7 @@ protected override async Task CreateOpportunityWithinTestDa case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellable: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFree: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableUsingPayment: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility", @@ -64,7 +64,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFree: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility", @@ -76,7 +76,7 @@ protected override async Task CreateOpportunityWithinTestDa case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOutsideValidFromBeforeStartDate: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithinValidFromBeforeStartDate; - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility {(isValid ? "Within" : "Outside")} Window", @@ -90,7 +90,7 @@ protected override async Task CreateOpportunityWithinTestDa case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableOutsideWindow: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableWithinWindow; - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility {(isValid ? "Within" : "Outside")} Cancellation Window", @@ -101,7 +101,7 @@ protected override async Task CreateOpportunityWithinTestDa } break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentOptional: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Prepayment Optional", @@ -111,7 +111,7 @@ protected override async Task CreateOpportunityWithinTestDa prepayment: RequiredStatusType.Optional); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentUnavailable: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Prepayment Unavailable", @@ -121,7 +121,7 @@ protected override async Task CreateOpportunityWithinTestDa prepayment: RequiredStatusType.Unavailable); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentRequired: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Prepayment Required", @@ -131,7 +131,7 @@ protected override async Task CreateOpportunityWithinTestDa prepayment: RequiredStatusType.Required); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNoSpaces: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility No Spaces", @@ -140,7 +140,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFiveSpaces: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility Five Spaces", @@ -149,7 +149,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOneSpace: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Facility One Space", @@ -158,7 +158,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxNet: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 2, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Tax Net", @@ -167,7 +167,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxGross: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility Tax Gross", @@ -176,7 +176,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableSellerTermsOfService: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility With Seller Terms Of Service", @@ -185,7 +185,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAttendeeDetails: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility That Requires Attendee Details", @@ -195,7 +195,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresAttendeeValidation: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAdditionalDetails: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility That Requires Additional Details", @@ -205,7 +205,7 @@ protected override async Task CreateOpportunityWithinTestDa requiresAdditionalDetails: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithNegotiation: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Facility That Allows Proposal Amendment", @@ -215,7 +215,7 @@ protected override async Task CreateOpportunityWithinTestDa allowProposalAmendment: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNotCancellable: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility Paid That Does Not Allow Full Refund", @@ -225,7 +225,7 @@ protected override async Task CreateOpportunityWithinTestDa allowCustomerCancellationFullRefund: false); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableInPast: - (facilityId, slotId) = await FakeBookingSystem.Database.AddFacility( + (facilityId, slotId) = await FakeBookingSystem.FakeDatabase.AddFacility( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Facility in the Past", @@ -252,7 +252,7 @@ protected override async Task CreateOpportunityWithinTestDa protected override async Task DeleteTestDataset(string testDatasetIdentifier) { - await FakeBookingSystem.Database.DeleteTestFacilitiesFromDataset(testDatasetIdentifier); + await FakeBookingSystem.FakeDatabase.DeleteTestFacilitiesFromDataset(testDatasetIdentifier); } protected override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, FacilityOpportunity idComponents) @@ -260,19 +260,19 @@ protected override async Task TriggerTestAction(OpenBookingSimulateAction simula switch (simulateAction) { case ChangeOfLogisticsTimeSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateFacilitySlotStartAndEndTimeByPeriodInMins(idComponents.SlotId.Value, 60)) + if (!await FakeBookingSystem.FakeDatabase.UpdateFacilitySlotStartAndEndTimeByPeriodInMins(idComponents.SlotId.Value, 60)) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsNameSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateFacilityUseName(idComponents.SlotId.Value, "Updated Facility Title")) + if (!await FakeBookingSystem.FakeDatabase.UpdateFacilityUseName(idComponents.SlotId.Value, "Updated Facility Title")) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsLocationSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateFacilityUseLocationLatLng(idComponents.SlotId.Value, 0.2m, 0.3m)) + if (!await FakeBookingSystem.FakeDatabase.UpdateFacilityUseLocationLatLng(idComponents.SlotId.Value, 0.2m, 0.3m)) { throw new OpenBookingException(new UnknownOpportunityError()); } @@ -286,6 +286,9 @@ protected override async Task TriggerTestAction(OpenBookingSimulateAction simula // Similar to the RPDE logic, this needs to render and return an new hypothetical OrderItem from the database based on the supplied opportunity IDs protected override async Task GetOrderItems(List> orderItemContexts, StoreBookingFlowContext flowContext, OrderStateContext stateContext) { + var facilityTypeId = Environment.GetEnvironmentVariable("FACILITY_TYPE_ID") ?? "https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"; + var facilityTypePrefLabel = Environment.GetEnvironmentVariable("FACILITY_TYPE_PREF_LABEL") ?? "Squash Court"; + // Note the implementation of this method must also check that this OrderItem is from the Seller specified by context.SellerId (this is not required if using a Single Seller) // Additionally this method must check that there are enough spaces in each entry @@ -294,13 +297,13 @@ protected override async Task GetOrderItems(List { - var getOccurrenceInfoResult = await FakeBookingSystem.Database.GetSlotAndBookedOrderItemInfoBySlotId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); + var getOccurrenceInfoResult = await FakeBookingSystem.FakeDatabase.GetSlotAndBookedOrderItemInfoBySlotId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); var (hasFoundOccurrence, facility, slot, bookedOrderItemInfo) = getOccurrenceInfoResult; if (hasFoundOccurrence == false) { return null; } - var remainingUsesIncludingOtherLeases = await FakeBookingSystem.Database.GetNumberOfOtherLeasesForSlot(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); + var remainingUsesIncludingOtherLeases = await FakeBookingSystem.FakeDatabase.GetNumberOfOtherLeasesForSlot(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.SlotId); return new { @@ -349,8 +352,8 @@ protected override async Task GetOrderItems(List { new Concept { - Id = new Uri("https://openactive.io/facility-types#a1f82b7a-1258-4d9a-8dc5-bfc2ae961651"), - PrefLabel = "Squash Court", + Id = new Uri(facilityTypeId), + PrefLabel = facilityTypePrefLabel, InScheme = new Uri("https://openactive.io/facility-types") } } diff --git a/Examples/BookingSystem.AspNetFramework/Stores/OrderStore.cs b/Examples/BookingSystem.AspNetFramework/Stores/OrderStore.cs index f7cbde2f..f5b5d5c6 100644 --- a/Examples/BookingSystem.AspNetFramework/Stores/OrderStore.cs +++ b/Examples/BookingSystem.AspNetFramework/Stores/OrderStore.cs @@ -35,7 +35,7 @@ public override async Task CustomerCancelOrderItems(OrderIdComponents orde { try { - return await FakeBookingSystem.Database.CancelOrderItems( + return await FakeBookingSystem.FakeDatabase.CancelOrderItems( orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, @@ -54,7 +54,7 @@ public override async Task CustomerCancelOrderItems(OrderIdComponents orde /// True if OrderProposal found, False if OrderProposal not found public override async Task CustomerRejectOrderProposal(OrderIdComponents orderId, SimpleIdComponents sellerId) { - return await FakeBookingSystem.Database.RejectOrderProposal(orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, true); + return await FakeBookingSystem.FakeDatabase.RejectOrderProposal(orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, true); } public override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, OrderIdComponents orderId) @@ -66,7 +66,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } - if (!await FakeBookingSystem.Database.AcceptOrderProposal(orderId.uuid)) + if (!await FakeBookingSystem.FakeDatabase.AcceptOrderProposal(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -77,7 +77,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } var version = Guid.NewGuid(); - if (!await FakeBookingSystem.Database.AmendOrderProposal(orderId.uuid, version)) + if (!await FakeBookingSystem.FakeDatabase.AmendOrderProposal(orderId.uuid, version)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -87,7 +87,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } - if (!await FakeBookingSystem.Database.RejectOrderProposal(null, null, orderId.uuid, false)) + if (!await FakeBookingSystem.FakeDatabase.RejectOrderProposal(null, null, orderId.uuid, false)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -97,7 +97,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.CancelOrderItems(null, null, orderId.uuid, null, false, true)) + if (!await FakeBookingSystem.FakeDatabase.CancelOrderItems(null, null, orderId.uuid, null, false, true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -107,7 +107,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.CancelOrderItems(null, null, orderId.uuid, null, false)) + if (!await FakeBookingSystem.FakeDatabase.CancelOrderItems(null, null, orderId.uuid, null, false)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -117,7 +117,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessCode: true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateAccess(orderId.uuid, updateAccessCode: true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -127,7 +127,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessPass: true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateAccess(orderId.uuid, updateAccessPass: true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -137,7 +137,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateOpportunityAttendance(orderId.uuid, true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateOpportunityAttendance(orderId.uuid, true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -147,7 +147,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateOpportunityAttendance(orderId.uuid, false)) + if (!await FakeBookingSystem.FakeDatabase.UpdateOpportunityAttendance(orderId.uuid, false)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -157,7 +157,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.AddCustomerNotice(orderId.uuid)) + if (!await FakeBookingSystem.FakeDatabase.AddCustomerNotice(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -167,7 +167,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.ReplaceOrderOpportunity(orderId.uuid)) + if (!await FakeBookingSystem.FakeDatabase.ReplaceOrderOpportunity(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -177,7 +177,7 @@ public override async Task TriggerTestAction(OpenBookingSimulateAction simulateA { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } - if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessChannel: true)) + if (!await FakeBookingSystem.FakeDatabase.UpdateAccess(orderId.uuid, updateAccessChannel: true)) { throw new OpenBookingException(new UnknownOrderError()); } @@ -267,7 +267,7 @@ public override ValueTask UpdateLease( public override async Task DeleteLease(OrderIdComponents orderId, SimpleIdComponents sellerId) { // Note if no lease support, simply do nothing here - await FakeBookingSystem.Database.DeleteLease( + await FakeBookingSystem.FakeDatabase.DeleteLease( orderId.ClientId, orderId.uuid, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */ @@ -358,7 +358,7 @@ public override ValueTask UpdateOrderProposal(OrderProposal responseOrderProposa public override async Task DeleteOrder(OrderIdComponents orderId, SimpleIdComponents sellerId) { - var result = await FakeBookingSystem.Database.DeleteOrder( + var result = await FakeBookingSystem.FakeDatabase.DeleteOrder( orderId.ClientId, orderId.uuid, sellerId.IdLong ?? null /* Small hack to allow use of FakeDatabase when in Single Seller mode */); @@ -382,7 +382,7 @@ public override async Task CreateOrderFromOrderProposal(OrderIdComponents // TODO more elegantly extract version UUID from orderProposalVersion (probably much further up the stack?) var version = new Guid(orderProposalVersion.ToString().Split('/').Last()); - var result = await FakeBookingSystem.Database.BookOrderProposal( + var result = await FakeBookingSystem.FakeDatabase.BookOrderProposal( orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, @@ -442,7 +442,7 @@ public static Order RenderOrderFromDatabaseResult(Uri orderId, OrderTable dbOrde public override async Task GetOrderStatus(OrderIdComponents orderId, SimpleIdComponents sellerId, ILegalEntity seller) { - var (getOrderResult, dbOrder, dbOrderItems) = await FakeBookingSystem.Database.GetOrderAndOrderItems( + var (getOrderResult, dbOrder, dbOrderItems) = await FakeBookingSystem.FakeDatabase.GetOrderAndOrderItems( orderId.ClientId, sellerId.IdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid); diff --git a/Examples/BookingSystem.AspNetFramework/Stores/OrderTransaction.cs b/Examples/BookingSystem.AspNetFramework/Stores/OrderTransaction.cs index 6d433a54..d2759641 100644 --- a/Examples/BookingSystem.AspNetFramework/Stores/OrderTransaction.cs +++ b/Examples/BookingSystem.AspNetFramework/Stores/OrderTransaction.cs @@ -10,7 +10,7 @@ public sealed class OrderTransaction : IDatabaseTransaction public OrderTransaction() { - FakeDatabaseTransaction = new FakeDatabaseTransaction(FakeBookingSystem.Database); + FakeDatabaseTransaction = new FakeDatabaseTransaction(FakeBookingSystem.FakeDatabase); } public async ValueTask Commit() diff --git a/Examples/BookingSystem.AspNetFramework/Stores/SellerStore.cs b/Examples/BookingSystem.AspNetFramework/Stores/SellerStore.cs index 5dda02fa..7f8be1bb 100644 --- a/Examples/BookingSystem.AspNetFramework/Stores/SellerStore.cs +++ b/Examples/BookingSystem.AspNetFramework/Stores/SellerStore.cs @@ -57,7 +57,7 @@ protected override async ValueTask GetSeller(SimpleIdComponents se } // Otherwise it may be looked up based on supplied sellerIdComponents which are extracted from the sellerId. - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var seller = db.SingleById(sellerIdComponents.IdLong); if (seller == null) diff --git a/Examples/BookingSystem.AspNetFramework/Stores/SessionStore.cs b/Examples/BookingSystem.AspNetFramework/Stores/SessionStore.cs index a71a0976..024d6e4c 100644 --- a/Examples/BookingSystem.AspNetFramework/Stores/SessionStore.cs +++ b/Examples/BookingSystem.AspNetFramework/Stores/SessionStore.cs @@ -44,7 +44,7 @@ protected override async Task CreateOpportunityWithinTestDat switch (criteria) { case TestOpportunityCriteriaEnumeration.TestOpportunityBookable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Event", @@ -55,7 +55,7 @@ protected override async Task CreateOpportunityWithinTestDat case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellable: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFree: case TestOpportunityCriteriaEnumeration.TestOpportunityBookableUsingPayment: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event", @@ -67,7 +67,7 @@ protected override async Task CreateOpportunityWithinTestDat case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOutsideValidFromBeforeStartDate: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithinValidFromBeforeStartDate; - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event {(isValid ? "Within" : "Outside")} Window", @@ -81,7 +81,7 @@ protected override async Task CreateOpportunityWithinTestDat case TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableOutsideWindow: { var isValid = criteria == TestOpportunityCriteriaEnumeration.TestOpportunityBookableCancellableWithinWindow; - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, $"[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event {(isValid ? "Within" : "Outside")} Cancellation Window", @@ -92,7 +92,7 @@ protected override async Task CreateOpportunityWithinTestDat } break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentOptional: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Prepayment Optional", @@ -102,7 +102,7 @@ protected override async Task CreateOpportunityWithinTestDat prepayment: RequiredStatusType.Optional); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentUnavailable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Prepayment Unavailable", @@ -112,7 +112,7 @@ protected override async Task CreateOpportunityWithinTestDat prepayment: RequiredStatusType.Unavailable); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreePrepaymentRequired: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Prepayment Required", @@ -122,7 +122,7 @@ protected override async Task CreateOpportunityWithinTestDat prepayment: RequiredStatusType.Required); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFree: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event", @@ -131,7 +131,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNoSpaces: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event No Spaces", @@ -140,7 +140,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableFiveSpaces: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event Five Spaces", @@ -149,7 +149,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableOneSpace: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Free Event One Space", @@ -158,7 +158,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxNet: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, 2, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Tax Net", @@ -167,7 +167,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNonFreeTaxGross: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event Tax Gross", @@ -176,7 +176,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableSellerTermsOfService: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, 1, "[OPEN BOOKING API TEST INTERFACE] Bookable Event With Seller Terms Of Service", @@ -185,7 +185,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresApproval); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAttendeeDetails: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event That Requires Attendee Details", @@ -195,7 +195,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresAttendeeValidation: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableAdditionalDetails: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event That Requires Additional Details", @@ -205,7 +205,7 @@ protected override async Task CreateOpportunityWithinTestDat requiresAdditionalDetails: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityOnlineBookable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Virtual Event", @@ -215,7 +215,7 @@ protected override async Task CreateOpportunityWithinTestDat isOnlineOrMixedAttendanceMode: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityOfflineBookable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Offline Event", @@ -225,7 +225,7 @@ protected override async Task CreateOpportunityWithinTestDat isOnlineOrMixedAttendanceMode: false); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableWithNegotiation: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid Event That Allows Proposal Amendment", @@ -235,7 +235,7 @@ protected override async Task CreateOpportunityWithinTestDat allowProposalAmendment: true); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableNotCancellable: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Paid That Does Not Allow Full Refund", @@ -245,7 +245,7 @@ protected override async Task CreateOpportunityWithinTestDat allowCustomerCancellationFullRefund: false); break; case TestOpportunityCriteriaEnumeration.TestOpportunityBookableInPast: - (classId, occurrenceId) = await FakeBookingSystem.Database.AddClass( + (classId, occurrenceId) = await FakeBookingSystem.FakeDatabase.AddClass( testDatasetIdentifier, sellerId, "[OPEN BOOKING API TEST INTERFACE] Bookable Sesssion in the Past", @@ -272,7 +272,7 @@ protected override async Task CreateOpportunityWithinTestDat protected override async Task DeleteTestDataset(string testDatasetIdentifier) { - await FakeBookingSystem.Database.DeleteTestClassesFromDataset(testDatasetIdentifier); + await FakeBookingSystem.FakeDatabase.DeleteTestClassesFromDataset(testDatasetIdentifier); } protected override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, SessionOpportunity idComponents) @@ -280,19 +280,19 @@ protected override async Task TriggerTestAction(OpenBookingSimulateAction simula switch (simulateAction) { case ChangeOfLogisticsTimeSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateScheduledSessionStartAndEndTimeByPeriodInMins(idComponents.ScheduledSessionId.Value, 60)) + if (!await FakeBookingSystem.FakeDatabase.UpdateScheduledSessionStartAndEndTimeByPeriodInMins(idComponents.ScheduledSessionId.Value, 60)) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsNameSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateClassTitle(idComponents.ScheduledSessionId.Value, "Updated Class Title")) + if (!await FakeBookingSystem.FakeDatabase.UpdateClassTitle(idComponents.ScheduledSessionId.Value, "Updated Class Title")) { throw new OpenBookingException(new UnknownOpportunityError()); } return; case ChangeOfLogisticsLocationSimulateAction _: - if (!await FakeBookingSystem.Database.UpdateSessionSeriesLocationLatLng(idComponents.ScheduledSessionId.Value, 0.2m, 0.3m)) + if (!await FakeBookingSystem.FakeDatabase.UpdateSessionSeriesLocationLatLng(idComponents.ScheduledSessionId.Value, 0.2m, 0.3m)) { throw new OpenBookingException(new UnknownOpportunityError()); } @@ -314,14 +314,14 @@ protected override async Task GetOrderItems(List { - var getOccurrenceResultAndRows = await FakeBookingSystem.Database.GetOccurrenceAndBookedOrderItemInfoByOccurrenceId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); + var getOccurrenceResultAndRows = await FakeBookingSystem.FakeDatabase.GetOccurrenceAndBookedOrderItemInfoByOccurrenceId(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); var (hasFoundOccurrence, @class, occurrence, bookedOrderItemInfo) = getOccurrenceResultAndRows; if (hasFoundOccurrence == false) { return null; } - var remainingUsesIncludingOtherLeases = await FakeBookingSystem.Database.GetNumberOfOtherLeaseForOccurrence(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); + var remainingUsesIncludingOtherLeases = await FakeBookingSystem.FakeDatabase.GetNumberOfOtherLeaseForOccurrence(flowContext.OrderId.uuid, orderItemContext.RequestBookableOpportunityOfferId.ScheduledSessionId); return new { diff --git a/Examples/BookingSystem.AspNetFramework/Web.config b/Examples/BookingSystem.AspNetFramework/Web.config index c13ebba0..80758c20 100644 --- a/Examples/BookingSystem.AspNetFramework/Web.config +++ b/Examples/BookingSystem.AspNetFramework/Web.config @@ -1,24 +1,24 @@ - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Examples/BookingSystem.AspNetFramework/packages.config b/Examples/BookingSystem.AspNetFramework/packages.config index 38b069d0..ad9bac46 100644 --- a/Examples/BookingSystem.AspNetFramework/packages.config +++ b/Examples/BookingSystem.AspNetFramework/packages.config @@ -1,130 +1,130 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET.Tests/FakeBookingSystemTest.cs b/Fakes/OpenActive.FakeDatabase.NET.Tests/FakeBookingSystemTest.cs index 016ee482..e24234c2 100644 --- a/Fakes/OpenActive.FakeDatabase.NET.Tests/FakeBookingSystemTest.cs +++ b/Fakes/OpenActive.FakeDatabase.NET.Tests/FakeBookingSystemTest.cs @@ -19,7 +19,7 @@ public FakeBookingSystemTest(ITestOutputHelper output) [Fact] public void FakeDatabase_Exists() { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var q = db.From() .Join(); @@ -53,7 +53,7 @@ public void Transaction_Effective() { var testSeller = new SellerTable() { Name = "Test" }; - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { @@ -77,7 +77,7 @@ public void Transaction_Effective() [Fact] public void ReadWrite_DateTime() { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var now = DateTime.Now; // Note date must be stored as local time, not UTC var testOccurrence = new OccurrenceTable() { Start = now, ClassId = 1 }; @@ -93,7 +93,7 @@ public void ReadWrite_DateTime() [Fact] public void ReadWrite_Enum() { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var status = BookingStatus.CustomerCancelled; var testOrderItem = new OrderItemsTable() { Status = status }; @@ -109,7 +109,7 @@ public void ReadWrite_Enum() [Fact] public void ReadWrite_OrderWithPrice() { - using (var db = FakeBookingSystem.Database.Mem.Database.Open()) + using (var db = FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.Open()) { var uuid = new Guid("8265ab72-d458-40aa-a460-a9619e13192c"); decimal price = 1.3M; diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index 120a2482..26e09e56 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -24,7 +24,7 @@ public static class FakeBookingSystem /// /// The Database is created as static, to simulate the persistence of a real database /// - public static FakeDatabase Database { get; } = FakeDatabase.GetPrepopulatedFakeDatabase().Result; + public static FakeDatabase FakeDatabase { get; } = FakeDatabase.GetPrepopulatedFakeDatabase().Result; public static void Initialise() { @@ -74,11 +74,15 @@ public static string Sha256(this string input) } } - // ReSharper disable once InconsistentNaming - public class InMemorySQLite + + public abstract class DatabaseWrapper { - public readonly OrmLiteConnectionFactory Database; + public OrmLiteConnectionFactory Database { get; set; } + } + // ReSharper disable once InconsistentNaming + public class InMemorySQLite : DatabaseWrapper + { public InMemorySQLite() { // ServiceStack registers a memory cache client by default https://docs.servicestack.net/caching @@ -98,12 +102,31 @@ public InMemorySQLite() } // Create empty tables - DatabaseCreator.CreateTables(Database); + // For in-memory database, data must be generated on every restart + DatabaseCreator.CreateTables(Database, true); // OrmLiteUtils.PrintSql(); } } + public class RemoteSqlServer : DatabaseWrapper + { + public RemoteSqlServer() + { + var connectionString = Environment.GetEnvironmentVariable("REMOTE_STORAGE_CONNECTION_STRING"); + if (connectionString == null) + { + throw new Exception("Environment variable 'REMOTE_STORAGE_CONNECTION_STRING' is not set when 'USE_REMOTE_STORAGE' is true"); + } + Database = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider); + + // Create empty tables + var dropTablesOnRestart = bool.TryParse(Environment.GetEnvironmentVariable("DROP_TABLES_ON_RESTART"), out var dropTablesEnvVar) ? dropTablesEnvVar : true; + DatabaseCreator.CreateTables(Database, dropTablesOnRestart); + } + + } + /// /// Result of deleting (or attempting to delete) an Order in a FakeDatabase /// @@ -166,7 +189,7 @@ public class FakeDatabase private const float ProportionWithRequiresAttendeeValidation = 1f / 10; private const float ProportionWithRequiresAdditionalDetails = 1f / 10; - public readonly InMemorySQLite Mem = new InMemorySQLite(); + public readonly DatabaseWrapper DatabaseWrapper; private static readonly Faker Faker = new Faker(); @@ -175,15 +198,92 @@ static FakeDatabase() Randomizer.Seed = new Random((int)(DateTime.Today - new DateTime(1970, 1, 1)).TotalDays); } - private static readonly int OpportunityCount = - int.TryParse(Environment.GetEnvironmentVariable("OPPORTUNITY_COUNT"), out var opportunityCount) ? opportunityCount : 2000; + public FakeDatabase() + { + var useRemoteStorage = bool.TryParse(Environment.GetEnvironmentVariable("USE_REMOTE_STORAGE"), out var remoteStorageEnvVar) ? remoteStorageEnvVar : false; + if (useRemoteStorage) + { + DatabaseWrapper = new RemoteSqlServer(); + } + else + { + DatabaseWrapper = new InMemorySQLite(); + } + } + + public async Task SoftDeletedPastOpportunitiesAndInsertNewAtEdgeOfWindow() + { + using (var db = await DatabaseWrapper.Database.OpenAsync()) + { + // Get Occurrences to be soft deleted + var toBeSoftDeletedOccurrences = await db.SelectAsync(x => x.Deleted != true && x.Start < DateTime.Now); + // Create new copy of occurrences where date is at edge of window (ie 15 days in the future), reset the uses, and insert + var occurrencesAtEdgeOfWindow = toBeSoftDeletedOccurrences.Select(x => new OccurrenceTable + { + TestDatasetIdentifier = x.TestDatasetIdentifier, + ClassTable = x.ClassTable, + ClassId = x.ClassId, + Start = x.Start.AddDays(15), + End = x.End.AddDays(15), + RemainingSpaces = x.TotalSpaces, + LeasedSpaces = 0, + TotalSpaces = x.TotalSpaces, + } + ).ToList(); + await db.InsertAllAsync(occurrencesAtEdgeOfWindow); + + // Mark old occurrences as soft deleted and update + var softDeletedOccurrences = toBeSoftDeletedOccurrences.Select(x => { x.Deleted = true; return x; }); + await db.UpdateAllAsync(softDeletedOccurrences); + + // Get Slots to be soft deleted + var toBeSoftDeletedSlots = await db.SelectAsync(x => x.Deleted != true && x.Start < DateTime.Now); + // Create new copy of slots where date is at edge of window (ie 15 days in the future), reset the uses, and insert + var slotsAtEdgeOfWindow = toBeSoftDeletedSlots.Select(x => new SlotTable + { + TestDatasetIdentifier = x.TestDatasetIdentifier, + FacilityUseId = x.FacilityUseId, + FacilityUseTable = x.FacilityUseTable, + Start = x.Start.AddDays(15), + End = x.End.AddDays(15), + RemainingUses = x.MaximumUses, + LeasedUses = 0, + MaximumUses = x.MaximumUses, + Price = x.Price, + AllowCustomerCancellationFullRefund = x.AllowCustomerCancellationFullRefund, + Prepayment = x.Prepayment, + RequiresAttendeeValidation = x.RequiresAttendeeValidation, + RequiresApproval = x.RequiresApproval, + RequiresAdditionalDetails = x.RequiresAdditionalDetails, + RequiredAdditionalDetails = x.RequiredAdditionalDetails, + ValidFromBeforeStartDate = x.ValidFromBeforeStartDate, + LatestCancellationBeforeStartDate = x.LatestCancellationBeforeStartDate, + AllowsProposalAmendment = x.AllowsProposalAmendment, + } + ).ToList(); + await db.InsertAllAsync(slotsAtEdgeOfWindow); + + // Mark old slots as soft deleted and update + var softDeletedSlots = toBeSoftDeletedSlots.Select(x => { x.Deleted = true; return x; }); + await db.UpdateAllAsync(softDeletedSlots); + } + } + + public async Task HardDeletedOldSoftDeletedOccurrencesAndSlots() + { + using (var db = await DatabaseWrapper.Database.OpenAsync()) + { + await db.DeleteAsync(x => x.Deleted == true && x.Modified < new DateTimeOffset(DateTime.Today.AddDays(-1)).UtcTicks); + await db.DeleteAsync(x => x.Deleted == true && x.Modified < new DateTimeOffset(DateTime.Today.AddDays(-1)).UtcTicks); + } + } /// /// TODO: Call this on a schedule from both .NET Core and .NET Framework reference implementations /// public async Task CleanupExpiredLeases() { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var occurrenceIds = new List(); var slotIds = new List(); @@ -257,7 +357,7 @@ await db.InsertAsync(new OrderTable /// public async Task UpdateFacilityUseName(long slotId, string newName) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .LeftJoin() @@ -282,7 +382,7 @@ public async Task UpdateFacilityUseName(long slotId, string newName) /// public async Task UpdateFacilitySlotStartAndEndTimeByPeriodInMins(long slotId, int numberOfMins) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var slot = await db.SingleAsync(x => x.Id == slotId && !x.Deleted); if (slot == null) @@ -305,7 +405,7 @@ public async Task UpdateFacilitySlotStartAndEndTimeByPeriodInMins(long slo /// public async Task UpdateFacilityUseLocationLatLng(long slotId, decimal newLat, decimal newLng) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .LeftJoin() @@ -331,7 +431,7 @@ public async Task UpdateFacilityUseLocationLatLng(long slotId, decimal new /// public async Task UpdateClassTitle(long occurrenceId, string newTitle) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .LeftJoin() @@ -356,7 +456,7 @@ public async Task UpdateClassTitle(long occurrenceId, string newTitle) /// public async Task UpdateScheduledSessionStartAndEndTimeByPeriodInMins(long occurrenceId, int numberOfMins) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var occurrence = await db.SingleAsync(x => x.Id == occurrenceId && !x.Deleted); if (occurrence == null) @@ -379,7 +479,7 @@ public async Task UpdateScheduledSessionStartAndEndTimeByPeriodInMins(long /// public async Task UpdateSessionSeriesLocationLatLng(long occurrenceId, decimal newLat, decimal newLng) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .LeftJoin() @@ -402,7 +502,7 @@ public async Task UpdateAccess(Guid uuid, bool updateAccessPass = false, b if (!updateAccessPass && !updateAccessCode && !updateAccessChannel) return false; - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { OrderTable order = await db.SingleAsync(x => x.OrderId == uuid.ToString() && !x.Deleted); @@ -450,7 +550,7 @@ public async Task UpdateAccess(Guid uuid, bool updateAccessPass = false, b public async Task UpdateOpportunityAttendance(Guid uuid, bool attended) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { OrderTable order = await db.SingleAsync(x => x.OrderId == uuid.ToString() && !x.Deleted); @@ -481,7 +581,7 @@ public async Task UpdateOpportunityAttendance(Guid uuid, bool attended) public async Task AddCustomerNotice(Guid uuid) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { OrderTable order = await db.SingleAsync(x => x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) @@ -510,7 +610,7 @@ public async Task AddCustomerNotice(Guid uuid) public async Task DeleteBookingPartner(string clientId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { await db.DeleteAsync(x => x.ClientId == clientId); } @@ -518,7 +618,7 @@ public async Task DeleteBookingPartner(string clientId) public async Task DeleteLease(string clientId, Guid uuid, long? sellerId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { // TODO: Note this should throw an error if the Seller ID does not match, same as DeleteOrder if (await db.ExistsAsync(x => x.ClientId == clientId && x.OrderMode == OrderMode.Lease && x.OrderId == uuid.ToString() && (!sellerId.HasValue || x.SellerId == sellerId))) @@ -611,7 +711,7 @@ await db.InsertAsync(new OrderTable public async Task<(FakeDatabaseGetOrderResult, OrderTable, List)> GetOrderAndOrderItems(string clientId, long? sellerId, Guid uuid) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var order = await db.SingleAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && !x.Deleted && (!sellerId.HasValue || x.SellerId == sellerId)); if (order == null) return (FakeDatabaseGetOrderResult.OrderWasNotFound, null, null); @@ -624,7 +724,7 @@ await db.InsertAsync(new OrderTable public async Task<(bool, ClassTable, OccurrenceTable, BookedOrderItemInfo)> GetOccurrenceAndBookedOrderItemInfoByOccurrenceId(Guid uuid, long? occurrenceId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .LeftJoin() @@ -660,7 +760,7 @@ await db.InsertAsync(new OrderTable public async Task<(bool, FacilityUseTable, SlotTable, BookedOrderItemInfo)> GetSlotAndBookedOrderItemInfoBySlotId(Guid uuid, long? slotId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .LeftJoin() @@ -692,7 +792,7 @@ await db.InsertAsync(new OrderTable public async Task DeleteOrder(string clientId, Guid uuid, long? sellerId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { // Set the Order to deleted in the feed, and erase all associated personal data var order = await db.SingleAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OrderMode != OrderMode.Lease); @@ -981,7 +1081,7 @@ List additionalDetailsString public async Task CancelOrderItems(string clientId, long? sellerId, Guid uuid, List orderItemIds, bool customerCancelled, bool includeCancellationMessage = false) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { var order = customerCancelled @@ -1091,7 +1191,7 @@ public async Task CancelOrderItems(string clientId, long? sellerId, Guid u public async Task ReplaceOrderOpportunity(Guid uuid) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .Join() @@ -1168,7 +1268,7 @@ public async Task ReplaceOrderOpportunity(Guid uuid) public async Task AcceptOrderProposal(Guid uuid) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var order = await db.SingleAsync(x => x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) @@ -1190,7 +1290,7 @@ public async Task AcceptOrderProposal(Guid uuid) } public async Task AmendOrderProposal(Guid uuid, Guid version) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var order = await db.SingleAsync(x => x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) @@ -1213,7 +1313,7 @@ public async Task AmendOrderProposal(Guid uuid, Guid version) public async Task BookOrderProposal(string clientId, long? sellerId, Guid uuid, Guid? proposalVersionUuid) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { // Note call is idempotent, so it might already be in the booked state var order = await db.SingleAsync(x => x.ClientId == clientId && (x.OrderMode == OrderMode.Proposal || x.OrderMode == OrderMode.Booking) && x.OrderId == uuid.ToString() && !x.Deleted); @@ -1263,31 +1363,47 @@ public async Task BookOrderProposal(string public async Task GetNumberOfOtherLeaseForOccurrence(Guid uuid, long? occurrenceId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { - return db.Count(x => x.OrderTable.OrderMode != OrderMode.Booking && - x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && - x.OrderTable.ProposalStatus != ProposalStatus.SellerRejected && - x.OccurrenceId == occurrenceId && - x.OrderId != uuid.ToString()); + var q = db.From().Join((orderItem, order) => + orderItem.OrderId == order.OrderId && + order.OrderMode != OrderMode.Booking && + order.ProposalStatus != ProposalStatus.CustomerRejected && + order.ProposalStatus != ProposalStatus.SellerRejected && + orderItem.OccurrenceId == occurrenceId && + orderItem.OrderId != uuid.ToString()); + return db.Count(q); } } public async Task GetNumberOfOtherLeasesForSlot(Guid uuid, long? slotId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { - return db.Count(x => x.OrderTable.OrderMode != OrderMode.Booking && + var q = db.From().Join((orderItem, order) => + orderItem.OrderId == order.OrderId && + order.OrderMode != OrderMode.Booking && + order.ProposalStatus != ProposalStatus.CustomerRejected && + order.ProposalStatus != ProposalStatus.SellerRejected && + orderItem.SlotId == slotId && + orderItem.OrderId != uuid.ToString() + ); + var a = db.SelectAsync(q); + + //return db.Count(q); + + var b = await db.LoadSelectAsync(x => x.OrderTable.OrderMode != OrderMode.Booking && x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && x.OrderTable.ProposalStatus != ProposalStatus.SellerRejected && x.SlotId == slotId && x.OrderId != uuid.ToString()); + return b.Count; } } public async Task RejectOrderProposal(string clientId, long? sellerId, Guid uuid, bool customerRejected) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var order = await db.SingleAsync(x => (clientId == null || x.ClientId == clientId) && x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) @@ -1322,11 +1438,19 @@ public static async Task RecalculateSlotUses(IDbConnection db, SlotTable slot) return; // Update number of leased spaces remaining for the opportunity - var leasedUses = (await db.LoadSelectAsync(x => x.OrderTable.OrderMode != OrderMode.Booking && x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && x.OrderTable.ProposalStatus != ProposalStatus.SellerRejected && x.SlotId == slot.Id)).Count; + var leasedUsesQuery = db.From().Join((x, y) => x.OrderId == y.OrderId) + .Where(x => x.OrderMode != OrderMode.Booking && + x.ProposalStatus != ProposalStatus.CustomerRejected && + x.ProposalStatus != ProposalStatus.SellerRejected) + .And(x => x.SlotId == slot.Id); + var leasedUses = await db.CountAsync(leasedUsesQuery); slot.LeasedUses = leasedUses; // Update number of actual spaces remaining for the opportunity - var totalUsesTaken = (await db.LoadSelectAsync(x => x.OrderTable.OrderMode == OrderMode.Booking && x.OccurrenceId == slot.Id && (x.Status == BookingStatus.Confirmed || x.Status == BookingStatus.Attended || x.Status == BookingStatus.Absent))).Count; + var totalUsesTakenQuery = db.From().Join((x, y) => x.OrderId == y.OrderId) + .Where(x => x.OrderMode == OrderMode.Booking) + .And(x => x.SlotId == slot.Id && (x.Status == BookingStatus.Confirmed || x.Status == BookingStatus.Attended || x.Status == BookingStatus.Absent)); + var totalUsesTaken = await db.CountAsync(totalUsesTakenQuery); slot.RemainingUses = slot.MaximumUses - totalUsesTaken; // Push the change into the future to avoid it getting lost in the feed (see race condition transaction challenges https://developer.openactive.io/publishing-data/data-feeds/implementing-rpde-feeds#preventing-the-race-condition) @@ -1350,11 +1474,23 @@ public static async Task RecalculateSpaces(IDbConnection db, OccurrenceTable occ return; // Update number of leased spaces remaining for the opportunity - var leasedSpaces = (await db.LoadSelectAsync(x => x.OrderTable.OrderMode != OrderMode.Booking && x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && x.OrderTable.ProposalStatus != ProposalStatus.SellerRejected && x.OccurrenceId == occurrence.Id)).Count; + var leasedUsesQuery = db.From().Join((orderItem, order) => + orderItem.OrderId == order.OrderId && + order.OrderMode != OrderMode.Booking && + order.ProposalStatus != ProposalStatus.CustomerRejected && + order.ProposalStatus != ProposalStatus.SellerRejected && + orderItem.OccurrenceId == occurrence.Id); + + var leasedSpaces = await db.CountAsync(leasedUsesQuery); occurrence.LeasedSpaces = leasedSpaces; // Update number of actual spaces remaining for the opportunity - var totalSpacesTaken = (await db.LoadSelectAsync(x => x.OrderTable.OrderMode == OrderMode.Booking && x.OccurrenceId == occurrence.Id && (x.Status == BookingStatus.Confirmed || x.Status == BookingStatus.Attended || x.Status == BookingStatus.Absent))).Count(); + var totalUsesTakenQuery = db.From().Join((orderItem, order) => + orderItem.OrderId == order.OrderId && + order.OrderMode == OrderMode.Booking && + orderItem.OccurrenceId == occurrence.Id + && (orderItem.Status == BookingStatus.Confirmed || orderItem.Status == BookingStatus.Attended || orderItem.Status == BookingStatus.Absent)); + var totalSpacesTaken = await db.CountAsync(totalUsesTakenQuery); occurrence.RemainingSpaces = occurrence.TotalSpaces - totalSpacesTaken; // Push the change into the future to avoid it getting lost in the feed (see race condition transaction challenges https://developer.openactive.io/publishing-data/data-feeds/implementing-rpde-feeds#preventing-the-race-condition) // TODO: Document this! @@ -1373,27 +1509,32 @@ public static async Task RecalculateSpaces(IDbConnection db, IEnumerable o public static async Task GetPrepopulatedFakeDatabase() { - var database = new FakeDatabase(); - using (var db = await database.Mem.Database.OpenAsync()) - using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) + var fakeDatabase = new FakeDatabase(); + var useRemoteStorage = bool.TryParse(Environment.GetEnvironmentVariable("USE_REMOTE_STORAGE"), out var remoteStorageEnvVar) ? remoteStorageEnvVar : false; + var dropTablesOnRestart = bool.TryParse(Environment.GetEnvironmentVariable("DROP_TABLES_ON_RESTART"), out var dropTablesEnvVar) ? dropTablesEnvVar : true; + var opportunityCount = + int.TryParse(Environment.GetEnvironmentVariable("OPPORTUNITY_COUNT"), out var opportunityCountEnvVar) ? opportunityCountEnvVar : 2000; + if (!useRemoteStorage || dropTablesOnRestart) { - - await CreateSellers(db); - await CreateSellerUsers(db); - await CreateFakeClasses(db); - await CreateFakeFacilitiesAndSlots(db); - await CreateOrders(db); // Add these in to generate your own orders and grants, otherwise generate them using the test suite - await CreateGrants(db); - await BookingPartnerTable.Create(db); - transaction.Commit(); + using (var db = await fakeDatabase.DatabaseWrapper.Database.OpenAsync()) + using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) + { + await CreateSellers(db); + await CreateSellerUsers(db); + await CreateFakeClasses(db, opportunityCount); + await CreateFakeFacilitiesAndSlots(db, opportunityCount); + await CreateOrders(db); // Add these in to generate your own orders and grants, otherwise generate them using the test suite + await CreateGrants(db); + await BookingPartnerTable.Create(db); + transaction.Commit(); + } } - - return database; + return fakeDatabase; } - private static async Task CreateFakeFacilitiesAndSlots(IDbConnection db) + private static async Task CreateFakeFacilitiesAndSlots(IDbConnection db, int opportunityCount) { - var opportunitySeeds = GenerateOpportunitySeedDistribution(OpportunityCount); + var opportunitySeeds = GenerateOpportunitySeedDistribution(opportunityCount); var facilities = opportunitySeeds .Select(seed => new FacilityUseTable @@ -1446,9 +1587,9 @@ private static async Task CreateFakeFacilitiesAndSlots(IDbConnection db) await db.InsertAllAsync(slots); } - public static async Task CreateFakeClasses(IDbConnection db) + public static async Task CreateFakeClasses(IDbConnection db, int opportunityCount) { - var opportunitySeeds = GenerateOpportunitySeedDistribution(OpportunityCount); + var opportunitySeeds = GenerateOpportunitySeedDistribution(opportunityCount); var classes = opportunitySeeds .Select(seed => new @@ -1615,9 +1756,9 @@ public static async Task CreateGrants(IDbConnection db) { var grants = new List { - new GrantTable { ClientId = "clientid_XXX", SubjectId = "100", Type = "user_consent" }, - new GrantTable { ClientId = "clientid_YYY", SubjectId = "100", Type = "user_consent" }, - new GrantTable { ClientId = "clientid_ZZZ", SubjectId = "100", Type = "user_consent" }, + new GrantTable { ClientId = "clientid_XXX", SubjectId = "100", Type = "user_consent", CreationTime = DateTime.Now, Key = Faker.Random.String2(25) }, + new GrantTable { ClientId = "clientid_YYY", SubjectId = "100", Type = "user_consent", CreationTime = DateTime.Now, Key = Faker.Random.String2(25) }, + new GrantTable { ClientId = "clientid_ZZZ", SubjectId = "100", Type = "user_consent", CreationTime = DateTime.Now, Key = Faker.Random.String2(25) }, }; await db.InsertAllAsync(grants); @@ -1640,7 +1781,7 @@ public static async Task CreateSellerUsers(IDbConnection db) public async Task ValidateSellerUserCredentials(string username, string password) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var matchingUser = await db.SingleAsync(x => x.Username == username && x.PasswordHash == password.Sha256()); return matchingUser != null; @@ -1649,7 +1790,7 @@ public async Task ValidateSellerUserCredentials(string username, string pa public async Task GetSellerUser(string username) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { return await db.SingleAsync(x => x.Username == username); } @@ -1657,7 +1798,7 @@ public async Task GetSellerUser(string username) public async Task GetSellerUserById(long sellerUserId) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { return await db.LoadSingleByIdAsync(sellerUserId); } @@ -1665,14 +1806,14 @@ public async Task GetSellerUserById(long sellerUserId) public async Task GetGrant(string key) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { return await db.SingleAsync(x => x.Key == key); } } public async Task> GetAllGrants(string subjectId, string sessionId, string clientId, string type) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From(); if (!String.IsNullOrWhiteSpace(clientId)) @@ -1698,7 +1839,7 @@ public async Task> GetAllGrants(string subjectId, string sessio public async Task AddGrant(string key, string type, string subjectId, string sessionId, string clientId, DateTime creationTime, DateTime? consumedTime, DateTime? expiration, string data) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var grant = new GrantTable { @@ -1718,7 +1859,7 @@ public async Task AddGrant(string key, string type, string subjectId, stri public async Task RemoveGrant(string key) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { await db.DeleteAsync(x => x.Key == key); } @@ -1726,7 +1867,7 @@ public async Task RemoveGrant(string key) public async Task RemoveAllGrants(string subjectId, string sessionId, string clientId, string type) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { var query = db.From(); if (!String.IsNullOrWhiteSpace(clientId)) @@ -1745,11 +1886,18 @@ public async Task RemoveAllGrants(string subjectId, string sessionId, string cli { query = query.Where(x => x.Type == type); } - await db.DeleteAsync(query); } } + public async Task RemoveGrant(string subjectId, string clientId, string type) + { + using (var db = await DatabaseWrapper.Database.OpenAsync()) + { + await db.DeleteAsync(x => x.SubjectId == subjectId && x.ClientId == clientId && x.Type == type); + } + } + public async Task<(int, int)> AddClass( string testDatasetIdentifier, long? sellerId, @@ -1773,7 +1921,7 @@ public async Task RemoveAllGrants(string subjectId, string sessionId, string cli var startTime = DateTime.Now.AddDays(inPast ? -1 : 1); var endTime = DateTime.Now.AddDays(inPast ? -1 : 1).AddHours(1); - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { var @class = new ClassTable @@ -1843,7 +1991,7 @@ public async Task RemoveAllGrants(string subjectId, string sessionId, string cli var startTime = DateTime.Now.AddDays(inPast ? -1 : 1); var endTime = DateTime.Now.AddDays(inPast ? -1 : 1).AddHours(1); - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { var facility = new FacilityUseTable @@ -1893,7 +2041,7 @@ public async Task RemoveAllGrants(string subjectId, string sessionId, string cli public async Task DeleteTestClassesFromDataset(string testDatasetIdentifier) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { await db.UpdateOnlyAsync(() => new ClassTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, where: x => x.TestDatasetIdentifier == testDatasetIdentifier && !x.Deleted); @@ -1905,7 +2053,7 @@ public async Task DeleteTestClassesFromDataset(string testDatasetIdentifier) public async Task DeleteTestFacilitiesFromDataset(string testDatasetIdentifier) { - using (var db = await Mem.Database.OpenAsync()) + using (var db = await DatabaseWrapper.Database.OpenAsync()) { await db.UpdateOnlyAsync(() => new FacilityUseTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, where: x => x.TestDatasetIdentifier == testDatasetIdentifier && !x.Deleted); diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs index fff6c808..39eb76c8 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs @@ -11,7 +11,7 @@ public class FakeDatabaseTransaction : IDisposable public FakeDatabaseTransaction(FakeDatabase database) { - DatabaseConnection = database.Mem.Database.Open(); + DatabaseConnection = database.DatabaseWrapper.Database.Open(); transaction = DatabaseConnection.OpenTransaction(IsolationLevel.Serializable); } diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs index 007791e9..9eb66475 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs @@ -73,7 +73,7 @@ public static async Task Create(IDbConnection db) public static async Task> Get() { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { return await db.SelectAsync(); } @@ -81,7 +81,7 @@ public static async Task> Get() public static async Task> GetBySellerUserId(long sellerUserId) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { var query = db.From() .Join((b, g) => b.ClientId == g.ClientId && g.Type == "user_consent") @@ -93,7 +93,7 @@ public static async Task> GetBySellerUserId(long selle public static async Task GetByInitialAccessToken(string registrationKey) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { var bookingPartner = await db.SingleAsync(x => x.InitialAccessToken == registrationKey); return bookingPartner?.InitialAccessTokenKeyValidUntil > DateTime.Now ? bookingPartner : null; @@ -102,7 +102,7 @@ public static async Task GetByInitialAccessToken(string reg public static async Task GetByClientId(string clientId) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { return await db.SingleAsync(x => x.ClientId == clientId); } @@ -110,7 +110,7 @@ public static async Task GetByClientId(string clientId) public static async Task Save(BookingPartnerTable bookingPartnerTable) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { await db.SaveAsync(bookingPartnerTable); } @@ -118,7 +118,7 @@ public static async Task Save(BookingPartnerTable bookingPartnerTable) public static async Task ResetCredentials(string clientId, string key) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); bookingPartner.Registered = false; @@ -131,7 +131,7 @@ public static async Task ResetCredentials(string clientId, string key) public static async Task SetKey(string clientId, string key) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); bookingPartner.InitialAccessToken = key; @@ -142,7 +142,7 @@ public static async Task SetKey(string clientId, string key) public static async Task UpdateScope(string clientId, string scope, bool bookingsSuspended) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); bookingPartner.Scope = scope; @@ -153,7 +153,7 @@ public static async Task UpdateScope(string clientId, string scope, bool booking public static async Task Add(BookingPartnerTable newBookingPartner) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { await db.SaveAsync(newBookingPartner); } diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs index af61cb1a..e8961941 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs @@ -15,7 +15,7 @@ public class BookingStatistics // ToDo: this is N+1 - if we nuke the ORM, we can re-write the main query to avoid this public static async Task Get(string clientId) { - using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + using (var db = await FakeBookingSystem.FakeDatabase.DatabaseWrapper.Database.OpenAsync()) { var thirtyDaysAgo = DateTimeOffset.Now.AddDays(-30); var bookingsByBroker = (await db.SelectAsync(o => o.ClientId == clientId && o.OrderCreated > thirtyDaysAgo)) diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs index 20630ff9..f26c28c9 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs @@ -32,30 +32,46 @@ public abstract class Table public static class DatabaseCreator { - public static void CreateTables(OrmLiteConnectionFactory dbFactory) + public static void CreateTables(OrmLiteConnectionFactory dbFactory, bool dropTablesOnRestart) { using (var db = dbFactory.Open()) { - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.DropTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); + if (dropTablesOnRestart) + { + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.DropTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + } + else + { + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + db.CreateTableIfNotExists(); + } } } } diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs index b6a72aa9..0a086917 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs @@ -14,11 +14,11 @@ public class OrderItemsTable : Table public string OrderId { get; set; } [Reference] public OccurrenceTable OccurrenceTable { get; set; } - [ForeignKey(typeof(OccurrenceTable), OnDelete = "CASCADE")] + [ForeignKey(typeof(OccurrenceTable), OnDelete = "NO ACTION")] // SQL Server seems to have a problem with chained cascading public long? OccurrenceId { get; set; } [Reference] public SlotTable SlotTable { get; set; } - [ForeignKey(typeof(SlotTable), OnDelete = "CASCADE")] + [ForeignKey(typeof(SlotTable), OnDelete = "NO ACTION")] // SQL Server seems to have a problem with chained cascading public long? SlotId { get; set; } public BookingStatus Status { get; set; } public string CancellationMessage { get; set; } diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs index f83fd1a8..10cf517c 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs @@ -41,7 +41,7 @@ public class OrderTable public string PaymentAccountId { get; set; } public decimal TotalOrderPrice { get; set; } public OrderMode OrderMode { get; set; } - public DateTime LeaseExpires { get; set; } + public DateTime? LeaseExpires { get; set; } public FeedVisibility VisibleInOrdersFeed { get; set; } public FeedVisibility VisibleInOrderProposalsFeed { get; set; } public ProposalStatus? ProposalStatus { get; set; } diff --git a/Fakes/OpenActive.FakeDatabase.NET/OpenActive.FakeDatabase.NET.csproj b/Fakes/OpenActive.FakeDatabase.NET/OpenActive.FakeDatabase.NET.csproj index 4b5c36b5..9c7c330c 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/OpenActive.FakeDatabase.NET.csproj +++ b/Fakes/OpenActive.FakeDatabase.NET/OpenActive.FakeDatabase.NET.csproj @@ -34,6 +34,7 @@ +