From 53e81f48a591afdd2097be3a9269d71654cf8a39 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Tue, 29 Jun 2021 21:12:21 +0100 Subject: [PATCH 01/14] fix: booking partners link and client registration location (#153) feat: Make fake database async --- .gitignore | 3 + .../Custom/Account/AccountController.cs | 61 +- .../Client/ClientRegistrationController.cs | 41 +- .../Custom/Client/ClientStore.cs | 14 +- .../Custom/Grants/BookingPartnerViewModel.cs | 2 - .../Grants/BookingPartnersController.cs | 110 ++- .../Custom/Grants/PersistedGrantStore.cs | 51 +- .../Custom/Profile/ProfileService.cs | 21 +- .../Custom/Resources/Config.cs | 1 - .../Resources/CustomIdentityResource.cs | 3 - .../Custom/Users/Extensions.cs | 4 - .../Custom/Users/Interfaces.cs | 12 +- .../Users/ResourceOwnerPasswordValidator.cs | 11 +- .../Custom/Users/UserRepository.cs | 37 +- .../Program.cs | 2 +- .../Startup.cs | 3 - .../BookingPartnerCreate.cshtml | 3 +- .../BookingPartners/BookingPartnerEdit.cshtml | 3 +- .../Views/BookingPartners/Index.cshtml | 12 +- .../Authentication/HasScopeRequirement.cs | 4 - .../TestHeaderAuthentication.cs | 1 - .../Feeds/FacilitiesFeeds.cs | 4 +- .../Feeds/OrdersFeed.cs | 4 +- .../Feeds/SessionsFeeds.cs | 4 +- Examples/BookingSystem.AspNetCore/Program.cs | 9 +- .../Stores/FacilityStore.cs | 76 +- .../Stores/OrderStore.cs | 70 +- .../Stores/SellerStore.cs | 2 +- .../Stores/SessionStore.cs | 80 +- .../Properties/AssemblyInfo.cs | 1 - .../App_Start/BundleConfig.cs | 5 +- .../App_Start/FilterConfig.cs | 3 +- .../App_Start/IdentityConfig.cs | 9 +- .../App_Start/RouteConfig.cs | 6 +- .../App_Start/Startup.Auth.cs | 13 +- .../App_Start/WebApiConfig.cs | 9 +- .../Controllers/DatasetSiteController.cs | 5 - .../Controllers/OpenDataController.cs | 6 +- .../Feeds/FacilitiesFeeds.cs | 4 +- .../Feeds/OrdersFeed.cs | 4 +- .../Feeds/SessionsFeeds.cs | 4 +- .../Global.asax.cs | 1 - .../Helpers/AuthenticationHelper.cs | 1 - .../Helpers/OpenBookingInputFormatter.cs | 3 - .../Models/AccountBindingModels.cs | 4 +- .../Models/AccountViewModels.cs | 3 +- .../Models/IdentityModels.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Stores/FacilityStore.cs | 76 +- .../Stores/OrderStore.cs | 70 +- .../Stores/SellerStore.cs | 2 +- .../Stores/SessionStore.cs | 80 +- .../Utils/DependencyResolver.cs | 2 - .../FakeBookingSystemTest.cs | 2 - .../FakeBookingSystem.cs | 866 +++++++++--------- .../FakeDatabaseTransaction.cs | 4 - OpenActive.Server.NET.Tests/IdTemplate.cs | 1 - .../SellerIdComponentsTest.cs | 5 +- .../StoreBookingEngineTest.cs | 5 +- .../Entrypoint/IBookingEngine.cs | 3 - .../AuthenticationExtensions.cs | 2 - .../AuthenticationTestHeaders.cs | 6 +- .../OpenActiveCustomClaimNames.cs | 6 +- .../Authentication/OpenActiveScopes.cs | 6 +- .../Beta/AuthenticatedPerson.cs | 3 - .../Content/ResponseContent.cs | 4 - .../Context/BookingFlowContext.cs | 4 - .../ClientRegistrationModel.cs | 2 - .../OidcConstants.cs | 6 +- .../OpenBookingHelper/Enums/FlowStage.cs | 6 +- .../Enums/TaxPayeeRelationship.cs | 6 +- .../Exceptions/OpenBookingException.cs | 2 - .../IdTransforms/DefaultComponents.cs | 3 - .../OpenBookingHelper/Model/ModelSupport.cs | 3 - .../Model/OrderModelSupport.cs | 6 +- .../OpenBookingHelper/Rpde/OrdersRpdeBase.cs | 2 - .../Rpde/RpdeOrderingStrategyRouter.cs | 2 +- .../Settings/BookingEngineSettings.cs | 3 - .../OpenBookingHelper/Stores/SellerStore.cs | 3 - .../Context/StoreBookingFlowContext.cs | 2 - .../StoreBookingEngine/StoreBookingEngine.cs | 20 +- .../Stores/OpportunityStore.cs | 1 - .../StoreBookingEngine/Stores/OrderStore.cs | 1 - 83 files changed, 849 insertions(+), 1097 deletions(-) diff --git a/.gitignore b/.gitignore index 07370c31..c83e3a48 100644 --- a/.gitignore +++ b/.gitignore @@ -333,3 +333,6 @@ ASALocalRun/ # MacOS .DS_Store + +# Fake database +*fakedatabase.db diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs index 789d167f..992134c4 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs @@ -9,7 +9,6 @@ using IdentityServer4.Models; using IdentityServer4.Services; using IdentityServer4.Stores; -using IdentityServer4.Test; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -82,36 +81,33 @@ public async Task Login(LoginInputModel model, string button) // the user clicked the "cancel" button if (button != "login") { - if (context != null) - { - // if the user cancels, send a result back into IdentityServer as if they - // denied the consent (even if this client does not require consent). - // this will send back an access denied OIDC error response to the client. - await _interaction.GrantConsentAsync(context, ConsentResponse.Denied); + if (context == null) + return Redirect("~/"); - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - if (await _clientStore.IsPkceClientAsync(context.ClientId)) - { - // if the client is PKCE then we assume it's native, so this change in how to - // return the response is for better UX for the end user. - return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); - } + // if the user cancels, send a result back into IdentityServer as if they + // denied the consent (even if this client does not require consent). + // this will send back an access denied OIDC error response to the client. + await _interaction.GrantConsentAsync(context, ConsentResponse.Denied); - return Redirect(model.ReturnUrl); - } - else + // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null + if (await _clientStore.IsPkceClientAsync(context.ClientId)) { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); } + + return Redirect(model.ReturnUrl); + + // since we don't have a valid context, then we just go back to the home page } if (ModelState.IsValid) { // validate username/password against in-memory store - if (_users.ValidateCredentials(model.Username, model.Password)) + if (await _users.ValidateCredentials(model.Username, model.Password)) { - var user = _users.FindByUsername(model.Username); + var user = await _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.ClientId)); // only set explicit expiration here if user chooses "remember me". @@ -124,7 +120,7 @@ public async Task Login(LoginInputModel model, string button) IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; - }; + } // issue authentication cookie with subject ID and username await HttpContext.SignInAsync(user.SubjectId, user.Username, props); @@ -144,18 +140,13 @@ public async Task Login(LoginInputModel model, string button) // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) - { return Redirect(model.ReturnUrl); - } - else if (string.IsNullOrEmpty(model.ReturnUrl)) - { + + if (string.IsNullOrEmpty(model.ReturnUrl)) return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } + + // user might have clicked on a malicious link - should be logged + throw new Exception("invalid return URL"); } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.ClientId)); @@ -166,7 +157,6 @@ public async Task Login(LoginInputModel model, string button) var vm = await BuildLoginViewModelAsync(model); return View(vm); } - /// /// Show logout page @@ -227,7 +217,6 @@ public IActionResult AccessDenied() return View(); } - /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ @@ -243,7 +232,7 @@ private async Task BuildLoginViewModelAsync(string returnUrl) { EnableLocalLogin = local, ReturnUrl = returnUrl, - Username = context?.LoginHint + Username = context.LoginHint }; if (!local) @@ -332,7 +321,7 @@ private async Task BuildLoggedOutViewModelAsync(string logou { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, - ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, + ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs index b974de5f..fe45432d 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs @@ -1,18 +1,11 @@ using IdentityModel; -using IdentityServer4.Events; -using IdentityServer4.Extensions; using IdentityServer4.Models; using IdentityServer4.Stores; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Primitives; using OpenActive.FakeDatabase.NET; using System; -using System.Collections.Generic; using System.Linq; -using System.Numerics; -using System.Security.Cryptography; -using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -23,11 +16,11 @@ namespace IdentityServer // [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Consumes("application/json")] [Produces("application/json")] - public class ClientResistrationController : ControllerBase + public class ClientRegistrationController : ControllerBase { private readonly IClientStore _clients; - public ClientResistrationController(IClientStore clients) + public ClientRegistrationController(IClientStore clients) { _clients = clients; } @@ -38,44 +31,33 @@ public ClientResistrationController(IClientStore clients) [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task PostAsync([FromBody] ClientRegistrationModel model) { - if (model.GrantTypes == null) - { - model.GrantTypes = new[] { OidcConstants.GrantTypes.AuthorizationCode, OidcConstants.GrantTypes.RefreshToken }; - } + model.GrantTypes ??= new[] {OidcConstants.GrantTypes.AuthorizationCode, OidcConstants.GrantTypes.RefreshToken}; if (model.GrantTypes.Any(x => x == OidcConstants.GrantTypes.Implicit) || model.GrantTypes.Any(x => x == OidcConstants.GrantTypes.AuthorizationCode)) { if (!model.RedirectUris.Any()) - { return BadRequest("A redirect URI is required for the supplied grant type."); - } if (model.RedirectUris.Any(redirectUri => !Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))) - { return BadRequest("One or more of the redirect URIs are invalid."); - } } // generate a secret for the client var key = KeyGenerator.GenerateSecret(); - - StringValues headerValues; var registrationKey = string.Empty; - if (Request.Headers.TryGetValue("Authorization", out headerValues)) - { + if (Request.Headers.TryGetValue("Authorization", out var headerValues)) registrationKey = headerValues.FirstOrDefault().Substring("Bearer ".Length); - } // update the booking system - var bookingPartner = FakeBookingSystem.Database.GetBookingPartnerByInitialAccessToken(registrationKey); + var bookingPartner = await FakeBookingSystem.Database.GetBookingPartnerByInitialAccessToken(registrationKey); if (bookingPartner == null) return Unauthorized("Initial Access Token is not valid, or is expired"); bookingPartner.Registered = true; bookingPartner.ClientSecret = key.Sha256(); bookingPartner.Name = model.ClientName; - bookingPartner.ClientProperties = new OpenActive.FakeDatabase.NET.ClientModel + bookingPartner.ClientProperties = new ClientModel { ClientUri = model.ClientUri, LogoUri = model.LogoUri, @@ -83,14 +65,14 @@ public async Task PostAsync([FromBody] ClientRegistrationModel mo RedirectUris = model.RedirectUris, Scope = model.Scope, }; - FakeBookingSystem.Database.SaveBookingPartner(bookingPartner); + + await FakeBookingSystem.Database.SaveBookingPartner(bookingPartner); // Read the updated client from the database and reflect back in the request var client = await _clients.FindClientByIdAsync(bookingPartner.ClientId); if (bookingPartner.ClientSecret != client.ClientSecrets?.FirstOrDefault()?.Value) - { return Problem(title: "New client secret not updated in cache", statusCode: 500); - } + var response = new ClientRegistrationResponse { ClientId = client.ClientId, @@ -103,7 +85,8 @@ public async Task PostAsync([FromBody] ClientRegistrationModel mo Scope = string.Join(' ', client.AllowedScopes) }; - return CreatedAtAction("ClientRegistration", response); + var baseUrl = $"{Request.Scheme}://{Request.Host.Value}/connect/register"; + return Created($"{baseUrl}/{client.ClientId}", response); } } @@ -122,7 +105,7 @@ public class ClientRegistrationModel public string[] GrantTypes { get; set; } [JsonPropertyName(OidcConstants.ClientMetadata.RedirectUris)] - public string[] RedirectUris { get; set; } = new string[] {}; + public string[] RedirectUris { get; set; } = {}; public string Scope { get; set; } = "openid profile email"; } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs index 720b7990..b89d7b18 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs @@ -1,8 +1,6 @@ using IdentityServer4.Models; using IdentityServer4.Stores; -using Microsoft.Extensions.Logging; using OpenActive.FakeDatabase.NET; -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -11,16 +9,18 @@ namespace IdentityServer { public class ClientStore : IClientStore { - public Task FindClientByIdAsync(string clientId) + public async Task FindClientByIdAsync(string clientId) { - var bookingPartner = FakeBookingSystem.Database.GetBookingPartner(clientId); - return Task.FromResult(this.ConvertToIS4Client(bookingPartner)); + var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); + return ConvertToIS4Client(bookingPartner); } private Client ConvertToIS4Client(BookingPartnerTable bookingPartner) { - if (bookingPartner == null) return null; - return new Client() + if (bookingPartner == null) + return null; + + return new Client { Enabled = bookingPartner.Registered, ClientId = bookingPartner.ClientId, diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs index 26cb3e2e..9150fc24 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs @@ -1,7 +1,6 @@ // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - using OpenActive.FakeDatabase.NET; using System; using System.Collections.Generic; @@ -25,5 +24,4 @@ public class BookingPartnerModel public IEnumerable ApiGrantNames { get; set; } public BookingPartnerTable BookingPartner { get; set; } } - } \ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs index 1915f813..29bdc820 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs @@ -1,21 +1,16 @@ // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - using IdentityServer4.Services; using IdentityServer4.Stores; using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using IdentityServer4.Events; using IdentityServer4.Extensions; using OpenActive.FakeDatabase.NET; -using System.Security.Cryptography; using System; -using IdentityServer4.Models; -using System.Text.RegularExpressions; +using System.Linq; using IdentityServer; namespace src @@ -29,17 +24,12 @@ public class BookingPartnersController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; - private readonly IResourceStore _resources; private readonly IEventService _events; - public BookingPartnersController(IIdentityServerInteractionService interaction, - IClientStore clients, - IResourceStore resources, - IEventService events) + public BookingPartnersController(IIdentityServerInteractionService interaction, IClientStore clients, IEventService events) { _interaction = interaction; _clients = clients; - _resources = resources; _events = events; } @@ -49,17 +39,19 @@ public BookingPartnersController(IIdentityServerInteractionService interaction, [HttpGet] public async Task Index() { - return View("Index", await BuildViewModelAsync()); + return View("Index", await BuildViewModel()); } /// /// Show list of grants /// [HttpGet] - public async Task Edit(string Id) + public async Task Edit(string id) { - var content = await BuildBookingPartnerViewModelAsync(Id); - if (content == null) return NotFound(); + var content = await BuildBookingPartnerViewModel(id); + if (content == null) + return NotFound(); + return View("BookingPartnerEdit", content); } @@ -79,7 +71,7 @@ public async Task Create() [ValidateAntiForgeryToken] public async Task CreateBookingPartner(string email, string bookingPartnerName) { - var newBookingPartner = new BookingPartnerTable() + var newBookingPartner = new BookingPartnerTable { ClientId = Guid.NewGuid().ToString(), Name = bookingPartnerName, @@ -92,9 +84,28 @@ public async Task CreateBookingPartner(string email, string booki BookingsSuspended = false }; - FakeBookingSystem.Database.AddBookingPartner(newBookingPartner); + await FakeBookingSystem.Database.AddBookingPartner(newBookingPartner); + return View("BookingPartnerEdit", await BuildBookingPartnerViewModel(newBookingPartner.ClientId)); + } + + /// + /// Handle postback to revoke a client + /// + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult ManageKeys(string clientId) + { + return RedirectToAction("Index"); + } - return View("BookingPartnerEdit", await BuildBookingPartnerViewModelAsync(newBookingPartner.ClientId)); + /// + /// Handle postback to revoke a client + /// + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult Restore(string clientId) + { + return RedirectToAction("Index"); } /// @@ -121,12 +132,7 @@ public async Task Suspend(string clientId) client.AllowedScopes.Remove("openactive-openbooking"); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); - FakeBookingSystem.Database.UpdateBookingPartnerScope( - clientId, - "openid profile openactive-ordersfeed", - true - ); - + await FakeBookingSystem.Database.UpdateBookingPartnerScope(clientId, "openid profile openactive-ordersfeed", true); return RedirectToAction("Index"); } @@ -137,10 +143,10 @@ public async Task Suspend(string clientId) [ValidateAntiForgeryToken] public async Task RegenerateKey(string clientId) { - var bookingPartner = FakeBookingSystem.Database.GetBookingPartner(clientId); - FakeBookingSystem.Database.SetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); + var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); + await FakeBookingSystem.Database.SetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); - return View("BookingPartnerEdit", await BuildBookingPartnerViewModelAsync(clientId)); + return View("BookingPartnerEdit", await BuildBookingPartnerViewModel(clientId)); } /// @@ -150,24 +156,24 @@ public async Task RegenerateKey(string clientId) [ValidateAntiForgeryToken] public async Task RegenerateAllKeys(string clientId) { - var bookingPartner = FakeBookingSystem.Database.GetBookingPartner(clientId); - FakeBookingSystem.Database.ResetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); + var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); + await FakeBookingSystem.Database.ResetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); // TODO: Is this cached in memory, does it need updating?? //var client = await _clients.FindClientByIdAsync(clientId); //client.ClientSecrets = new List() { new Secret(clientSecret.Sha256()) }; - return View("BookingPartnerEdit", await BuildBookingPartnerViewModelAsync(clientId)); + return View("BookingPartnerEdit", await BuildBookingPartnerViewModel(clientId)); } - private async Task BuildBookingPartnerViewModelAsync(string clientId) + private static async Task BuildBookingPartnerViewModel(string clientId) { // var client = await _clients.FindClientByIdAsync(clientId); - var bookingPartner = FakeBookingSystem.Database.GetBookingPartner(clientId); + var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); + if (bookingPartner == null) + return null; - if (bookingPartner == null) return null; - - return new BookingPartnerModel() + return new BookingPartnerModel { ClientId = bookingPartner.ClientId, ClientName = bookingPartner.Name, @@ -176,28 +182,20 @@ private async Task BuildBookingPartnerViewModelAsync(string BookingPartner = bookingPartner }; } - private async Task BuildViewModelAsync() + + private static async Task BuildViewModel() { - var bookingPartners = FakeBookingSystem.Database.GetBookingPartners(); - var list = new List(); - foreach (var bookingPartner in bookingPartners) + var bookingPartners = await FakeBookingSystem.Database.GetBookingPartners(); + var list = bookingPartners.Select(bookingPartner => new BookingPartnerModel { - var item = new BookingPartnerModel() - { - ClientId = bookingPartner.ClientId, - ClientName = bookingPartner.Name, - ClientLogoUrl = bookingPartner.ClientProperties?.LogoUri, - ClientUrl = bookingPartner.ClientProperties?.ClientUri, - BookingPartner = bookingPartner - }; - - list.Add(item); - } - - return new BookingPartnerViewModel - { - BookingPartners = list - }; + ClientId = bookingPartner.ClientId, + ClientName = bookingPartner.Name, + ClientLogoUrl = bookingPartner.ClientProperties?.LogoUri, + ClientUrl = bookingPartner.ClientProperties?.ClientUri, + BookingPartner = bookingPartner + }).ToList(); + + return new BookingPartnerViewModel { BookingPartners = list }; } } } \ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs index 9408faa6..4ef01c1a 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs @@ -1,7 +1,6 @@ using IdentityServer4.Models; using IdentityServer4.Stores; using OpenActive.FakeDatabase.NET; -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -10,32 +9,28 @@ namespace IdentityServer { public class AcmePersistedGrantStore : IPersistedGrantStore { - public Task> GetAllAsync(string subjectId) + public async Task> GetAllAsync(string subjectId) { - var grants = FakeBookingSystem.Database.GetAllGrants(subjectId); - - List persistedGrants = new List(); - foreach(var grant in grants) + var grants = await FakeBookingSystem.Database.GetAllGrants(subjectId); + var persistedGrants = grants.Select(grant => new PersistedGrant { - persistedGrants.Add(new PersistedGrant() - { - Key = grant.Key, - Type = grant.Type, - SubjectId = grant.SubjectId, - ClientId = grant.ClientId, - CreationTime = grant.CreationTime, - Expiration = grant.Expiration, - Data = grant.Data - }); - } - return Task.FromResult>(persistedGrants); + Key = grant.Key, + Type = grant.Type, + SubjectId = grant.SubjectId, + ClientId = grant.ClientId, + CreationTime = grant.CreationTime, + Expiration = grant.Expiration, + Data = grant.Data + }).ToList(); + + return persistedGrants; } - public Task GetAsync(string key) + public async Task GetAsync(string key) { - var grant = FakeBookingSystem.Database.GetGrant(key); + var grant = await FakeBookingSystem.Database.GetGrant(key); - return Task.FromResult(grant != null ? new PersistedGrant() + return grant != null ? new PersistedGrant() { Key = grant.Key, Type = grant.Type, @@ -44,27 +39,31 @@ public Task GetAsync(string key) CreationTime = grant.CreationTime, Expiration = grant.Expiration, Data = grant.Data - } : null); + } : null; } - public async Task RemoveAllAsync(string subjectId, string clientId) + public Task RemoveAllAsync(string subjectId, string clientId) { FakeBookingSystem.Database.RemoveGrant(subjectId, clientId); + return Task.CompletedTask; } - public async Task RemoveAllAsync(string subjectId, string clientId, string type) + public Task RemoveAllAsync(string subjectId, string clientId, string type) { FakeBookingSystem.Database.RemoveGrant(subjectId, clientId, type); + return Task.CompletedTask; } - public async Task RemoveAsync(string key) + public Task RemoveAsync(string key) { FakeBookingSystem.Database.RemoveGrant(key); + return Task.CompletedTask; } - public async Task StoreAsync(PersistedGrant grant) + public Task StoreAsync(PersistedGrant grant) { FakeBookingSystem.Database.AddGrant(grant.Key, grant.Type, grant.SubjectId, grant.ClientId, grant.CreationTime, grant.Expiration, grant.Data); + return Task.CompletedTask; } } } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Profile/ProfileService.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Profile/ProfileService.cs index 510b6a4e..08756234 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Profile/ProfileService.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Profile/ProfileService.cs @@ -1,13 +1,8 @@ using IdentityServer4.Extensions; using IdentityServer4.Models; using IdentityServer4.Services; -using IdentityServer4.Test; -using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; using System.Linq; -using System.Security.Claims; using System.Threading.Tasks; namespace IdentityServer @@ -40,14 +35,14 @@ public ProfileService(IUserRepository users, ILogger logger) /// /// The context. /// - public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) + public virtual async Task GetProfileDataAsync(ProfileDataRequestContext context) { context.LogProfileRequest(Logger); // Claims added here are defined be the API and RESOURCE configurations in Config.cs, only the requested claims will be added to the IssuedClaims collection if (context.RequestedClaimTypes.Any()) { - var user = Users.FindBySubjectId(context.Subject.GetSubjectId()); + var user = await Users.FindBySubjectId(context.Subject.GetSubjectId()); if (user != null) { context.AddRequestedClaims(user.Claims); @@ -57,13 +52,10 @@ public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) { context.IssuedClaims.Add(sellerIdClaim); } - } } context.LogIssuedClaims(Logger); - - return Task.CompletedTask; } /// @@ -72,15 +64,12 @@ public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) /// /// The context. /// - public virtual Task IsActiveAsync(IsActiveContext context) + public virtual async Task IsActiveAsync(IsActiveContext context) { - Logger.LogDebug("IsActive called from: {caller}", context.Caller); + Logger.LogDebug("IsActive called from: {Caller}", context.Caller); - var user = Users.FindBySubjectId(context.Subject.GetSubjectId()); + var user = await Users.FindBySubjectId(context.Subject.GetSubjectId()); context.IsActive = user?.IsActive == true; - - return Task.CompletedTask; } - } } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/Config.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/Config.cs index 0b7b2bac..0e949d76 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/Config.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/Config.cs @@ -3,7 +3,6 @@ using IdentityModel; -using IdentityServer4; using IdentityServer4.Models; using System.Collections.Generic; diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/CustomIdentityResource.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/CustomIdentityResource.cs index b4f87edb..d5ae5b43 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/CustomIdentityResource.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Resources/CustomIdentityResource.cs @@ -1,8 +1,5 @@ using IdentityServer4.Models; -using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace IdentityServer { diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Extensions.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Extensions.cs index 8ebf91c5..358a7df1 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Extensions.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Extensions.cs @@ -1,8 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace IdentityServer { diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Interfaces.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Interfaces.cs index d81a4d1f..2900161a 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Interfaces.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/Interfaces.cs @@ -1,15 +1,11 @@ -using OpenActive.FakeDatabase.NET; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace IdentityServer { public interface IUserRepository { - bool ValidateCredentials(string username, string password); - UserWithClaims FindBySubjectId(string subjectId); - User FindByUsername(string username); + Task ValidateCredentials(string username, string password); + Task FindBySubjectId(string subjectId); + Task FindByUsername(string username); } } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/ResourceOwnerPasswordValidator.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/ResourceOwnerPasswordValidator.cs index 95224345..07025669 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/ResourceOwnerPasswordValidator.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/ResourceOwnerPasswordValidator.cs @@ -1,8 +1,5 @@ using IdentityModel; using IdentityServer4.Validation; -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace IdentityServer @@ -16,16 +13,14 @@ public ResourceOwnerPasswordValidator(IUserRepository userRepository) _userRepository = userRepository; } - public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) + public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { // context.Username refers to the cardId; context.Password refers to Lastname - if (_userRepository.ValidateCredentials(context.UserName, context.Password)) + if (await _userRepository.ValidateCredentials(context.UserName, context.Password)) { - var user = _userRepository.FindByUsername(context.UserName); + var user = await _userRepository.FindByUsername(context.UserName); context.Result = new GrantValidationResult(user.SubjectId, OidcConstants.AuthenticationMethods.Password); } - - return Task.FromResult(0); } } } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs index 09a9bcc8..ae3b5089 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Users/UserRepository.cs @@ -1,8 +1,5 @@ -using IdentityModel; -using OpenActive.FakeDatabase.NET; -using System; +using OpenActive.FakeDatabase.NET; using System.Collections.Generic; -using System.Linq; using System.Security.Claims; using System.Threading.Tasks; @@ -10,33 +7,28 @@ namespace IdentityServer { public class UserRepository : IUserRepository { - private string jsonLdIdBaseUrl; + private readonly string _jsonLdIdBaseUrl; public UserRepository(string jsonLdIdBaseUrl) { - this.jsonLdIdBaseUrl = jsonLdIdBaseUrl; + this._jsonLdIdBaseUrl = jsonLdIdBaseUrl; } - public bool ValidateCredentials(string username, string password) + public Task ValidateCredentials(string username, string password) { return FakeBookingSystem.Database.ValidateSellerUserCredentials(username, password); } - public UserWithClaims FindBySubjectId(string subjectId) + public async Task FindBySubjectId(string subjectId) { - if (long.TryParse(subjectId, out long longSubjectId)) - { - return GetUserFromSellerUserWithClaims(FakeBookingSystem.Database.GetSellerUserById(longSubjectId)); - } - else - { - return null; - } + return long.TryParse(subjectId, out var longSubjectId) + ? GetUserFromSellerUserWithClaims(await FakeBookingSystem.Database.GetSellerUserById(longSubjectId)) + : null; } - public User FindByUsername(string username) + public async Task FindByUsername(string username) { - return GetUserFromSellerUser(FakeBookingSystem.Database.GetSellerUser(username)); + return GetUserFromSellerUser(await FakeBookingSystem.Database.GetSellerUser(username)); } // TODO: Make this an extension method @@ -58,16 +50,19 @@ private UserWithClaims GetUserFromSellerUserWithClaims(SellerUserTable sellerUse IsActive = true, Claims = new List() }; + AddClaimIfNotNull(user.Claims, "https://openactive.io/sellerName", sellerUser.SellerTable.Name); - AddClaimIfNotNull(user.Claims, "https://openactive.io/sellerId", jsonLdIdBaseUrl + "/api/identifiers/sellers/" + sellerUser.SellerTable.Id); + AddClaimIfNotNull(user.Claims, "https://openactive.io/sellerId", _jsonLdIdBaseUrl + "/api/identifiers/sellers/" + sellerUser.SellerTable.Id); AddClaimIfNotNull(user.Claims, "https://openactive.io/sellerUrl", sellerUser.SellerTable.Url); AddClaimIfNotNull(user.Claims, "https://openactive.io/sellerLogo", sellerUser.SellerTable.LogoUrl); return user; } - private User GetUserFromSellerUser(SellerUserTable sellerUser) + private static User GetUserFromSellerUser(SellerUserTable sellerUser) { - if (sellerUser == null) return null; + if (sellerUser == null) + return null; + return new User { Username = sellerUser.Username, diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Program.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Program.cs index 8c6d9a5d..e5fb8053 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Program.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Program.cs @@ -43,7 +43,7 @@ public static int Main(string[] args) } catch (Exception ex) { - Log.Fatal(ex, "Host terminated unexpectedly."); + Log.Fatal(ex, "Host terminated unexpectedly"); return 1; } finally diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Startup.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Startup.cs index b759c40f..aaf3bebf 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Startup.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Startup.cs @@ -1,15 +1,12 @@ // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using IdentityServer4.Services; using IdentityServer4.Stores; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Configuration; -using src; namespace IdentityServer { diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml index b010dae8..e94a7db3 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml @@ -1,7 +1,6 @@ @model BookingPartnerModel -@using System; -
+
- public static FakeDatabase Database { get; } = FakeDatabase.GetPrepopulatedFakeDatabase(); + public static FakeDatabase Database { get; } = FakeDatabase.GetPrepopulatedFakeDatabase().Result; public static void Initialise() { @@ -31,13 +32,19 @@ public static void Initialise() // This SQLite database file is shared between the Booking System and Identity Server, and // Initialise() must be called on startup of each to ensure they do not wipe the database // on the first call to it +#pragma warning disable 4014 Database.GetBookingPartners(); +#pragma warning restore 4014 } public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan) { - if (timeSpan == TimeSpan.Zero) return dateTime; // Or could throw an ArgumentException - if (dateTime == DateTime.MinValue || dateTime == DateTime.MaxValue) return dateTime; // do not modify "guard" values + if (timeSpan == TimeSpan.Zero) + return dateTime; // Or could throw an ArgumentException + + if (dateTime == DateTime.MinValue || dateTime == DateTime.MaxValue) + return dateTime; // do not modify "guard" values + return dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks)); } } @@ -54,7 +61,8 @@ internal static class HashExtensions /// A hash public static string Sha256(this string input) { - if (string.IsNullOrWhiteSpace(input)) return string.Empty; + if (string.IsNullOrWhiteSpace(input)) + return string.Empty; using (var sha = SHA256.Create()) { @@ -75,7 +83,7 @@ public InMemorySQLite() { // ServiceStack registers a memory cache client by default https://docs.servicestack.net/caching // There are issues with transactions when using full in-memory SQLite. To workaround this, we create a temporary file and use this to hold the SQLite database. - string connectionString = Path.GetTempPath() + "openactive-fakedatabase.db"; + var connectionString = Path.GetTempPath() + "openactive-fakedatabase.db"; Database = new OrmLiteConnectionFactory(connectionString, SqliteDialect.Provider); using (var connection = Database.Open()) @@ -172,35 +180,35 @@ static FakeDatabase() /// /// TODO: Call this on a schedule from both .NET Core and .NET Framework reference implementations /// - public void CleanupExpiredLeases() + public async Task CleanupExpiredLeases() { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var occurrenceIds = new List(); var slotIds = new List(); - foreach (var order in db.Select(x => x.LeaseExpires < DateTimeOffset.Now)) + foreach (var order in await db.SelectAsync(x => x.LeaseExpires < DateTimeOffset.Now)) { // ReSharper disable twice PossibleInvalidOperationException - occurrenceIds.AddRange(db.Select(x => x.OrderId == order.OrderId && x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value)); - slotIds.AddRange(db.Select(x => x.OrderId == order.OrderId && x.SlotId.HasValue).Select(x => x.SlotId.Value)); - db.Delete(x => x.OrderId == order.OrderId); - db.Delete(x => x.OrderId == order.OrderId); + occurrenceIds.AddRange((await db.SelectAsync(x => x.OrderId == order.OrderId && x.OccurrenceId.HasValue)).Select(x => x.OccurrenceId.Value)); + slotIds.AddRange((await db.SelectAsync(x => x.OrderId == order.OrderId && x.SlotId.HasValue)).Select(x => x.SlotId.Value)); + await db.DeleteAsync(x => x.OrderId == order.OrderId); + await db.DeleteAsync(x => x.OrderId == order.OrderId); } - RecalculateSpaces(db, occurrenceIds.Distinct()); - RecalculateSlotUses(db, slotIds.Distinct()); + await RecalculateSpaces(db, occurrenceIds.Distinct()); + await RecalculateSlotUses(db, slotIds.Distinct()); } } - public static bool AddLease(string clientId, Guid uuid, BrokerRole brokerRole, string brokerName, Uri brokerUrl, string brokerTelephone, long? sellerId, string customerEmail, DateTimeOffset leaseExpires, FakeDatabaseTransaction transaction) + public static async Task AddLease(string clientId, Guid uuid, BrokerRole brokerRole, string brokerName, Uri brokerUrl, string brokerTelephone, long? sellerId, string customerEmail, DateTimeOffset leaseExpires, FakeDatabaseTransaction transaction) { var db = transaction.DatabaseConnection; - var existingOrder = db.Single(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); + var existingOrder = await db.SingleAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); if (existingOrder == null) { - db.Insert(new OrderTable + await db.InsertAsync(new OrderTable { ClientId = clientId, OrderId = uuid.ToString(), @@ -215,32 +223,29 @@ public static bool AddLease(string clientId, Guid uuid, BrokerRole brokerRole, s LeaseExpires = leaseExpires.DateTime, VisibleInOrdersFeed = FeedVisibility.None }); + return true; } + // Return false if there's a clash with an existing Order or OrderProposal - else if (existingOrder.OrderMode != OrderMode.Lease || existingOrder.Deleted) - { + if (existingOrder.OrderMode != OrderMode.Lease || existingOrder.Deleted) return false; - } - // Reuse existing lease if it exists - else - { - existingOrder.BrokerRole = brokerRole; - existingOrder.BrokerName = brokerName; - existingOrder.BrokerUrl = brokerUrl; - existingOrder.BrokerTelephone = brokerTelephone; - existingOrder.SellerId = sellerId ?? 1; - existingOrder.CustomerEmail = customerEmail; - existingOrder.OrderMode = OrderMode.Lease; - existingOrder.LeaseExpires = leaseExpires.DateTime; - db.Update(existingOrder); - - // TODO: Remove this and improve leasing logic to add/update rather than delete/replace - // Remove previous lease - db.Delete(x => x.OrderId == existingOrder.OrderId); - return true; - } + // Reuse existing lease if it exists + existingOrder.BrokerRole = brokerRole; + existingOrder.BrokerName = brokerName; + existingOrder.BrokerUrl = brokerUrl; + existingOrder.BrokerTelephone = brokerTelephone; + existingOrder.SellerId = sellerId ?? 1; + existingOrder.CustomerEmail = customerEmail; + existingOrder.OrderMode = OrderMode.Lease; + existingOrder.LeaseExpires = leaseExpires.DateTime; + await db.UpdateAsync(existingOrder); + + // TODO: Remove this and improve leasing logic to add/update rather than delete/replace + // Remove previous lease + await db.DeleteAsync(x => x.OrderId == existingOrder.OrderId); + return true; } /// @@ -249,23 +254,21 @@ public static bool AddLease(string clientId, Guid uuid, BrokerRole brokerRole, s /// /// /// - public bool UpdateFacilityUseName(long slotId, string newName) + public async Task UpdateFacilityUseName(long slotId, string newName) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var query = db.From() - .LeftJoin() - .Where(x => x.Id == slotId) - .And(y => !y.Deleted); - var facilityUse = db.Select(query).Single(); + .LeftJoin() + .Where(x => x.Id == slotId) + .And(y => !y.Deleted); + var facilityUse = (await db.SelectAsync(query)).Single(); if (facilityUse == null) - { return false; - } facilityUse.Name = newName; facilityUse.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(facilityUse); + await db.UpdateAsync(facilityUse); return true; } } @@ -276,20 +279,18 @@ public bool UpdateFacilityUseName(long slotId, string newName) /// /// /// - public bool UpdateFacilitySlotStartAndEndTimeByPeriodInMins(long slotId, int numberOfMins) + public async Task UpdateFacilitySlotStartAndEndTimeByPeriodInMins(long slotId, int numberOfMins) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var slot = db.Single(x => x.Id == slotId && !x.Deleted); + var slot = await db.SingleAsync(x => x.Id == slotId && !x.Deleted); if (slot == null) - { return false; - } - slot.Start.AddMinutes(numberOfMins); - slot.End.AddMinutes(numberOfMins); + slot.Start = slot.Start.AddMinutes(numberOfMins); + slot.End = slot.End.AddMinutes(numberOfMins); slot.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(slot); + await db.UpdateAsync(slot); return true; } } @@ -301,24 +302,22 @@ public bool UpdateFacilitySlotStartAndEndTimeByPeriodInMins(long slotId, int num /// /// /// - public bool UpdateFacilityUseLocationLatLng(long slotId, decimal newLat, decimal newLng) + public async Task UpdateFacilityUseLocationLatLng(long slotId, decimal newLat, decimal newLng) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var query = db.From() - .LeftJoin() - .Where(x => x.Id == slotId) - .And(y => !y.Deleted); - var facilityUse = db.Select(query).Single(); + .LeftJoin() + .Where(x => x.Id == slotId) + .And(y => !y.Deleted); + var facilityUse = (await db.SelectAsync(query)).Single(); if (facilityUse == null) - { return false; - } facilityUse.LocationLat = newLat; facilityUse.LocationLng = newLng; facilityUse.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(facilityUse); + await db.UpdateAsync(facilityUse); return true; } } @@ -329,23 +328,21 @@ public bool UpdateFacilityUseLocationLatLng(long slotId, decimal newLat, decimal /// /// /// - public bool UpdateClassTitle(long occurrenceId, string newTitle) + public async Task UpdateClassTitle(long occurrenceId, string newTitle) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var query = db.From() .LeftJoin() .Where(x => x.Id == occurrenceId) .And(y => !y.Deleted); - var classInstance = db.Select(query).Single(); + var classInstance = await db.SingleAsync(query); if (classInstance == null) - { return false; - } classInstance.Title = newTitle; classInstance.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(classInstance); + await db.UpdateAsync(classInstance); return true; } } @@ -356,20 +353,18 @@ public bool UpdateClassTitle(long occurrenceId, string newTitle) /// /// /// - public bool UpdateScheduledSessionStartAndEndTimeByPeriodInMins(long occurrenceId, int numberOfMins) + public async Task UpdateScheduledSessionStartAndEndTimeByPeriodInMins(long occurrenceId, int numberOfMins) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var occurrence = db.Single(x => x.Id == occurrenceId && !x.Deleted); + var occurrence = await db.SingleAsync(x => x.Id == occurrenceId && !x.Deleted); if (occurrence == null) - { return false; - } - occurrence.Start.AddMinutes(numberOfMins); - occurrence.End.AddMinutes(numberOfMins); + occurrence.Start = occurrence.Start.AddMinutes(numberOfMins); + occurrence.End = occurrence.End.AddMinutes(numberOfMins); occurrence.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(occurrence); + await db.UpdateAsync(occurrence); return true; } } @@ -381,42 +376,38 @@ public bool UpdateScheduledSessionStartAndEndTimeByPeriodInMins(long occurrenceI /// /// /// - public bool UpdateSessionSeriesLocationLatLng(long occurrenceId, decimal newLat, decimal newLng) + public async Task UpdateSessionSeriesLocationLatLng(long occurrenceId, decimal newLat, decimal newLng) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var query = db.From() .LeftJoin() .Where(x => x.Id == occurrenceId) .And(y => !y.Deleted); - var classInstance = db.Select(query).Single(); + var classInstance = await db.SingleAsync(query); if (classInstance == null) - { return false; - } classInstance.LocationLat = newLat; classInstance.LocationLng = newLng; classInstance.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(classInstance); + await db.UpdateAsync(classInstance); return true; } } - public bool UpdateAccess(Guid uuid, bool updateAccessPass = false, bool updateAccessCode = false, bool updateAccessChannel = false) + public async Task UpdateAccess(Guid uuid, bool updateAccessPass = false, bool updateAccessCode = false, bool updateAccessChannel = false) { if (!updateAccessPass && !updateAccessCode && !updateAccessChannel) - { return false; - } - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - OrderTable order = db.Single(x => x.OrderId == uuid.ToString() && !x.Deleted); + OrderTable order = await db.SingleAsync(x => x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) { - List orderItems = db.Select(x => x.OrderId == order.OrderId); + List orderItems = await db.SelectAsync(x => x.OrderId == order.OrderId); foreach (OrderItemsTable orderItem in orderItems) { @@ -441,32 +432,30 @@ public bool UpdateAccess(Guid uuid, bool updateAccessPass = false, bool updateAc } orderItem.Modified = DateTimeOffset.Now.UtcTicks; - db.Save(orderItem); + await db.SaveAsync(orderItem); } } order.OrderModified = DateTimeOffset.Now.UtcTicks; order.VisibleInOrdersFeed = FeedVisibility.Visible; - db.Update(order); + await db.UpdateAsync(order); return true; } - else - { - return false; - } + + return false; } } - public bool UpdateOpportunityAttendance(Guid uuid, bool attended) + public async Task UpdateOpportunityAttendance(Guid uuid, bool attended) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - OrderTable order = db.Single(x => x.OrderId == uuid.ToString() && !x.Deleted); + OrderTable order = await db.SingleAsync(x => x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) { - List orderItems = db.Select(x => x.OrderId == order.OrderId); + List orderItems = await db.SelectAsync(x => x.OrderId == order.OrderId); foreach (OrderItemsTable orderItem in orderItems) { @@ -474,75 +463,71 @@ public bool UpdateOpportunityAttendance(Guid uuid, bool attended) { orderItem.Status = attended ? BookingStatus.Attended : BookingStatus.Absent; orderItem.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(orderItem); + await db.UpdateAsync(orderItem); } } order.OrderModified = DateTimeOffset.Now.UtcTicks; order.VisibleInOrdersFeed = FeedVisibility.Visible; - db.Update(order); + await db.UpdateAsync(order); return true; } - else - { - return false; - } + + return false; } } - public bool AddCustomerNotice(Guid uuid) + public async Task AddCustomerNotice(Guid uuid) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - OrderTable order = db.Single(x => x.OrderId == uuid.ToString() && !x.Deleted); + OrderTable order = await db.SingleAsync(x => x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) { - List orderItems = db.Select(x => x.OrderId == order.OrderId); + List orderItems = await db.SelectAsync(x => x.OrderId == order.OrderId); foreach (OrderItemsTable orderItem in orderItems) { if (orderItem.Status == BookingStatus.Confirmed || orderItem.Status == BookingStatus.None) { orderItem.CustomerNotice = $"customer notice message: {Faker.Random.String(10, minChar: 'a', maxChar: 'z')}"; orderItem.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(orderItem); + await db.UpdateAsync(orderItem); } } order.OrderModified = DateTimeOffset.Now.UtcTicks; order.VisibleInOrdersFeed = FeedVisibility.Visible; - db.Update(order); + await db.UpdateAsync(order); return true; } - else - { - return false; - } + + return false; } } - public void DeleteLease(string clientId, Guid uuid, long? sellerId) + public async Task DeleteLease(string clientId, Guid uuid, long? sellerId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { // TODO: Note this should throw an error if the Seller ID does not match, same as DeleteOrder - if (db.Exists(x => x.ClientId == clientId && x.OrderMode == OrderMode.Lease && x.OrderId == uuid.ToString() && (!sellerId.HasValue || x.SellerId == sellerId))) + if (await db.ExistsAsync(x => x.ClientId == clientId && x.OrderMode == OrderMode.Lease && x.OrderId == uuid.ToString() && (!sellerId.HasValue || x.SellerId == sellerId))) { // ReSharper disable twice PossibleInvalidOperationException - var occurrenceIds = db.Select(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct(); - var slotIds = db.Select(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct(); + var occurrenceIds = (await db.SelectAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OccurrenceId.HasValue)).Select(x => x.OccurrenceId.Value).Distinct(); + var slotIds = (await db.SelectAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId.HasValue)).Select(x => x.SlotId.Value).Distinct(); - db.Delete(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); - db.Delete(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); + await db.DeleteAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); + await db.DeleteAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); - RecalculateSpaces(db, occurrenceIds); - RecalculateSlotUses(db, slotIds); + await RecalculateSpaces(db, occurrenceIds); + await RecalculateSlotUses(db, slotIds); } } } - public static bool AddOrder( + public static async Task AddOrder( string clientId, Guid uuid, BrokerRole brokerRole, string brokerName, Uri brokerUrl, string brokerTelephone, long? sellerId, string customerEmail, CustomerType customerType, string customerOrganizationName, string customerIdentifier, string customerGivenName, string customerFamilyName, string customerTelephone, @@ -551,10 +536,10 @@ public static bool AddOrder( { var db = transaction.DatabaseConnection; - var existingOrder = db.Single(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); + var existingOrder = await db.SingleAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); if (existingOrder == null) { - db.Insert(new OrderTable + await db.InsertAsync(new OrderTable { ClientId = clientId, OrderId = uuid.ToString(), @@ -584,69 +569,65 @@ public static bool AddOrder( return true; } // Return false if there's a clash with an existing Order or OrderProposal - else if (existingOrder.OrderMode != OrderMode.Lease || existingOrder.Deleted) + + if (existingOrder.OrderMode != OrderMode.Lease || existingOrder.Deleted) { return false; } // Reuse existing lease if it exists - else - { - existingOrder.BrokerRole = brokerRole; - existingOrder.BrokerName = brokerName; - existingOrder.BrokerUrl = brokerUrl; - existingOrder.BrokerTelephone = brokerTelephone; - existingOrder.SellerId = sellerId ?? 1; - existingOrder.CustomerEmail = customerEmail; - existingOrder.CustomerType = customerType; - existingOrder.CustomerOrganizationName = customerOrganizationName; - existingOrder.CustomerIdentifier = customerIdentifier; - existingOrder.CustomerGivenName = customerGivenName; - existingOrder.CustomerFamilyName = customerFamilyName; - existingOrder.CustomerTelephone = customerTelephone; - existingOrder.PaymentIdentifier = paymentIdentifier; - existingOrder.PaymentName = paymentName; - existingOrder.PaymentProviderId = paymentProviderId; - existingOrder.PaymentAccountId = paymentAccountId; - existingOrder.TotalOrderPrice = totalOrderPrice; - existingOrder.OrderMode = proposalVersionUuid != null ? OrderMode.Proposal : OrderMode.Booking; - existingOrder.ProposalVersionId = proposalVersionUuid; - existingOrder.ProposalStatus = proposalStatus; - db.Update(existingOrder); - - return true; - } - } - - public (FakeDatabaseGetOrderResult, OrderTable, List) GetOrderAndOrderItems(string clientId, long? sellerId, Guid uuid) - { - using (var db = Mem.Database.Open()) - { - var order = db.Single(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && !x.Deleted && (!sellerId.HasValue || x.SellerId == sellerId)); + existingOrder.BrokerRole = brokerRole; + existingOrder.BrokerName = brokerName; + existingOrder.BrokerUrl = brokerUrl; + existingOrder.BrokerTelephone = brokerTelephone; + existingOrder.SellerId = sellerId ?? 1; + existingOrder.CustomerEmail = customerEmail; + existingOrder.CustomerType = customerType; + existingOrder.CustomerOrganizationName = customerOrganizationName; + existingOrder.CustomerIdentifier = customerIdentifier; + existingOrder.CustomerGivenName = customerGivenName; + existingOrder.CustomerFamilyName = customerFamilyName; + existingOrder.CustomerTelephone = customerTelephone; + existingOrder.PaymentIdentifier = paymentIdentifier; + existingOrder.PaymentName = paymentName; + existingOrder.PaymentProviderId = paymentProviderId; + existingOrder.PaymentAccountId = paymentAccountId; + existingOrder.TotalOrderPrice = totalOrderPrice; + existingOrder.OrderMode = proposalVersionUuid != null ? OrderMode.Proposal : OrderMode.Booking; + existingOrder.ProposalVersionId = proposalVersionUuid; + existingOrder.ProposalStatus = proposalStatus; + await db.UpdateAsync(existingOrder); + + return true; + } + + public async Task<(FakeDatabaseGetOrderResult, OrderTable, List)> GetOrderAndOrderItems(string clientId, long? sellerId, Guid uuid) + { + using (var db = await Mem.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); - var orderItems = db.Select(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); + var orderItems = await db.SelectAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString()); if (orderItems.Count == 0) return (FakeDatabaseGetOrderResult.OrderWasNotFound, null, null); return (FakeDatabaseGetOrderResult.OrderSuccessfullyGot, order, orderItems); } } - public (bool, ClassTable, OccurrenceTable, BookedOrderItemInfo) GetOccurrenceAndBookedOrderItemInfoByOccurrenceId(Guid uuid, long? occurrenceId) + public async Task<(bool, ClassTable, OccurrenceTable, BookedOrderItemInfo)> GetOccurrenceAndBookedOrderItemInfoByOccurrenceId(Guid uuid, long? occurrenceId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var query = db.From() .LeftJoin() - .Where((x) => x.Id == occurrenceId); + .Where(x => x.Id == occurrenceId); var rows = db.SelectMulti(query); - var hasFoundOccurrence = false; if (!rows.Any()) - { - return (hasFoundOccurrence, null, null, null); - } + return (true, null, null, null); + var (occurrence, thisClass) = rows.FirstOrDefault(); - var orderItem = db.Single(x => x.OrderId == uuid.ToString() && x.OccurrenceId == occurrenceId); - var bookedOrderItemInfo = (orderItem != null && orderItem.Status == BookingStatus.Confirmed) ? + var orderItem = await db.SingleAsync(x => x.OrderId == uuid.ToString() && x.OccurrenceId == occurrenceId); + var bookedOrderItemInfo = orderItem != null && orderItem.Status == BookingStatus.Confirmed ? new BookedOrderItemInfo { OrderItemId = orderItem.Id, @@ -659,9 +640,8 @@ public static bool AddOrder( } : null; - hasFoundOccurrence = true; return ( - hasFoundOccurrence, + true, thisClass, occurrence, bookedOrderItemInfo @@ -669,21 +649,19 @@ public static bool AddOrder( } } - public (bool, FacilityUseTable, SlotTable, BookedOrderItemInfo) GetSlotAndBookedOrderItemInfoBySlotId(Guid uuid, long? slotId) + public async Task<(bool, FacilityUseTable, SlotTable, BookedOrderItemInfo)> GetSlotAndBookedOrderItemInfoBySlotId(Guid uuid, long? slotId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var query = db.From() .LeftJoin() - .Where((x) => x.Id == slotId); + .Where(x => x.Id == slotId); var rows = db.SelectMulti(query); - var hasFoundOccurrence = false; if (!rows.Any()) - { - return (hasFoundOccurrence, null, null, null); - } + return (false, null, null, null); + var (slot, facilityUse) = rows.FirstOrDefault(); - var orderItem = db.Single(x => x.OrderId == uuid.ToString() && x.SlotId == slotId); + var orderItem = await db.SingleAsync(x => x.OrderId == uuid.ToString() && x.SlotId == slotId); var bookedOrderItemInfo = (orderItem != null && orderItem.Status == BookingStatus.Confirmed) ? new BookedOrderItemInfo { @@ -694,9 +672,8 @@ public static bool AddOrder( } : null; - hasFoundOccurrence = true; return ( - hasFoundOccurrence, + true, facilityUse, slot, bookedOrderItemInfo @@ -704,12 +681,12 @@ public static bool AddOrder( } } - public FakeDatabaseDeleteOrderResult DeleteOrder(string clientId, Guid uuid, long? sellerId) + public async Task DeleteOrder(string clientId, Guid uuid, long? sellerId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { // Set the Order to deleted in the feed, and erase all associated personal data - var order = db.Single(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OrderMode != OrderMode.Lease); + var order = await db.SingleAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OrderMode != OrderMode.Lease); if (order == null) { return FakeDatabaseDeleteOrderResult.OrderWasNotFound; @@ -725,24 +702,24 @@ public FakeDatabaseDeleteOrderResult DeleteOrder(string clientId, Guid uuid, lon order.Deleted = true; order.CustomerEmail = null; order.OrderModified = DateTimeOffset.Now.UtcTicks; - db.Update(order); + await db.UpdateAsync(order); - var occurrenceIds = db.Select(x => x.ClientId == clientId && x.OrderId == order.OrderId && x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct(); - var slotIds = db.Select(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct(); - db.Delete(x => x.ClientId == clientId && x.OrderId == order.OrderId); + var occurrenceIds = (await db.SelectAsync(x => x.ClientId == clientId && x.OrderId == order.OrderId && x.OccurrenceId.HasValue)).Select(x => x.OccurrenceId.Value).Distinct(); + var slotIds = (await db.SelectAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId.HasValue)).Select(x => x.SlotId.Value).Distinct(); + await db.DeleteAsync(x => x.ClientId == clientId && x.OrderId == order.OrderId); - RecalculateSpaces(db, occurrenceIds); - RecalculateSlotUses(db, slotIds); + await RecalculateSpaces(db, occurrenceIds); + await RecalculateSlotUses(db, slotIds); return FakeDatabaseDeleteOrderResult.OrderSuccessfullyDeleted; } } - public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForClassOccurrence(FakeDatabaseTransaction transaction, string clientId, long? sellerId, Guid uuid, long occurrenceId, long spacesRequested) + public static async Task<(ReserveOrderItemsResult, long?, long?)> LeaseOrderItemsForClassOccurrence(FakeDatabaseTransaction transaction, string clientId, long? sellerId, Guid uuid, long occurrenceId, long spacesRequested) { var db = transaction.DatabaseConnection; - var thisOccurrence = db.Single(x => x.Id == occurrenceId && !x.Deleted); - var thisClass = db.Single(x => x.Id == thisOccurrence.ClassId && !x.Deleted); + var thisOccurrence = await db.SingleAsync(x => x.Id == occurrenceId && !x.Deleted); + var thisClass = await db.SingleAsync(x => x.Id == thisOccurrence.ClassId && !x.Deleted); if (thisOccurrence == null || thisClass == null) return (ReserveOrderItemsResult.OpportunityNotFound, null, null); @@ -755,8 +732,8 @@ public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForClassOcc // Remove existing leases // Note a real implementation would likely maintain existing leases instead of removing and recreating them - db.Delete(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OccurrenceId == occurrenceId); - RecalculateSpaces(db, thisOccurrence); + await db.DeleteAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OccurrenceId == occurrenceId); + await RecalculateSpaces(db, thisOccurrence); // Only lease if all spaces requested are available if (thisOccurrence.RemainingSpaces - thisOccurrence.LeasedSpaces < spacesRequested) @@ -769,7 +746,7 @@ public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForClassOcc for (var i = 0; i < spacesRequested; i++) { - db.Insert(new OrderItemsTable + await db.InsertAsync(new OrderItemsTable { ClientId = clientId, Deleted = false, @@ -780,15 +757,15 @@ public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForClassOcc } // Update number of spaces remaining for the opportunity - RecalculateSpaces(db, thisOccurrence); + await RecalculateSpaces(db, thisOccurrence); return (ReserveOrderItemsResult.Success, null, null); } - public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForFacilitySlot(FakeDatabaseTransaction transaction, string clientId, long? sellerId, Guid uuid, long slotId, long spacesRequested) + public static async Task<(ReserveOrderItemsResult, long?, long?)> LeaseOrderItemsForFacilitySlot(FakeDatabaseTransaction transaction, string clientId, long? sellerId, Guid uuid, long slotId, long spacesRequested) { var db = transaction.DatabaseConnection; - var thisSlot = db.Single(x => x.Id == slotId && !x.Deleted); - var thisFacility = db.Single(x => x.Id == thisSlot.FacilityUseId && !x.Deleted); + var thisSlot = await db.SingleAsync(x => x.Id == slotId && !x.Deleted); + var thisFacility = await db.SingleAsync(x => x.Id == thisSlot.FacilityUseId && !x.Deleted); if (thisSlot == null || thisFacility == null) return (ReserveOrderItemsResult.OpportunityNotFound, null, null); @@ -801,8 +778,8 @@ public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForFacility // Remove existing leases // Note a real implementation would likely maintain existing leases instead of removing and recreating them - db.Delete(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId == slotId); - RecalculateSlotUses(db, thisSlot); + await db.DeleteAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId == slotId); + await RecalculateSlotUses(db, thisSlot); // Only lease if all spaces requested are available if (thisSlot.RemainingUses - thisSlot.LeasedUses < spacesRequested) @@ -815,7 +792,7 @@ public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForFacility for (var i = 0; i < spacesRequested; i++) { - db.Insert(new OrderItemsTable + await db.InsertAsync(new OrderItemsTable { ClientId = clientId, Deleted = false, @@ -826,7 +803,7 @@ public static (ReserveOrderItemsResult, long?, long?) LeaseOrderItemsForFacility } // Update number of spaces remaining for the opportunity - RecalculateSlotUses(db, thisSlot); + await RecalculateSlotUses(db, thisSlot); return (ReserveOrderItemsResult.Success, null, null); } @@ -843,7 +820,7 @@ public class BookedOrderItemInfo } // TODO this should reuse code of LeaseOrderItemsForClassOccurrence - public static (ReserveOrderItemsResult, List) BookOrderItemsForClassOccurrence( + public static async Task<(ReserveOrderItemsResult, List)> BookOrderItemsForClassOccurrence( FakeDatabaseTransaction transaction, string clientId, long? sellerId, @@ -856,8 +833,8 @@ bool proposal ) { var db = transaction.DatabaseConnection; - var thisOccurrence = db.Single(x => x.Id == occurrenceId && !x.Deleted); - var thisClass = db.Single(x => x.Id == thisOccurrence.ClassId && !x.Deleted); + var thisOccurrence = await db.SingleAsync(x => x.Id == occurrenceId && !x.Deleted); + var thisClass = await db.SingleAsync(x => x.Id == thisOccurrence.ClassId && !x.Deleted); if (thisOccurrence == null || thisClass == null) return (ReserveOrderItemsResult.OpportunityNotFound, null); @@ -870,8 +847,8 @@ bool proposal // Remove existing leases // Note a real implementation would likely maintain existing leases instead of removing and recreating them - db.Delete(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OccurrenceId == occurrenceId); - RecalculateSpaces(db, thisOccurrence); + await db.DeleteAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.OccurrenceId == occurrenceId); + await RecalculateSpaces(db, thisOccurrence); // Only lease if all spaces requested are available if (thisOccurrence.RemainingSpaces - thisOccurrence.LeasedSpaces < numberOfSpaces) @@ -898,7 +875,7 @@ bool proposal MeetingId = thisClass.AttendanceMode != AttendanceMode.Offline ? Faker.Random.String(length: 10, minChar: '0', maxChar: '9') : null, MeetingPassword = thisClass.AttendanceMode != AttendanceMode.Offline ? Faker.Random.String(length: 10, minChar: '0', maxChar: '9') : null }; - db.Save(orderItem); + await db.SaveAsync(orderItem); bookedOrderItemInfos.Add(new BookedOrderItemInfo { OrderItemId = orderItem.Id, @@ -911,12 +888,12 @@ bool proposal }); } - RecalculateSpaces(db, thisOccurrence); + await RecalculateSpaces(db, thisOccurrence); return (ReserveOrderItemsResult.Success, bookedOrderItemInfos); } // TODO this should reuse code of LeaseOrderItemsForFacilityOccurrence - public static (ReserveOrderItemsResult, List) BookOrderItemsForFacilitySlot( + public static async Task<(ReserveOrderItemsResult, List)> BookOrderItemsForFacilitySlot( FakeDatabaseTransaction transaction, string clientId, long? sellerId, @@ -929,8 +906,8 @@ bool proposal ) { var db = transaction.DatabaseConnection; - var thisSlot = db.Single(x => x.Id == slotId && !x.Deleted); - var thisFacility = db.Single(x => x.Id == thisSlot.FacilityUseId && !x.Deleted); + var thisSlot = await db.SingleAsync(x => x.Id == slotId && !x.Deleted); + var thisFacility = await db.SingleAsync(x => x.Id == thisSlot.FacilityUseId && !x.Deleted); if (thisSlot == null || thisFacility == null) return (ReserveOrderItemsResult.OpportunityNotFound, null); @@ -943,8 +920,8 @@ bool proposal // Remove existing leases // Note a real implementation would likely maintain existing leases instead of removing and recreating them - db.Delete(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId == slotId); - RecalculateSlotUses(db, thisSlot); + await db.DeleteAsync(x => x.ClientId == clientId && x.OrderId == uuid.ToString() && x.SlotId == slotId); + await RecalculateSlotUses(db, thisSlot); // Only lease if all spaces requested are available if (thisSlot.RemainingUses - thisSlot.LeasedUses < numberOfSpaces) @@ -969,7 +946,7 @@ bool proposal BarCodeText = Faker.Random.String(length: 10, minChar: '0', maxChar: '9') }; - db.Save(orderItem); + await db.SaveAsync(orderItem); bookedOrderItemInfos.Add(new BookedOrderItemInfo { @@ -980,18 +957,18 @@ bool proposal }); } - RecalculateSlotUses(db, thisSlot); + await RecalculateSlotUses(db, thisSlot); return (ReserveOrderItemsResult.Success, bookedOrderItemInfos); } - public bool CancelOrderItems(string clientId, long? sellerId, Guid uuid, List orderItemIds, bool customerCancelled, bool includeCancellationMessage = false) + public async Task CancelOrderItems(string clientId, long? sellerId, Guid uuid, List orderItemIds, bool customerCancelled, bool includeCancellationMessage = false) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { var order = customerCancelled - ? db.Single(x => x.ClientId == clientId && x.OrderMode == OrderMode.Booking && x.OrderId == uuid.ToString() && !x.Deleted) - : db.Single(x => x.OrderId == uuid.ToString() && !x.Deleted); + ? await db.SingleAsync(x => x.ClientId == clientId && x.OrderMode == OrderMode.Booking && x.OrderId == uuid.ToString() && !x.Deleted) + : await db.SingleAsync(x => x.OrderId == uuid.ToString() && !x.Deleted); if (order == null) return false; @@ -1053,11 +1030,9 @@ public bool CancelOrderItems(string clientId, long? sellerId, Guid uuid, List 0) { - var totalPrice = db.Select(x => + var totalPrice = (await db.SelectAsync(x => x.ClientId == clientId && x.OrderId == order.OrderId && - (x.Status == BookingStatus.Confirmed || x.Status == BookingStatus.Attended || x.Status == BookingStatus.Absent)).Sum(x => x.Price); + (x.Status == BookingStatus.Confirmed || x.Status == BookingStatus.Attended || x.Status == BookingStatus.Absent))).Sum(x => x.Price); order.TotalOrderPrice = totalPrice; order.VisibleInOrdersFeed = FeedVisibility.Visible; order.OrderModified = DateTimeOffset.Now.UtcTicks; - db.Update(order); + await db.UpdateAsync(order); // Note an actual implementation would need to handle different opportunity types here // Update the number of spaces available as a result of cancellation - RecalculateSpaces(db, updatedOrderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); - RecalculateSlotUses(db, updatedOrderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); + await RecalculateSpaces(db, updatedOrderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); + await RecalculateSlotUses(db, updatedOrderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); } transaction.Commit(); @@ -1096,9 +1071,9 @@ public bool CancelOrderItems(string clientId, long? sellerId, Guid uuid, List ReplaceOrderOpportunity(Guid uuid) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { var query = db.From() .Join() @@ -1108,7 +1083,7 @@ public bool ReplaceOrderOpportunity(Guid uuid) if (!orderItemsAndOrder.Any()) return false; var order = orderItemsAndOrder.First().Item2; - var orderItems = orderItemsAndOrder.Select(x => x.Item1); + var orderItems = orderItemsAndOrder.Select(x => x.Item1).AsList(); var index = Faker.Random.Int(0, orderItemsAndOrder.Count - 1); var orderItem = orderItemsAndOrder[index].Item1; @@ -1158,26 +1133,26 @@ public bool ReplaceOrderOpportunity(Guid uuid) return false; } - db.Update(orderItem); + await db.UpdateAsync(orderItem); - order.TotalOrderPrice = orderItems.Sum(x => x.Price); ; + order.TotalOrderPrice = orderItems.Sum(x => x.Price); order.VisibleInOrdersFeed = FeedVisibility.Visible; order.OrderModified = DateTimeOffset.Now.UtcTicks; - db.Update(order); + await db.UpdateAsync(order); // Note an actual implementation would need to handle different opportunity types here // Update the number of spaces available as a result of cancellation - RecalculateSpaces(db, orderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); - RecalculateSlotUses(db, orderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); + await RecalculateSpaces(db, orderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); + await RecalculateSlotUses(db, orderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); return true; } } - public bool AcceptOrderProposal(Guid uuid) + public async Task AcceptOrderProposal(Guid uuid) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var order = db.Single(x => x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); + var order = await db.SingleAsync(x => x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) { // This makes the call idempotent @@ -1187,21 +1162,19 @@ public bool AcceptOrderProposal(Guid uuid) order.ProposalStatus = ProposalStatus.SellerAccepted; order.VisibleInOrderProposalsFeed = FeedVisibility.Visible; order.OrderProposalModified = DateTimeOffset.Now.UtcTicks; - db.Update(order); + await db.UpdateAsync(order); } return true; } - else - { - return false; - } + + return false; } } - public bool AmendOrderProposal(Guid uuid, Guid version) + public async Task AmendOrderProposal(Guid uuid, Guid version) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var order = db.Single(x => x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); + var order = await db.SingleAsync(x => x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) { // This makes the call idempotent @@ -1211,23 +1184,21 @@ public bool AmendOrderProposal(Guid uuid, Guid version) order.ProposalVersionId = version; order.VisibleInOrderProposalsFeed = FeedVisibility.Visible; order.OrderProposalModified = DateTimeOffset.Now.UtcTicks; - db.Update(order); + await db.UpdateAsync(order); } return true; } - else - { - return false; - } + + return false; } } - public FakeDatabaseBookOrderProposalResult BookOrderProposal(string clientId, long? sellerId, Guid uuid, Guid? proposalVersionUuid) + public async Task BookOrderProposal(string clientId, long? sellerId, Guid uuid, Guid? proposalVersionUuid) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { // Note call is idempotent, so it might already be in the booked state - var order = db.Single(x => x.ClientId == clientId && (x.OrderMode == OrderMode.Proposal || x.OrderMode == OrderMode.Booking) && x.OrderId == uuid.ToString() && !x.Deleted); + var order = await db.SingleAsync(x => x.ClientId == clientId && (x.OrderMode == OrderMode.Proposal || x.OrderMode == OrderMode.Booking) && x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) { if (sellerId.HasValue && order.SellerId != sellerId) @@ -1243,13 +1214,13 @@ public FakeDatabaseBookOrderProposalResult BookOrderProposal(string clientId, lo return FakeDatabaseBookOrderProposalResult.OrderProposalNotAccepted; } List updatedOrderItems = new List(); - foreach (OrderItemsTable orderItem in db.Select(x => x.ClientId == clientId && x.OrderId == order.OrderId)) + foreach (OrderItemsTable orderItem in await db.SelectAsync(x => x.ClientId == clientId && x.OrderId == order.OrderId)) { if (orderItem.Status != BookingStatus.Confirmed) { updatedOrderItems.Add(orderItem); orderItem.Status = BookingStatus.Confirmed; - db.Save(orderItem); + await db.SaveAsync(orderItem); } } // Update the status and modified date of the OrderProposal to update the feed, if something has changed @@ -1259,24 +1230,22 @@ public FakeDatabaseBookOrderProposalResult BookOrderProposal(string clientId, lo order.OrderMode = OrderMode.Booking; order.VisibleInOrderProposalsFeed = FeedVisibility.Archived; order.OrderProposalModified = DateTimeOffset.Now.UtcTicks; - db.Update(order); + await db.UpdateAsync(order); // Note an actual implementation would need to handle different opportunity types here // Update the number of spaces available as a result of cancellation - RecalculateSpaces(db, updatedOrderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); - RecalculateSlotUses(db, updatedOrderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); + await RecalculateSpaces(db, updatedOrderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); + await RecalculateSlotUses(db, updatedOrderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); } return FakeDatabaseBookOrderProposalResult.OrderSuccessfullyBooked; } - else - { - return FakeDatabaseBookOrderProposalResult.OrderWasNotFound; - } + + return FakeDatabaseBookOrderProposalResult.OrderWasNotFound; } } - public long GetNumberOfOtherLeaseForOccurrence(Guid uuid, long? occurrenceId) + public async Task GetNumberOfOtherLeaseForOccurrence(Guid uuid, long? occurrenceId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { return db.Count(x => x.OrderTable.OrderMode != OrderMode.Booking && x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && @@ -1286,9 +1255,9 @@ public long GetNumberOfOtherLeaseForOccurrence(Guid uuid, long? occurrenceId) } } - public long GetNumberOfOtherLeasesForSlot(Guid uuid, long? slotId) + public async Task GetNumberOfOtherLeasesForSlot(Guid uuid, long? slotId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { return db.Count(x => x.OrderTable.OrderMode != OrderMode.Booking && x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && @@ -1298,11 +1267,11 @@ public long GetNumberOfOtherLeasesForSlot(Guid uuid, long? slotId) } } - public bool RejectOrderProposal(string clientId, long? sellerId, Guid uuid, bool customerRejected) + public async Task RejectOrderProposal(string clientId, long? sellerId, Guid uuid, bool customerRejected) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var order = db.Single(x => (clientId == null || x.ClientId == clientId) && x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); + var order = await db.SingleAsync(x => (clientId == null || x.ClientId == clientId) && x.OrderMode == OrderMode.Proposal && x.OrderId == uuid.ToString() && !x.Deleted); if (order != null) { if (sellerId.HasValue && order.SellerId != sellerId) @@ -1315,95 +1284,94 @@ public bool RejectOrderProposal(string clientId, long? sellerId, Guid uuid, bool order.ProposalStatus = customerRejected ? ProposalStatus.CustomerRejected : ProposalStatus.SellerRejected; order.VisibleInOrderProposalsFeed = FeedVisibility.Visible; order.OrderProposalModified = DateTimeOffset.Now.UtcTicks; - db.Update(order); + await db.UpdateAsync(order); // Note an actual implementation would need to handle different opportunity types here // Update the number of spaces available as a result of cancellation - List updatedOrderItems = db.Select(x => (clientId == null || x.ClientId == clientId) && x.OrderId == order.OrderId).ToList(); - RecalculateSpaces(db, updatedOrderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); - RecalculateSlotUses(db, updatedOrderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); + List updatedOrderItems = (await db.SelectAsync(x => (clientId == null || x.ClientId == clientId) && x.OrderId == order.OrderId)).AsList(); + await RecalculateSpaces(db, updatedOrderItems.Where(x => x.OccurrenceId.HasValue).Select(x => x.OccurrenceId.Value).Distinct()); + await RecalculateSlotUses(db, updatedOrderItems.Where(x => x.SlotId.HasValue).Select(x => x.SlotId.Value).Distinct()); } return true; } - else - { - return false; - } + + return false; } } - public static void RecalculateSlotUses(IDbConnection db, SlotTable slot) + public static async Task RecalculateSlotUses(IDbConnection db, SlotTable slot) { if (slot == null) return; // Update number of leased spaces remaining for the opportunity - var leasedUses = db.LoadSelect(x => x.OrderTable.OrderMode != OrderMode.Booking && x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && x.OrderTable.ProposalStatus != ProposalStatus.SellerRejected && x.SlotId == slot.Id).Count(); + 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; slot.LeasedUses = leasedUses; // Update number of actual spaces remaining for the opportunity - var totalUsesTaken = db.LoadSelect(x => x.OrderTable.OrderMode == OrderMode.Booking && x.OccurrenceId == slot.Id && (x.Status == BookingStatus.Confirmed || x.Status == BookingStatus.Attended || x.Status == BookingStatus.Absent)).Count(); + 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; 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) // TODO: Document this! slot.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(slot); + await db.UpdateAsync(slot); } - public static void RecalculateSlotUses(IDbConnection db, IEnumerable slotIds) + public static async Task RecalculateSlotUses(IDbConnection db, IEnumerable slotIds) { foreach (var slotId in slotIds) { - var thisSlot = db.Single(x => x.Id == slotId && !x.Deleted); - RecalculateSlotUses(db, thisSlot); + var thisSlot = await db.SingleAsync(x => x.Id == slotId && !x.Deleted); + await RecalculateSlotUses(db, thisSlot); } } - public static void RecalculateSpaces(IDbConnection db, OccurrenceTable occurrence) + public static async Task RecalculateSpaces(IDbConnection db, OccurrenceTable occurrence) { if (occurrence == null) return; // Update number of leased spaces remaining for the opportunity - var leasedSpaces = db.LoadSelect(x => x.OrderTable.OrderMode != OrderMode.Booking && x.OrderTable.ProposalStatus != ProposalStatus.CustomerRejected && x.OrderTable.ProposalStatus != ProposalStatus.SellerRejected && x.OccurrenceId == occurrence.Id).Count(); + 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; occurrence.LeasedSpaces = leasedSpaces; // Update number of actual spaces remaining for the opportunity - var totalSpacesTaken = db.LoadSelect(x => x.OrderTable.OrderMode == OrderMode.Booking && x.OccurrenceId == occurrence.Id && (x.Status == BookingStatus.Confirmed || x.Status == BookingStatus.Attended || x.Status == BookingStatus.Absent)).Count(); + 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(); 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! occurrence.Modified = DateTimeOffset.Now.UtcTicks; - db.Update(occurrence); + await db.UpdateAsync(occurrence); } - public static void RecalculateSpaces(IDbConnection db, IEnumerable occurrenceIds) + public static async Task RecalculateSpaces(IDbConnection db, IEnumerable occurrenceIds) { foreach (var occurrenceId in occurrenceIds) { - var thisOccurrence = db.Single(x => x.Id == occurrenceId && !x.Deleted); - RecalculateSpaces(db, thisOccurrence); + var thisOccurrence = await db.SingleAsync(x => x.Id == occurrenceId && !x.Deleted); + await RecalculateSpaces(db, thisOccurrence); } } - public static FakeDatabase GetPrepopulatedFakeDatabase() + public static async Task GetPrepopulatedFakeDatabase() { var database = new FakeDatabase(); - using (var db = database.Mem.Database.Open()) + using (var db = await database.Mem.Database.OpenAsync()) using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { - CreateSellers(db); - CreateSellerUsers(db); - CreateFakeClasses(db); - CreateFakeFacilitiesAndSlots(db); - CreateBookingPartners(db); + await CreateSellers(db); + await CreateSellerUsers(db); + await CreateFakeClasses(db); + await CreateFakeFacilitiesAndSlots(db); + await CreateBookingPartners(db); transaction.Commit(); } + return database; } - private static void CreateFakeFacilitiesAndSlots(IDbConnection db) + private static async Task CreateFakeFacilitiesAndSlots(IDbConnection db) { var opportunitySeeds = GenerateOpportunitySeedDistribution(OpportunityCount); @@ -1415,9 +1383,9 @@ private static void CreateFakeFacilitiesAndSlots(IDbConnection db) Name = $"{Faker.Commerce.ProductMaterial()} {Faker.PickRandomParam("Sports Hall", "Swimming Pool Hall", "Running Hall", "Jumping Hall")}", SellerId = Faker.Random.Bool(0.8f) ? Faker.Random.Long(1, 2) : Faker.Random.Long(3, 5), // distribution: 80% 1-2, 20% 3-5 }) - .ToList(); + .AsList(); - int slotId = 0; + var slotId = 0; var slots = opportunitySeeds.Select(seed => Enumerable.Range(0, 10) .Select(_ => new @@ -1426,7 +1394,7 @@ private static void CreateFakeFacilitiesAndSlots(IDbConnection db) TotalUses = Faker.Random.Int(0, 8), Price = decimal.Parse(Faker.Random.Bool() ? "0.00" : Faker.Commerce.Price(0, 20)), }) - .Select((slot) => + .Select(slot => { var requiresAdditionalDetails = Faker.Random.Bool(ProportionWithRequiresAdditionalDetails); return new SlotTable @@ -1446,7 +1414,7 @@ private static void CreateFakeFacilitiesAndSlots(IDbConnection db) RequiresAdditionalDetails = requiresAdditionalDetails, RequiredAdditionalDetails = requiresAdditionalDetails ? PickRandomAdditionalDetails() : null, RequiresApproval = seed.RequiresApproval, - AllowsProposalAmendment = seed.RequiresApproval ? Faker.Random.Bool() : false, + AllowsProposalAmendment = seed.RequiresApproval && Faker.Random.Bool(), ValidFromBeforeStartDate = seed.RandomValidFromBeforeStartDate(), LatestCancellationBeforeStartDate = RandomLatestCancellationBeforeStartDate(), AllowCustomerCancellationFullRefund = Faker.Random.Bool() @@ -1454,11 +1422,11 @@ private static void CreateFakeFacilitiesAndSlots(IDbConnection db) } )).SelectMany(os => os); - db.InsertAll(facilities); - db.InsertAll(slots); + await db.InsertAllAsync(facilities); + await db.InsertAllAsync(slots); } - public static void CreateFakeClasses(IDbConnection db) + public static async Task CreateFakeClasses(IDbConnection db) { var opportunitySeeds = GenerateOpportunitySeedDistribution(OpportunityCount); @@ -1470,7 +1438,7 @@ public static void CreateFakeClasses(IDbConnection db) ValidFromBeforeStartDate = seed.RandomValidFromBeforeStartDate(), seed.RequiresApproval }) - .Select((@class) => + .Select(@class => { var requiresAdditionalDetails = Faker.Random.Bool(ProportionWithRequiresAdditionalDetails); return new ClassTable @@ -1486,7 +1454,7 @@ public static void CreateFakeClasses(IDbConnection db) RequiresAdditionalDetails = requiresAdditionalDetails, RequiredAdditionalDetails = requiresAdditionalDetails ? PickRandomAdditionalDetails() : null, RequiresApproval = @class.RequiresApproval, - AllowsProposalAmendment = @class.RequiresApproval ? Faker.Random.Bool() : false, + AllowsProposalAmendment = @class.RequiresApproval && Faker.Random.Bool(), LatestCancellationBeforeStartDate = RandomLatestCancellationBeforeStartDate(), SellerId = Faker.Random.Bool(0.8f) ? Faker.Random.Long(1, 2) : Faker.Random.Long(3, 5), // distribution: 80% 1-2, 20% 3-5 ValidFromBeforeStartDate = @class.ValidFromBeforeStartDate, @@ -1494,9 +1462,9 @@ public static void CreateFakeClasses(IDbConnection db) AllowCustomerCancellationFullRefund = Faker.Random.Bool() }; }) - .ToList(); + .AsList(); - int occurrenceId = 0; + var occurrenceId = 0; var occurrences = opportunitySeeds.Select(seed => Enumerable.Range(0, 10) .Select(_ => new @@ -1515,11 +1483,11 @@ public static void CreateFakeClasses(IDbConnection db) RemainingSpaces = occurrence.TotalSpaces })).SelectMany(os => os); - db.InsertAll(classes); - db.InsertAll(occurrences); + await db.InsertAllAsync(classes); + await db.InsertAllAsync(occurrences); } - public static void CreateSellers(IDbConnection db) + public static async Task CreateSellers(IDbConnection db) { var sellers = new List { @@ -1530,10 +1498,10 @@ public static void CreateSellers(IDbConnection db) new SellerTable { Id = 5, Name = "Jane Smith", IsIndividual = true, LogoUrl = "https://placekitten.com/640/360", Url = "https://www.example.com", IsTaxGross = true } }; - db.InsertAll(sellers); + await db.InsertAllAsync(sellers); } - public static void CreateSellerUsers(IDbConnection db) + public static async Task CreateSellerUsers(IDbConnection db) { var sellerUsers = new List { @@ -1545,31 +1513,31 @@ public static void CreateSellerUsers(IDbConnection db) new SellerUserTable { Id = 105, Username = "test5", PasswordHash = "test5".Sha256(), SellerId = 5 }, }; - db.InsertAll(sellerUsers); + await db.InsertAllAsync(sellerUsers); } - public bool ValidateSellerUserCredentials(string Username, string Password) + public async Task ValidateSellerUserCredentials(string username, string password) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var matchingUser = db.Single(x => x.Username == Username && x.PasswordHash == Password.Sha256()); - return (matchingUser != null); + var matchingUser = await db.SingleAsync(x => x.Username == username && x.PasswordHash == password.Sha256()); + return matchingUser != null; } } - public SellerUserTable GetSellerUser(string Username) + public async Task GetSellerUser(string username) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - return db.Single(x => x.Username == Username); + return await db.SingleAsync(x => x.Username == username); } } - public SellerUserTable GetSellerUserById(long sellerUserId) + public async Task GetSellerUserById(long sellerUserId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - return db.LoadSingleById(sellerUserId); + return await db.LoadSingleByIdAsync(sellerUserId); } } @@ -1596,8 +1564,7 @@ public List GetBookingPartnerAdministrators() } */ - - public static void CreateBookingPartners(IDbConnection db) + public static async Task CreateBookingPartners(IDbConnection db) { var bookingPartners = new List { @@ -1608,7 +1575,7 @@ public static void CreateBookingPartners(IDbConnection db) GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, ClientUri = "http://example.com", LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", - RedirectUris = new string[] { "http://localhost:3000/cb" } + RedirectUris = new[] { "http://localhost:3000/cb" } } }, new BookingPartnerTable { Name = "Example app", ClientId = "clientid_801", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98768", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, @@ -1617,183 +1584,182 @@ public static void CreateBookingPartners(IDbConnection db) GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, ClientUri = "http://example.com", LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", - RedirectUris = new string[] { "http://localhost:3000/cb" } + RedirectUris = new[] { "http://localhost:3000/cb" } } }, new BookingPartnerTable { Name = "Test Suite 2", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-primary-745ddf2d13019ce8b69c", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, new BookingPartnerTable { Name = "Test Suite 3", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-secondary-a21518cb57af7b6052df", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now } }; - var grants = new List() - { - new GrantTable() - { - Key = "8vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", - Type = "user_consent", - SubjectId = "TestSubjectId", - ClientId = "clientid_123", - CreationTime = DateTime.Now, - Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_123\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" - }, - new GrantTable() - { - Key = "7vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", - Type = "user_consent", - SubjectId = "TestSubjectId", - ClientId = "clientid_456", - CreationTime = DateTime.Now, - Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_456\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" - }, - new GrantTable() - { - Key = "9vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", - Type = "user_consent", - SubjectId = "TestSubjectId", - ClientId = "clientid_789", - CreationTime = DateTime.Now, - Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_789\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" - }, - }; - - db.InsertAll(bookingPartners); - //db.InsertAll(grants); - } - - public List GetBookingPartners() - { - using (var db = Mem.Database.Open()) - { - return db.Select().ToList(); + // var grants = new List() + // { + // new GrantTable + // { + // Key = "8vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", + // Type = "user_consent", + // SubjectId = "TestSubjectId", + // ClientId = "clientid_123", + // CreationTime = DateTime.Now, + // Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_123\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" + // }, + // new GrantTable + // { + // Key = "7vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", + // Type = "user_consent", + // SubjectId = "TestSubjectId", + // ClientId = "clientid_456", + // CreationTime = DateTime.Now, + // Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_456\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" + // }, + // new GrantTable + // { + // Key = "9vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", + // Type = "user_consent", + // SubjectId = "TestSubjectId", + // ClientId = "clientid_789", + // CreationTime = DateTime.Now, + // Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_789\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" + // }, + // }; + + await db.InsertAllAsync(bookingPartners); + //await db.InsertAllAsync(grants); + } + + public async Task> GetBookingPartners() + { + using (var db = await Mem.Database.OpenAsync()) + { + return (await db.SelectAsync()).AsList(); } } - public BookingPartnerTable GetBookingPartnerByInitialAccessToken(string registrationKey) + public async Task GetBookingPartnerByInitialAccessToken(string registrationKey) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var bookingPartner = db.Single(x => x.InitialAccessToken == registrationKey); + var bookingPartner = await db.SingleAsync(x => x.InitialAccessToken == registrationKey); return bookingPartner?.InitialAccessTokenKeyValidUntil > DateTime.Now ? bookingPartner : null; } } - public BookingPartnerTable GetBookingPartner(string clientId) + public async Task GetBookingPartner(string clientId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - return db.Single(x => x.ClientId == clientId); + return await db.SingleAsync(x => x.ClientId == clientId); } } - public void SaveBookingPartner(BookingPartnerTable bookingPartnerTable) + public async Task SaveBookingPartner(BookingPartnerTable bookingPartnerTable) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - db.Save(bookingPartnerTable); + await db.SaveAsync(bookingPartnerTable); } } - public void ResetBookingPartnerKey(string clientId, string key) + public async Task ResetBookingPartnerKey(string clientId, string key) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var bookingPartner = db.Single(x => x.ClientId == clientId); + var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); bookingPartner.Registered = false; bookingPartner.InitialAccessToken = key; bookingPartner.InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(2); bookingPartner.ClientSecret = null; - db.Save(bookingPartner); + await db.SaveAsync(bookingPartner); } } - public void SetBookingPartnerKey(string clientId, string key) + public async Task SetBookingPartnerKey(string clientId, string key) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var bookingPartner = db.Single(x => x.ClientId == clientId); + var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); bookingPartner.InitialAccessToken = key; bookingPartner.InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(2); - db.Save(bookingPartner); + await db.SaveAsync(bookingPartner); } } - public void UpdateBookingPartnerScope(string clientId, string scope, bool bookingsSuspended) + public async Task UpdateBookingPartnerScope(string clientId, string scope, bool bookingsSuspended) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var bookingPartner = db.Single(x => x.ClientId == clientId); + var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); bookingPartner.ClientProperties.Scope = scope; bookingPartner.BookingsSuspended = true; - db.Save(bookingPartner); + await db.SaveAsync(bookingPartner); } } - - public void AddBookingPartner(BookingPartnerTable newBookingPartner) + public async Task AddBookingPartner(BookingPartnerTable newBookingPartner) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - db.Save(newBookingPartner); + await db.SaveAsync(newBookingPartner); } } - public GrantTable GetGrant(string key) + public async Task GetGrant(string key) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - return db.Single(x => x.Key == key); + return await db.SingleAsync(x => x.Key == key); } } - public IEnumerable GetAllGrants(string subjectId) + public async Task> GetAllGrants(string subjectId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - return db.Select(x => x.SubjectId == subjectId).ToList(); + return (await db.SelectAsync(x => x.SubjectId == subjectId)).AsList(); } } - public void AddGrant(string key, string type, string subjectId, string clientId, DateTime CreationTime, DateTime? Expiration, string data) + public async Task AddGrant(string key, string type, string subjectId, string clientId, DateTime creationTime, DateTime? expiration, string data) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - var grant = new GrantTable() + var grant = new GrantTable { Key = key, Type = type, SubjectId = subjectId, ClientId = clientId, - CreationTime = CreationTime, - Expiration = Expiration, + CreationTime = creationTime, + Expiration = expiration, Data = data }; - db.Save(grant); + await db.SaveAsync(grant); } } - public void RemoveGrant(string key) + public async Task RemoveGrant(string key) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - db.Delete(x => x.Key == key); + await db.DeleteAsync(x => x.Key == key); } } - public void RemoveGrant(string subjectId, string clientId) + public async Task RemoveGrant(string subjectId, string clientId) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - db.Delete(x => x.SubjectId == subjectId && x.ClientId == clientId); + await db.DeleteAsync(x => x.SubjectId == subjectId && x.ClientId == clientId); } } - public void RemoveGrant(string subjectId, string clientId, string type) + public async Task RemoveGrant(string subjectId, string clientId, string type) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - db.Delete(x => x.SubjectId == subjectId && x.ClientId == clientId && x.Type == type); + await db.DeleteAsync(x => x.SubjectId == subjectId && x.ClientId == clientId && x.Type == type); } } - public (int, int) AddClass( + public async Task<(int, int)> AddClass( string testDatasetIdentifier, long? sellerId, string title, @@ -1816,7 +1782,7 @@ public void RemoveGrant(string subjectId, string clientId, string type) var startTime = DateTime.Now.AddDays(inPast ? -1 : 1); var endTime = DateTime.Now.AddDays(inPast ? -1 : 1).AddHours(1); - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { var @class = new ClassTable @@ -1844,7 +1810,7 @@ public void RemoveGrant(string subjectId, string clientId, string type) AllowCustomerCancellationFullRefund = allowCustomerCancellationFullRefund, Modified = DateTimeOffset.Now.UtcTicks }; - db.Save(@class); + await db.SaveAsync(@class); var occurrence = new OccurrenceTable { @@ -1857,7 +1823,7 @@ public void RemoveGrant(string subjectId, string clientId, string type) RemainingSpaces = totalSpaces, Modified = DateTimeOffset.Now.UtcTicks }; - db.Save(occurrence); + await db.SaveAsync(occurrence); transaction.Commit(); @@ -1865,7 +1831,7 @@ public void RemoveGrant(string subjectId, string clientId, string type) } } - public (int, int) AddFacility( + public async Task<(int, int)> AddFacility( string testDatasetIdentifier, long? sellerId, string title, @@ -1886,7 +1852,7 @@ public void RemoveGrant(string subjectId, string clientId, string type) var startTime = DateTime.Now.AddDays(inPast ? -1 : 1); var endTime = DateTime.Now.AddDays(inPast ? -1 : 1).AddHours(1); - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) using (var transaction = db.OpenTransaction(IsolationLevel.Serializable)) { var facility = new FacilityUseTable @@ -1899,7 +1865,7 @@ public void RemoveGrant(string subjectId, string clientId, string type) LocationLng = locationLng, Modified = DateTimeOffset.Now.UtcTicks }; - db.Save(facility); + await db.SaveAsync(facility); var slot = new SlotTable { @@ -1926,7 +1892,7 @@ public void RemoveGrant(string subjectId, string clientId, string type) AllowCustomerCancellationFullRefund = allowCustomerCancellationFullRefund, Modified = DateTimeOffset.Now.UtcTicks }; - db.Save(slot); + await db.SaveAsync(slot); transaction.Commit(); @@ -1934,26 +1900,26 @@ public void RemoveGrant(string subjectId, string clientId, string type) } } - public void DeleteTestClassesFromDataset(string testDatasetIdentifier) + public async Task DeleteTestClassesFromDataset(string testDatasetIdentifier) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - db.UpdateOnly(() => new ClassTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, + await db.UpdateOnlyAsync(() => new ClassTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, where: x => x.TestDatasetIdentifier == testDatasetIdentifier && !x.Deleted); - db.UpdateOnly(() => new OccurrenceTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, + await db.UpdateOnlyAsync(() => new OccurrenceTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, where: x => x.TestDatasetIdentifier == testDatasetIdentifier && !x.Deleted); } } - public void DeleteTestFacilitiesFromDataset(string testDatasetIdentifier) + public async Task DeleteTestFacilitiesFromDataset(string testDatasetIdentifier) { - using (var db = Mem.Database.Open()) + using (var db = await Mem.Database.OpenAsync()) { - db.UpdateOnly(() => new FacilityUseTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, + await db.UpdateOnlyAsync(() => new FacilityUseTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, where: x => x.TestDatasetIdentifier == testDatasetIdentifier && !x.Deleted); - db.UpdateOnly(() => new SlotTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, + await db.UpdateOnlyAsync(() => new SlotTable { Modified = DateTimeOffset.Now.UtcTicks, Deleted = true }, where: x => x.TestDatasetIdentifier == testDatasetIdentifier && !x.Deleted); } } @@ -1988,7 +1954,7 @@ private static OpportunitySeed GenerateRandomOpportunityData(Faker faker, int in { return new OpportunitySeed { - Faker = faker, + SeedFaker = faker, Id = index + 1, StartDateBounds = BoundsDaysToMinutes(input.startDateRange).Value, ValidFromBeforeStartDateBounds = !input.validFromBeforeStartDateRange.HasValue ? (Bounds?)null : BoundsDaysToMinutes(input.validFromBeforeStartDateRange).Value, @@ -1998,8 +1964,8 @@ private static OpportunitySeed GenerateRandomOpportunityData(Faker faker, int in private static Bounds? BoundsDaysToMinutes(Bounds? bounds) { - const int MINUTES_IN_DAY = 60 * 24; - return !bounds.HasValue ? (Bounds?)null : new Bounds(bounds.Value.Lower * MINUTES_IN_DAY, bounds.Value.Upper * MINUTES_IN_DAY); + const int minutesInDay = 60 * 24; + return !bounds.HasValue ? (Bounds?)null : new Bounds(bounds.Value.Lower * minutesInDay, bounds.Value.Upper * minutesInDay); } /// @@ -2007,20 +1973,20 @@ private static OpportunitySeed GenerateRandomOpportunityData(Faker faker, int in /// private static List GenerateOpportunitySeedDistribution(int count) { - return Faker.GenerateIntegerDistribution(count, BucketDefinitions, GenerateRandomOpportunityData).ToList(); + return Faker.GenerateIntegerDistribution(count, BucketDefinitions, GenerateRandomOpportunityData).AsList(); } private struct OpportunitySeed { - public Faker Faker { get; set; } + public Faker SeedFaker { get; set; } public int Id { get; set; } public Bounds StartDateBounds { get; set; } public Bounds? ValidFromBeforeStartDateBounds { get; set; } - public DateTime RandomStartDate() => DateTime.Now.AddMinutes(this.Faker.Random.Int(StartDateBounds)); + public DateTime RandomStartDate() => DateTime.Now.AddMinutes(SeedFaker.Random.Int(StartDateBounds)); public TimeSpan? RandomValidFromBeforeStartDate() => ValidFromBeforeStartDateBounds.HasValue - ? TimeSpan.FromMinutes(this.Faker.Random.Int(ValidFromBeforeStartDateBounds.Value)) + ? TimeSpan.FromMinutes(SeedFaker.Random.Int(ValidFromBeforeStartDateBounds.Value)) : (TimeSpan?)null; public bool RequiresApproval { get; set; } } @@ -2035,7 +2001,7 @@ private struct OpportunitySeed private static List PickRandomAdditionalDetails() { - return new HashSet { Faker.PickRandom(), Faker.PickRandom() }.ToList(); + return new HashSet { Faker.PickRandom(), Faker.PickRandom() }.AsList(); } } } diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs index fa4b6c02..fff6c808 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeDatabaseTransaction.cs @@ -1,10 +1,6 @@ using ServiceStack.OrmLite; using System; -using System.Collections.Generic; using System.Data; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace OpenActive.FakeDatabase.NET { diff --git a/OpenActive.Server.NET.Tests/IdTemplate.cs b/OpenActive.Server.NET.Tests/IdTemplate.cs index 3f40bd39..26f615c8 100644 --- a/OpenActive.Server.NET.Tests/IdTemplate.cs +++ b/OpenActive.Server.NET.Tests/IdTemplate.cs @@ -1,6 +1,5 @@ using System; using Xunit; -using OpenActive.Server.NET; using OpenActive.DatasetSite.NET; using OpenActive.Server.NET.OpenBookingHelper; diff --git a/OpenActive.Server.NET.Tests/SellerIdComponentsTest.cs b/OpenActive.Server.NET.Tests/SellerIdComponentsTest.cs index 2fa4c7e1..717d4b04 100644 --- a/OpenActive.Server.NET.Tests/SellerIdComponentsTest.cs +++ b/OpenActive.Server.NET.Tests/SellerIdComponentsTest.cs @@ -1,7 +1,4 @@ -using System; -using Xunit; -using OpenActive.Server.NET; -using OpenActive.DatasetSite.NET; +using Xunit; using OpenActive.Server.NET.OpenBookingHelper; namespace OpenActive.Server.NET.Tests diff --git a/OpenActive.Server.NET.Tests/StoreBookingEngineTest.cs b/OpenActive.Server.NET.Tests/StoreBookingEngineTest.cs index f7d6edbf..d105fcad 100644 --- a/OpenActive.Server.NET.Tests/StoreBookingEngineTest.cs +++ b/OpenActive.Server.NET.Tests/StoreBookingEngineTest.cs @@ -1,7 +1,4 @@ -using System; -using Xunit; -using OpenActive.Server.NET; -using OpenActive.NET; +using Xunit; namespace OpenActive.Server.NET.Tests { diff --git a/OpenActive.Server.NET/Entrypoint/IBookingEngine.cs b/OpenActive.Server.NET/Entrypoint/IBookingEngine.cs index 1f8a0eb0..a48efdb3 100644 --- a/OpenActive.Server.NET/Entrypoint/IBookingEngine.cs +++ b/OpenActive.Server.NET/Entrypoint/IBookingEngine.cs @@ -1,8 +1,5 @@ using System; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; -using OpenActive.NET; -using OpenActive.NET.Rpde.Version1; using OpenActive.Server.NET.OpenBookingHelper; namespace OpenActive.Server.NET diff --git a/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationExtensions.cs b/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationExtensions.cs index 1b06cfb1..3a523dd8 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationExtensions.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationExtensions.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Security.Claims; -using System.Text; using OpenActive.NET; namespace OpenActive.Server.NET.OpenBookingHelper diff --git a/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationTestHeaders.cs b/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationTestHeaders.cs index d0526321..dc672927 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationTestHeaders.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Authentication/AuthenticationTestHeaders.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenActive.Server.NET.OpenBookingHelper +namespace OpenActive.Server.NET.OpenBookingHelper { public static class AuthenticationTestHeaders { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveCustomClaimNames.cs b/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveCustomClaimNames.cs index d4985cfd..f148a8c0 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveCustomClaimNames.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveCustomClaimNames.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenActive.Server.NET.OpenBookingHelper +namespace OpenActive.Server.NET.OpenBookingHelper { public static class OpenActiveCustomClaimNames { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveScopes.cs b/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveScopes.cs index e839f7e7..eff392cb 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveScopes.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Authentication/OpenActiveScopes.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenActive.Server.NET.OpenBookingHelper +namespace OpenActive.Server.NET.OpenBookingHelper { public static class OpenActiveScopes { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Beta/AuthenticatedPerson.cs b/OpenActive.Server.NET/OpenBookingHelper/Beta/AuthenticatedPerson.cs index 59cd1de3..a2e6e66a 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Beta/AuthenticatedPerson.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Beta/AuthenticatedPerson.cs @@ -1,7 +1,4 @@ using OpenActive.NET; -using System; -using System.Collections.Generic; -using System.Text; namespace OpenActive.Server.NET.OpenBookingHelper.Beta { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Content/ResponseContent.cs b/OpenActive.Server.NET/OpenBookingHelper/Content/ResponseContent.cs index f132d3ba..7a372724 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Content/ResponseContent.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Content/ResponseContent.cs @@ -1,9 +1,5 @@ using OpenActive.NET; -using System; -using System.Collections.Generic; using System.Net; -using System.Net.Http; -using System.Text; namespace OpenActive.Server.NET.OpenBookingHelper { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Context/BookingFlowContext.cs b/OpenActive.Server.NET/OpenBookingHelper/Context/BookingFlowContext.cs index 919c810d..b9ebdbe7 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Context/BookingFlowContext.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Context/BookingFlowContext.cs @@ -1,8 +1,4 @@ using OpenActive.NET; -using OpenActive.Server.NET.OpenBookingHelper; -using System; -using System.Collections.Generic; -using System.Text; namespace OpenActive.Server.NET.OpenBookingHelper { diff --git a/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/ClientRegistrationModel.cs b/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/ClientRegistrationModel.cs index 46e8472c..6ee41b6f 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/ClientRegistrationModel.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/ClientRegistrationModel.cs @@ -1,8 +1,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using System; using System.Collections.Generic; -using System.Text; namespace OpenActive.Server.NET.OpenBookingHelper { diff --git a/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/OidcConstants.cs b/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/OidcConstants.cs index 571ca8ae..34223d1d 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/OidcConstants.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/DynamicClientRegistration/OidcConstants.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenActive.Server.NET.OpenBookingHelper +namespace OpenActive.Server.NET.OpenBookingHelper { public static class OidcConstants { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Enums/FlowStage.cs b/OpenActive.Server.NET/OpenBookingHelper/Enums/FlowStage.cs index 6ea4bced..f68232ff 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Enums/FlowStage.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Enums/FlowStage.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenActive.Server.NET.OpenBookingHelper +namespace OpenActive.Server.NET.OpenBookingHelper { public enum FlowStage { C1, C2, P, B, OrderStatus } } diff --git a/OpenActive.Server.NET/OpenBookingHelper/Enums/TaxPayeeRelationship.cs b/OpenActive.Server.NET/OpenBookingHelper/Enums/TaxPayeeRelationship.cs index 2a3639da..c70e43e2 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Enums/TaxPayeeRelationship.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Enums/TaxPayeeRelationship.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenActive.Server.NET.OpenBookingHelper +namespace OpenActive.Server.NET.OpenBookingHelper { public enum TaxPayeeRelationship { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Exceptions/OpenBookingException.cs b/OpenActive.Server.NET/OpenBookingHelper/Exceptions/OpenBookingException.cs index f66726d3..2aeb0d1a 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Exceptions/OpenBookingException.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Exceptions/OpenBookingException.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Runtime.Serialization; using OpenActive.NET; using System.Net; diff --git a/OpenActive.Server.NET/OpenBookingHelper/IdTransforms/DefaultComponents.cs b/OpenActive.Server.NET/OpenBookingHelper/IdTransforms/DefaultComponents.cs index 28988776..a13a0f21 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/IdTransforms/DefaultComponents.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/IdTransforms/DefaultComponents.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; -using OpenActive.NET; namespace OpenActive.Server.NET.OpenBookingHelper { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Model/ModelSupport.cs b/OpenActive.Server.NET/OpenBookingHelper/Model/ModelSupport.cs index 4a5ea4c1..149af1c2 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Model/ModelSupport.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Model/ModelSupport.cs @@ -1,8 +1,5 @@ using OpenActive.DatasetSite.NET; -using OpenActive.NET; using System; -using System.Collections.Generic; -using System.Text; namespace OpenActive.Server.NET.OpenBookingHelper { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Model/OrderModelSupport.cs b/OpenActive.Server.NET/OpenBookingHelper/Model/OrderModelSupport.cs index dd8208e3..cdf686d2 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Model/OrderModelSupport.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Model/OrderModelSupport.cs @@ -1,8 +1,4 @@ -using OpenActive.DatasetSite.NET; -using OpenActive.NET; -using System; -using System.Collections.Generic; -using System.Text; +using System; namespace OpenActive.Server.NET.OpenBookingHelper { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Rpde/OrdersRpdeBase.cs b/OpenActive.Server.NET/OpenBookingHelper/Rpde/OrdersRpdeBase.cs index 8b3657a4..165f76bf 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Rpde/OrdersRpdeBase.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Rpde/OrdersRpdeBase.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using OpenActive.DatasetSite.NET; -using OpenActive.NET; using OpenActive.NET.Rpde.Version1; // TODO: Consolidate this logic with RpdeBase.cs to remove duplication (using generics?) diff --git a/OpenActive.Server.NET/OpenBookingHelper/Rpde/RpdeOrderingStrategyRouter.cs b/OpenActive.Server.NET/OpenBookingHelper/Rpde/RpdeOrderingStrategyRouter.cs index af3adc88..d1882e17 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Rpde/RpdeOrderingStrategyRouter.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Rpde/RpdeOrderingStrategyRouter.cs @@ -45,7 +45,7 @@ public static class RpdeOrderingStrategyRouter /// /// /// - public async static Task GetRpdePage(this IRpdeFeedGenerator generator, string feedidentifier, long? afterTimestamp, string afterId, long? afterChangeNumber) + public static async Task GetRpdePage(this IRpdeFeedGenerator generator, string feedidentifier, long? afterTimestamp, string afterId, long? afterChangeNumber) { switch (generator) { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Settings/BookingEngineSettings.cs b/OpenActive.Server.NET/OpenBookingHelper/Settings/BookingEngineSettings.cs index 1206fb1f..551b2c54 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Settings/BookingEngineSettings.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Settings/BookingEngineSettings.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; using OpenActive.DatasetSite.NET; -using OpenActive.NET; -using OpenActive.NET.Rpde.Version1; namespace OpenActive.Server.NET.OpenBookingHelper { diff --git a/OpenActive.Server.NET/OpenBookingHelper/Stores/SellerStore.cs b/OpenActive.Server.NET/OpenBookingHelper/Stores/SellerStore.cs index 40e8f897..d0601b94 100644 --- a/OpenActive.Server.NET/OpenBookingHelper/Stores/SellerStore.cs +++ b/OpenActive.Server.NET/OpenBookingHelper/Stores/SellerStore.cs @@ -1,8 +1,5 @@ using OpenActive.NET; -using OpenActive.Server.NET.OpenBookingHelper; using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; diff --git a/OpenActive.Server.NET/StoreBookingEngine/Context/StoreBookingFlowContext.cs b/OpenActive.Server.NET/StoreBookingEngine/Context/StoreBookingFlowContext.cs index a577a213..47c8f257 100644 --- a/OpenActive.Server.NET/StoreBookingEngine/Context/StoreBookingFlowContext.cs +++ b/OpenActive.Server.NET/StoreBookingEngine/Context/StoreBookingFlowContext.cs @@ -1,9 +1,7 @@ using OpenActive.NET; -using OpenActive.Server.NET.CustomBooking; using OpenActive.Server.NET.OpenBookingHelper; using System; using System.Collections.Generic; -using System.Text; namespace OpenActive.Server.NET.StoreBooking { diff --git a/OpenActive.Server.NET/StoreBookingEngine/StoreBookingEngine.cs b/OpenActive.Server.NET/StoreBookingEngine/StoreBookingEngine.cs index 1e35a801..7fca642c 100644 --- a/OpenActive.Server.NET/StoreBookingEngine/StoreBookingEngine.cs +++ b/OpenActive.Server.NET/StoreBookingEngine/StoreBookingEngine.cs @@ -296,7 +296,7 @@ public StoreBookingEngine(BookingEngineSettings settings, DatasetSiteGeneratorSe private readonly Dictionary storeRouting; private readonly StoreBookingEngineSettings storeBookingEngineSettings; - protected async override Task InsertTestOpportunity(string testDatasetIdentifier, OpportunityType opportunityType, TestOpportunityCriteriaEnumeration criteria, TestOpenBookingFlowEnumeration openBookingFlow, SellerIdComponents seller) + protected override async Task InsertTestOpportunity(string testDatasetIdentifier, OpportunityType opportunityType, TestOpportunityCriteriaEnumeration criteria, TestOpenBookingFlowEnumeration openBookingFlow, SellerIdComponents seller) { if (!storeRouting.ContainsKey(opportunityType)) throw new InternalOpenBookingException(new InternalLibraryConfigurationError(), "Specified opportunity type is not configured as bookable in the StoreBookingEngine constructor."); @@ -304,7 +304,7 @@ protected async override Task InsertTestOpportunity(string testDatasetIde return await storeRouting[opportunityType].CreateOpportunityWithinTestDataset(testDatasetIdentifier, opportunityType, criteria, openBookingFlow, seller); } - protected async override Task DeleteTestDataset(string testDatasetIdentifier) + protected override async Task DeleteTestDataset(string testDatasetIdentifier) { foreach (var store in storeRouting.Values) { @@ -312,7 +312,7 @@ protected async override Task DeleteTestDataset(string testDatasetIdentifier) } } - protected async override Task TriggerTestAction(OpenBookingSimulateAction simulateAction, OrderIdTemplate orderIdTemplate) + protected override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, OrderIdTemplate orderIdTemplate) { switch (simulateAction.Object.Value) { @@ -355,7 +355,7 @@ protected async override Task TriggerTestAction(OpenBookingSimulateAction simula } - public async override Task ProcessCustomerCancellation(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate, List orderItemIds) + public override async Task ProcessCustomerCancellation(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate, List orderItemIds) { if (!await storeBookingEngineSettings.OrderStore.CustomerCancelOrderItems(orderId, sellerId, orderItemIds)) { @@ -363,7 +363,7 @@ public async override Task ProcessCustomerCancellation(OrderIdComponents orderId } } - public async override Task ProcessOrderProposalCustomerRejection(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate) + public override async Task ProcessOrderProposalCustomerRejection(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate) { if (!await storeBookingEngineSettings.OrderStore.CustomerRejectOrderProposal(orderId, sellerId)) { @@ -371,12 +371,12 @@ public async override Task ProcessOrderProposalCustomerRejection(OrderIdComponen } } - protected async override Task ProcessOrderDeletion(OrderIdComponents orderId, SellerIdComponents sellerId) + protected override async Task ProcessOrderDeletion(OrderIdComponents orderId, SellerIdComponents sellerId) { return await storeBookingEngineSettings.OrderStore.DeleteOrder(orderId, sellerId); } - protected async override Task ProcessOrderQuoteDeletion(OrderIdComponents orderId, SellerIdComponents sellerId) + protected override async Task ProcessOrderQuoteDeletion(OrderIdComponents orderId, SellerIdComponents sellerId) { await storeBookingEngineSettings.OrderStore.DeleteLease(orderId, sellerId); } @@ -433,7 +433,7 @@ private static void CheckOrderIntegrity(Order requestOrder, Order responseOrder) } } - protected async override Task ProcessGetOrderStatus(OrderIdComponents orderId, SellerIdComponents sellerIdComponents, ILegalEntity seller) + protected override async Task ProcessGetOrderStatus(OrderIdComponents orderId, SellerIdComponents sellerIdComponents, ILegalEntity seller) { // Get Order without OrderItems expanded var order = await storeBookingEngineSettings.OrderStore.GetOrderStatus(orderId, sellerIdComponents, seller); @@ -468,7 +468,7 @@ protected async override Task ProcessGetOrderStatus(OrderIdComponents ord return order; } - public async override Task ProcessOrderCreationFromOrderProposal(OrderIdComponents orderId, OrderIdTemplate orderIdTemplate, ILegalEntity seller, SellerIdComponents sellerId, Order order) + public override async Task ProcessOrderCreationFromOrderProposal(OrderIdComponents orderId, OrderIdTemplate orderIdTemplate, ILegalEntity seller, SellerIdComponents sellerId, Order order) { if (!await storeBookingEngineSettings.OrderStore.CreateOrderFromOrderProposal(orderId, sellerId, order.OrderProposalVersion, order)) { @@ -650,7 +650,7 @@ public void AugmentWithOpenBookingPrepaymentConflictErrors(List ProcessFlowRequest(BookingFlowContext request, TOrder order) + public override async Task ProcessFlowRequest(BookingFlowContext request, TOrder order) { var flowContext = AugmentContextFromOrder(request, order); diff --git a/OpenActive.Server.NET/StoreBookingEngine/Stores/OpportunityStore.cs b/OpenActive.Server.NET/StoreBookingEngine/Stores/OpportunityStore.cs index 2efec0c0..eb741b78 100644 --- a/OpenActive.Server.NET/StoreBookingEngine/Stores/OpportunityStore.cs +++ b/OpenActive.Server.NET/StoreBookingEngine/Stores/OpportunityStore.cs @@ -1,7 +1,6 @@ using OpenActive.DatasetSite.NET; using OpenActive.NET; using OpenActive.Server.NET.OpenBookingHelper; -using OpenActive.Server.NET.StoreBooking; using System; using System.Collections.Generic; using System.Linq; diff --git a/OpenActive.Server.NET/StoreBookingEngine/Stores/OrderStore.cs b/OpenActive.Server.NET/StoreBookingEngine/Stores/OrderStore.cs index 296481a9..220ff538 100644 --- a/OpenActive.Server.NET/StoreBookingEngine/Stores/OrderStore.cs +++ b/OpenActive.Server.NET/StoreBookingEngine/Stores/OrderStore.cs @@ -2,7 +2,6 @@ using OpenActive.Server.NET.OpenBookingHelper; using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace OpenActive.Server.NET.StoreBooking From 062d58a53c90cb4ef152e6a6cb527a74e3fb3496 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Tue, 29 Jun 2021 21:14:07 +0100 Subject: [PATCH 02/14] refactor: remove razor helpers from booking partners form and fix uris --- .../Grants/BookingPartnersController.cs | 47 +++++++------- .../BookingPartnerCreate.cshtml | 65 ++++++++++--------- .../BookingPartners/BookingPartnerEdit.cshtml | 8 ++- .../Views/BookingPartners/Index.cshtml | 16 +++-- .../Views/Home/Index.cshtml | 2 +- .../Views/Shared/_Layout.cshtml | 22 +++++-- 6 files changed, 89 insertions(+), 71 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs index 29bdc820..e5daf4df 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs @@ -20,6 +20,7 @@ namespace src /// [SecurityHeaders] [Authorize] + [Route("booking-partners")] public class BookingPartnersController : Controller { private readonly IIdentityServerInteractionService _interaction; @@ -45,7 +46,7 @@ public async Task Index() /// /// Show list of grants /// - [HttpGet] + [HttpGet("edit/{id}")] public async Task Edit(string id) { var content = await BuildBookingPartnerViewModel(id); @@ -58,7 +59,7 @@ public async Task Edit(string id) /// /// Show list of grants /// - [HttpGet] + [HttpGet("create")] public async Task Create() { return View("BookingPartnerCreate", await Task.FromResult(new BookingPartnerModel())); @@ -67,9 +68,9 @@ public async Task Create() /// /// Add a new booking partner /// - [HttpPost] + [HttpPost("create")] [ValidateAntiForgeryToken] - public async Task CreateBookingPartner(string email, string bookingPartnerName) + public async Task Create([FromForm] string email, [FromForm] string bookingPartnerName) { var newBookingPartner = new BookingPartnerTable { @@ -85,76 +86,76 @@ public async Task CreateBookingPartner(string email, string booki }; await FakeBookingSystem.Database.AddBookingPartner(newBookingPartner); - return View("BookingPartnerEdit", await BuildBookingPartnerViewModel(newBookingPartner.ClientId)); + return Redirect($"/booking-partners/edit/{newBookingPartner.ClientId}"); } /// /// Handle postback to revoke a client /// - [HttpPost] + [HttpPost("manage-keys")] [ValidateAntiForgeryToken] - public IActionResult ManageKeys(string clientId) + public IActionResult ManageKeys([FromForm] string clientId) { - return RedirectToAction("Index"); + return Redirect("/booking-partners"); } /// /// Handle postback to revoke a client /// - [HttpPost] + [HttpPost("restore")] [ValidateAntiForgeryToken] - public IActionResult Restore(string clientId) + public IActionResult Restore([FromForm] string clientId) { - return RedirectToAction("Index"); + return Redirect("/booking-partners"); } /// /// Handle postback to revoke a client /// - [HttpPost] + [HttpPost("revoke")] [ValidateAntiForgeryToken] - public async Task Revoke(string clientId) + public async Task Revoke([FromForm] string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); - return RedirectToAction("Index"); + return Redirect("/booking-partners"); } /// /// Handle postback to suspend a client /// - [HttpPost] + [HttpPost("suspend")] [ValidateAntiForgeryToken] - public async Task Suspend(string clientId) + public async Task Suspend([FromForm] string clientId) { var client = await _clients.FindClientByIdAsync(clientId); client.AllowedScopes.Remove("openactive-openbooking"); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); await FakeBookingSystem.Database.UpdateBookingPartnerScope(clientId, "openid profile openactive-ordersfeed", true); - return RedirectToAction("Index"); + return Redirect("/booking-partners"); } /// /// Handle postback to generate a registration key /// - [HttpPost] + [HttpPost("regenerate-key")] [ValidateAntiForgeryToken] - public async Task RegenerateKey(string clientId) + public async Task RegenerateKey([FromForm] string clientId) { var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); await FakeBookingSystem.Database.SetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); - return View("BookingPartnerEdit", await BuildBookingPartnerViewModel(clientId)); + return Redirect($"/booking-partners/edit/{clientId}"); } /// /// Handle postback to generate a registration key, and a new client secret /// - [HttpPost] + [HttpPost("regenerate-all-keys")] [ValidateAntiForgeryToken] - public async Task RegenerateAllKeys(string clientId) + public async Task RegenerateAllKeys([FromForm] string clientId) { var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); await FakeBookingSystem.Database.ResetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); @@ -163,7 +164,7 @@ public async Task RegenerateAllKeys(string clientId) //var client = await _clients.FindClientByIdAsync(clientId); //client.ClientSecrets = new List() { new Secret(clientSecret.Sha256()) }; - return View("BookingPartnerEdit", await BuildBookingPartnerViewModel(clientId)); + return Redirect($"/booking-partners/edit/{clientId}"); } private static async Task BuildBookingPartnerViewModel(string clientId) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml index e94a7db3..52cdcbef 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerCreate.cshtml @@ -1,41 +1,42 @@ @model BookingPartnerModel
-
diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml index 410a0b72..b7f00072 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml @@ -66,7 +66,7 @@
- Close + Close
\ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/Index.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml similarity index 76% rename from Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/Index.cshtml rename to Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml index 23324c44..27bac2eb 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/Index.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml @@ -3,14 +3,8 @@
- -
-
- Add a Booking Partner +

Seller Administration

+
Below is the list of your approved booking partners.
@@ -32,9 +26,6 @@
@bookingPartner.ClientName
-
- Manage -
@if (bookingPartner.BookingPartner.BookingsSuspended) { @@ -48,11 +39,7 @@
-
- @Html.AntiForgeryToken() - - API Key -
+ Manage @if (bookingPartner.BookingPartner.Registered && !bookingPartner.BookingPartner.BookingsSuspended) { @@ -74,10 +61,10 @@ @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) { -
+ @Html.AntiForgeryToken() - +
}
diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml new file mode 100644 index 00000000..022c5610 --- /dev/null +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml @@ -0,0 +1,67 @@ +@model BookingPartnerViewModel + +
+ + + + + @if (Model.BookingPartners.Any() == false) + { +
+
+
+ You have not given access to any applications +
+
+
+ } + else + { + foreach (var bookingPartner in Model.BookingPartners) + { +
+
+
@bookingPartner.ClientName
+
+ +
+ @if (bookingPartner.BookingPartner.BookingsSuspended) + { + Bookings suspended + } + + @if (!bookingPartner.BookingPartner.Registered) + { + Pending registration + } +
+ +
+
+ @Html.AntiForgeryToken() + + API Key +
+ + @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) + { +
+ @Html.AntiForgeryToken() + + +
+ } +
+
+ } + } +
\ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml index 5261a709..827059e4 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml @@ -16,7 +16,7 @@

- Click here to manage booking partners. + Click here to manage booking partners.

diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml index 86635b52..32fd70f1 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml @@ -41,8 +41,8 @@ From 35c5e9897cc9786178d5e99a6ab18162b8a8e3e9 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 10:58:02 +0100 Subject: [PATCH 04/14] feat: Layout improvements --- .../Views/BookingPartners/SellerAdmin.cshtml | 112 +++++++++--------- .../Views/BookingPartners/SysAdmin.cshtml | 99 +++++++--------- 2 files changed, 98 insertions(+), 113 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml index 27bac2eb..aa9c6ad4 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml @@ -2,73 +2,67 @@
- @if (Model.BookingPartners.Any() == false) - { -
-
-
- You have not given access to any applications -
+
+ @if (Model.BookingPartners.Any() == false) + { +
+ You have not given access to any applications
-
- } - else - { - foreach (var bookingPartner in Model.BookingPartners) + } + else { -
-
-
@bookingPartner.ClientName
-
-
- @if (bookingPartner.BookingPartner.BookingsSuspended) + + + + + + + + + + @foreach (var bookingPartner in Model.BookingPartners) { - Bookings suspended - } - - @if (!bookingPartner.BookingPartner.Registered) - { - Pending registration - } - - -
- Manage +
+ + + + } - - + +
Booking partnerBookings by broker / last 30 days
@bookingPartner.ClientName + Manage - @if (bookingPartner.BookingPartner.Registered && !bookingPartner.BookingPartner.BookingsSuspended) - { -
- @Html.AntiForgeryToken() - - -
- } + @if (bookingPartner.BookingPartner.Registered && !bookingPartner.BookingPartner.BookingsSuspended) + { +
+ @Html.AntiForgeryToken() + + +
+ } - @if (bookingPartner.BookingPartner.Registered && bookingPartner.BookingPartner.BookingsSuspended) - { -
- @Html.AntiForgeryToken() - - -
- } + @if (bookingPartner.BookingPartner.Registered && bookingPartner.BookingPartner.BookingsSuspended) + { +
+ @Html.AntiForgeryToken() + + +
+ } - @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) - { -
- @Html.AntiForgeryToken() - - -
+ @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) + { +
+ @Html.AntiForgeryToken() + + +
+ } +
} - } +
\ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml index 022c5610..85e4fea8 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml @@ -2,66 +2,57 @@
- - @if (Model.BookingPartners.Any() == false) - { -
-
-
- You have not given access to any applications -
+ @if (!Model.BookingPartners.Any()) + { +
+ You have not given access to any applications
-
- } - else - { - foreach (var bookingPartner in Model.BookingPartners) + } + else { -
-
-
@bookingPartner.ClientName
-
- -
- @if (bookingPartner.BookingPartner.BookingsSuspended) - { - Bookings suspended - } - - @if (!bookingPartner.BookingPartner.Registered) + + + + + + + + + + + @foreach (var bookingPartner in Model.BookingPartners) { - Pending registration - } - - -
-
- @Html.AntiForgeryToken() - - API Key - +
+ + + + + } - - + +
Booking partnerSellers enabledBookings by broker / last 30 days +  Add a Booking Partner +
@bookingPartner.ClientName +
+ @Html.AntiForgeryToken() + + API Key +
- @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) - { -
- @Html.AntiForgeryToken() - - -
+ @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) + { +
+ @Html.AntiForgeryToken() + + +
+ } +
} - } -
\ No newline at end of file +
+
From a2067b94c044e4c6aa97ae958919ffe0797b956b Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 15:34:39 +0100 Subject: [PATCH 05/14] fix: booking partners table structure --- .../Client/ClientRegistrationController.cs | 13 ++- .../Custom/Client/ClientStore.cs | 16 ++-- .../Grants/BookingPartnersController.cs | 31 +++++-- .../Custom/Grants/PersistedGrantStore.cs | 2 +- .../Views/BookingPartners/SellerAdmin.cshtml | 2 +- .../DatabaseTables.cs | 19 ++--- .../FakeBookingSystem.cs | 81 ++++++++----------- 7 files changed, 77 insertions(+), 87 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs index fe45432d..e8918e08 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs @@ -57,14 +57,11 @@ public async Task PostAsync([FromBody] ClientRegistrationModel mo bookingPartner.Registered = true; bookingPartner.ClientSecret = key.Sha256(); bookingPartner.Name = model.ClientName; - bookingPartner.ClientProperties = new ClientModel - { - ClientUri = model.ClientUri, - LogoUri = model.LogoUri, - GrantTypes = model.GrantTypes, - RedirectUris = model.RedirectUris, - Scope = model.Scope, - }; + bookingPartner.ClientUri = model.ClientUri; + bookingPartner.LogoUri = model.LogoUri; + bookingPartner.GrantTypes = model.GrantTypes; + bookingPartner.RedirectUris = model.RedirectUris; + bookingPartner.Scope = model.Scope; await FakeBookingSystem.Database.SaveBookingPartner(bookingPartner); diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs index b89d7b18..74c0888f 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs @@ -12,10 +12,10 @@ public class ClientStore : IClientStore public async Task FindClientByIdAsync(string clientId) { var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); - return ConvertToIS4Client(bookingPartner); + return ConvertToIs4Client(bookingPartner); } - private Client ConvertToIS4Client(BookingPartnerTable bookingPartner) + private static Client ConvertToIs4Client(BookingPartnerTable bookingPartner) { if (bookingPartner == null) return null; @@ -25,20 +25,20 @@ private Client ConvertToIS4Client(BookingPartnerTable bookingPartner) Enabled = bookingPartner.Registered, ClientId = bookingPartner.ClientId, ClientName = bookingPartner.Name, - AllowedGrantTypes = bookingPartner.ClientProperties?.GrantTypes == null ? new List() : bookingPartner.ClientProperties.GrantTypes.ToList(), - ClientSecrets = bookingPartner.ClientSecret == null ? new List() : new List() { new Secret(bookingPartner.ClientSecret) }, - AllowedScopes = bookingPartner.ClientProperties?.Scope == null ? new List() : bookingPartner.ClientProperties.Scope.Split(' ').ToList(), + AllowedGrantTypes = bookingPartner.GrantTypes == null ? new List() : bookingPartner.GrantTypes.ToList(), + ClientSecrets = bookingPartner.ClientSecret == null ? new List() : new List { new Secret(bookingPartner.ClientSecret) }, + AllowedScopes = bookingPartner.Scope == null ? new List() : bookingPartner.Scope.Split(' ').ToList(), Claims = bookingPartner.ClientId == null ? new List() : new List() { new System.Security.Claims.Claim("https://openactive.io/clientId", bookingPartner.ClientId) }, ClientClaimsPrefix = "", AlwaysSendClientClaims = true, AlwaysIncludeUserClaimsInIdToken = true, AllowOfflineAccess = true, UpdateAccessTokenClaimsOnRefresh = true, - RedirectUris = bookingPartner.ClientProperties?.RedirectUris == null ? new List() : bookingPartner.ClientProperties.RedirectUris.ToList(), + RedirectUris = bookingPartner.RedirectUris == null ? new List() : bookingPartner.RedirectUris.ToList(), RequireConsent = true, RequirePkce = true, - LogoUri = bookingPartner.ClientProperties?.LogoUri, - ClientUri = bookingPartner.ClientProperties?.ClientUri + LogoUri = bookingPartner.LogoUri, + ClientUri = bookingPartner.ClientUri }; } } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs index ff96a957..fc6bf9cb 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs @@ -37,7 +37,8 @@ public BookingPartnersController(IIdentityServerInteractionService interaction, [HttpGet("seller-admin")] public async Task SellerAdmin() { - return View("SellerAdmin", await BuildViewModel()); + var sellerUserId = User.GetSubjectId(); + return View("SellerAdmin", await BuildViewModel(long.Parse(sellerUserId))); } [HttpGet("sys-admin")] @@ -113,6 +114,20 @@ public async Task Remove([FromForm] string clientId) return Redirect("/booking-partners/seller-admin"); } + /// + /// Handle postback to delete a client + /// + [HttpPost("delete")] + [ValidateAntiForgeryToken] + public async Task Delete([FromForm] string clientId) + { + await _interaction.RevokeUserConsentAsync(clientId); + await FakeBookingSystem.Database.DeleteBookingPartner(clientId); + await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); + + return Redirect("/booking-partners/seller-admin"); + } + /// /// Handle postback to suspend a client /// @@ -169,21 +184,23 @@ private static async Task BuildBookingPartnerViewModel(stri { ClientId = bookingPartner.ClientId, ClientName = bookingPartner.Name, - ClientLogoUrl = bookingPartner.ClientProperties?.LogoUri, - ClientUrl = bookingPartner.ClientProperties?.ClientUri, + ClientLogoUrl = bookingPartner.LogoUri, + ClientUrl = bookingPartner.ClientUri, BookingPartner = bookingPartner }; } - private static async Task BuildViewModel() + private static async Task BuildViewModel(long? sellerUserId = null) { - var bookingPartners = await FakeBookingSystem.Database.GetBookingPartners(); + var bookingPartners = sellerUserId.HasValue + ? await FakeBookingSystem.Database.GetBookingPartners(sellerUserId.Value) + : await FakeBookingSystem.Database.GetBookingPartners(); var list = bookingPartners.Select(bookingPartner => new BookingPartnerModel { ClientId = bookingPartner.ClientId, ClientName = bookingPartner.Name, - ClientLogoUrl = bookingPartner.ClientProperties?.LogoUri, - ClientUrl = bookingPartner.ClientProperties?.ClientUri, + ClientLogoUrl = bookingPartner.LogoUri, + ClientUrl = bookingPartner.ClientUri, BookingPartner = bookingPartner }).ToList(); diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs index 4ef01c1a..0f50aca9 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/PersistedGrantStore.cs @@ -30,7 +30,7 @@ public async Task GetAsync(string key) { var grant = await FakeBookingSystem.Database.GetGrant(key); - return grant != null ? new PersistedGrant() + return grant != null ? new PersistedGrant { Key = grant.Key, Type = grant.Type, diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml index aa9c6ad4..19dffca5 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml @@ -30,7 +30,7 @@ @bookingPartner.ClientName - Manage + Manage @if (bookingPartner.BookingPartner.Registered && !bookingPartner.BookingPartner.BookingsSuspended) { diff --git a/Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs b/Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs index aca2109a..476a8d68 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs @@ -209,7 +209,11 @@ public class BookingPartnerTable public string ClientId { get; set; } public string Name { get; set; } public string ClientSecret { get; set; } - public ClientModel ClientProperties { get; set; } + public string ClientUri { get; set; } + public string LogoUri { get; set; } + public string[] GrantTypes { get; set; } + public string[] RedirectUris { get; set; } + public string Scope { get; set; } public bool Registered { get; set; } public DateTime CreatedDate { get; set; } public string InitialAccessToken { get; set; } @@ -229,19 +233,6 @@ public class GrantTable public string Data { get; set; } } - public class ClientModel - { - public string ClientUri { get; set; } - - public string LogoUri { get; set; } - - public string[] GrantTypes { get; set; } - - public string[] RedirectUris { get; set; } - - public string Scope { get; set; } - } - public static class DatabaseCreator { public static void CreateTables(OrmLiteConnectionFactory dbFactory) diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index efac6cd1..ad348147 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -507,6 +507,14 @@ public async Task AddCustomerNotice(Guid uuid) } } + public async Task DeleteBookingPartner(string clientId) + { + using (var db = await Mem.Database.OpenAsync()) + { + await db.DeleteAsync(x => x.ClientId == clientId); + } + } + public async Task DeleteLease(string clientId, Guid uuid, long? sellerId) { using (var db = await Mem.Database.OpenAsync()) @@ -1570,60 +1578,25 @@ public static async Task CreateBookingPartners(IDbConnection db) { new BookingPartnerTable { Name = "Test Suite 1", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "openactive_test_suite_client_12345xaq", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, new BookingPartnerTable { Name = "Acmefitness", ClientId = "clientid_800", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98767", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, - ClientProperties = new ClientModel { - Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", - GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, - ClientUri = "http://example.com", - LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", - RedirectUris = new[] { "http://localhost:3000/cb" } - } + Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", + GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, + ClientUri = "http://example.com", + LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", + RedirectUris = new[] { "http://localhost:3000/cb" } }, - new BookingPartnerTable { Name = "Example app", ClientId = "clientid_801", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98768", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, - ClientProperties = new ClientModel { - Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", - GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, - ClientUri = "http://example.com", - LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", - RedirectUris = new[] { "http://localhost:3000/cb" } - } + new BookingPartnerTable { Name = "Example app", ClientId = "clientid_709", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98768", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, + Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", + GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, + ClientUri = "http://example.com", + LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", + RedirectUris = new[] { "http://localhost:3000/cb" } }, new BookingPartnerTable { Name = "Test Suite 2", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-primary-745ddf2d13019ce8b69c", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, new BookingPartnerTable { Name = "Test Suite 3", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-secondary-a21518cb57af7b6052df", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now } }; - // var grants = new List() - // { - // new GrantTable - // { - // Key = "8vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", - // Type = "user_consent", - // SubjectId = "TestSubjectId", - // ClientId = "clientid_123", - // CreationTime = DateTime.Now, - // Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_123\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" - // }, - // new GrantTable - // { - // Key = "7vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", - // Type = "user_consent", - // SubjectId = "TestSubjectId", - // ClientId = "clientid_456", - // CreationTime = DateTime.Now, - // Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_456\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" - // }, - // new GrantTable - // { - // Key = "9vJ5rH7eSj7HL4TD5Tlaeyfa+U6WkFc/ofBdkVuM/RY=", - // Type = "user_consent", - // SubjectId = "TestSubjectId", - // ClientId = "clientid_789", - // CreationTime = DateTime.Now, - // Data = "{\"SubjectId\":\"818727\",\"ClientId\":\"clientid_789\",\"Scopes\":[\"openid\",\"profile\",\"openactive-identity\",\"openactive-openbooking\",\"oauth-dymamic-client-update\",\"offline_access\"],\"CreationTime\":\"2020-03-01T13:17:57Z\",\"Expiration\":null}" - // }, - // }; - await db.InsertAllAsync(bookingPartners); - //await db.InsertAllAsync(grants); + // To populate GrantTable locally, run the tests, e.g. `NODE_APP_INSTANCE=dev npm run start auth non-free` } public async Task> GetBookingPartners() @@ -1634,6 +1607,18 @@ public async Task> GetBookingPartners() } } + public async Task> GetBookingPartners(long sellerUserId) + { + using (var db = await Mem.Database.OpenAsync()) + { + var query = db.From() + .Join((bpt, gt) => bpt.ClientId == gt.ClientId) + .Join((gt, st) => gt.SubjectId == st.Id.ToString()) + .Where(st => st.Id == sellerUserId); + return (await db.SelectAsync(query)).AsList(); + } + } + public async Task GetBookingPartnerByInitialAccessToken(string registrationKey) { using (var db = await Mem.Database.OpenAsync()) @@ -1688,7 +1673,7 @@ public async Task UpdateBookingPartnerScope(string clientId, string scope, bool using (var db = await Mem.Database.OpenAsync()) { var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); - bookingPartner.ClientProperties.Scope = scope; + bookingPartner.Scope = scope; bookingPartner.BookingsSuspended = true; await db.SaveAsync(bookingPartner); } From 16f484c8b3ada93004c49a7433b9aea87f3a4935 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 18:38:00 +0100 Subject: [PATCH 06/14] feat: booking partners stats --- .../Client/ClientRegistrationController.cs | 4 +- .../Custom/Client/ClientStore.cs | 2 +- .../Custom/Grants/BookingPartnerViewModel.cs | 27 ++ .../Grants/BookingPartnersController.cs | 42 +-- .../Views/BookingPartners/SellerAdmin.cshtml | 7 +- .../Views/BookingPartners/SysAdmin.cshtml | 9 +- .../DatabaseTables.cs | 265 ------------------ .../FakeBookingSystem.cs | 144 +--------- .../Models/BookingPartnerTable.cs | 143 ++++++++++ .../Models/BookingStatistics.cs | 33 +++ .../Models/ClassTable.cs | 29 ++ .../Models/DatabaseTables.cs | 62 ++++ .../Models/FacilityUseTable.cs | 17 ++ .../Models/GrantTable.cs | 15 + .../Models/OccurrenceTable.cs | 19 ++ .../Models/OrderItemsTable.cs | 34 +++ .../Models/OrderTable.cs | 50 ++++ .../Models/SellerTable.cs | 15 + .../Models/SellerUserTable.cs | 17 ++ .../Models/SlotTable.cs | 30 ++ 20 files changed, 522 insertions(+), 442 deletions(-) delete mode 100644 Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/ClassTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/FacilityUseTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/GrantTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/OccurrenceTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/SellerTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs create mode 100644 Fakes/OpenActive.FakeDatabase.NET/Models/SlotTable.cs diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs index e8918e08..9f101165 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs @@ -50,7 +50,7 @@ public async Task PostAsync([FromBody] ClientRegistrationModel mo registrationKey = headerValues.FirstOrDefault().Substring("Bearer ".Length); // update the booking system - var bookingPartner = await FakeBookingSystem.Database.GetBookingPartnerByInitialAccessToken(registrationKey); + var bookingPartner = await BookingPartnerTable.GetByInitialAccessToken(registrationKey); if (bookingPartner == null) return Unauthorized("Initial Access Token is not valid, or is expired"); @@ -63,7 +63,7 @@ public async Task PostAsync([FromBody] ClientRegistrationModel mo bookingPartner.RedirectUris = model.RedirectUris; bookingPartner.Scope = model.Scope; - await FakeBookingSystem.Database.SaveBookingPartner(bookingPartner); + await BookingPartnerTable.Save(bookingPartner); // Read the updated client from the database and reflect back in the request var client = await _clients.FindClientByIdAsync(bookingPartner.ClientId); diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs index 74c0888f..fe527385 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientStore.cs @@ -11,7 +11,7 @@ public class ClientStore : IClientStore { public async Task FindClientByIdAsync(string clientId) { - var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); + var bookingPartner = await BookingPartnerTable.GetByClientId(clientId); return ConvertToIs4Client(bookingPartner); } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs index 9150fc24..62112c36 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs @@ -4,12 +4,37 @@ using OpenActive.FakeDatabase.NET; using System; using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace src { public class BookingPartnerViewModel { public IEnumerable BookingPartners { get; set; } + + public static async Task Build(long? sellerUserId = null) + { + var bookingPartners = sellerUserId.HasValue + ? await BookingPartnerTable.GetBySellerUserId(sellerUserId.Value) + : await BookingPartnerTable.Get(); + var list = (await Task.WhenAll(bookingPartners.Select(async bookingPartner => + { + var bookingStatistics = await BookingStatistics.Get(bookingPartner.ClientId); + return new BookingPartnerModel + { + ClientId = bookingPartner.ClientId, + ClientName = bookingPartner.Name, + ClientLogoUrl = bookingPartner.LogoUri, + ClientUrl = bookingPartner.ClientUri, + BookingPartner = bookingPartner, + SellersEnabled = bookingStatistics.SellersEnabled, + BookingsByBroker = bookingStatistics.BookingsByBroker + }; + }))).ToList(); + + return new BookingPartnerViewModel { BookingPartners = list }; + } } public class BookingPartnerModel @@ -23,5 +48,7 @@ public class BookingPartnerModel public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } public BookingPartnerTable BookingPartner { get; set; } + public IDictionary BookingsByBroker { get; set; } + public int SellersEnabled { get; set; } } } \ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs index fc6bf9cb..873c346d 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs @@ -10,7 +10,6 @@ using IdentityServer4.Extensions; using OpenActive.FakeDatabase.NET; using System; -using System.Linq; using IdentityServer; namespace src @@ -37,20 +36,20 @@ public BookingPartnersController(IIdentityServerInteractionService interaction, [HttpGet("seller-admin")] public async Task SellerAdmin() { - var sellerUserId = User.GetSubjectId(); - return View("SellerAdmin", await BuildViewModel(long.Parse(sellerUserId))); + var sellerUserId = long.Parse(User.GetSubjectId()); + return View("SellerAdmin", await BookingPartnerViewModel.Build(sellerUserId)); } [HttpGet("sys-admin")] public async Task SysAdmin() { - return View("SysAdmin", await BuildViewModel()); + return View("SysAdmin", await BookingPartnerViewModel.Build()); } [HttpGet("edit/{id}")] public async Task Edit(string id) { - var content = await BuildBookingPartnerViewModel(id); + var content = await BuildBookingPartnerModel(id); if (content == null) return NotFound(); @@ -83,7 +82,7 @@ public async Task Create([FromForm] string email, [FromForm] stri BookingsSuspended = false }; - await FakeBookingSystem.Database.AddBookingPartner(newBookingPartner); + await BookingPartnerTable.Add(newBookingPartner); return Redirect($"/booking-partners/edit/{newBookingPartner.ClientId}"); } @@ -139,7 +138,7 @@ public async Task Suspend([FromForm] string clientId) client.AllowedScopes.Remove("openactive-openbooking"); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); - await FakeBookingSystem.Database.UpdateBookingPartnerScope(clientId, "openid profile openactive-ordersfeed", true); + await BookingPartnerTable.UpdateScope(clientId, "openid profile openactive-ordersfeed", true); return Redirect("/booking-partners/seller-admin"); } @@ -150,8 +149,8 @@ public async Task Suspend([FromForm] string clientId) [ValidateAntiForgeryToken] public async Task RegenerateKey([FromForm] string clientId) { - var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); - await FakeBookingSystem.Database.SetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); + var bookingPartner = await BookingPartnerTable.GetByClientId(clientId); + await BookingPartnerTable.SetKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); return Redirect($"/booking-partners/edit/{clientId}"); } @@ -163,8 +162,8 @@ public async Task RegenerateKey([FromForm] string clientId) [ValidateAntiForgeryToken] public async Task RegenerateAllKeys([FromForm] string clientId) { - var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); - await FakeBookingSystem.Database.ResetBookingPartnerKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); + var bookingPartner = await BookingPartnerTable.GetByClientId(clientId); + await BookingPartnerTable.ResetKey(clientId, KeyGenerator.GenerateInitialAccessToken(bookingPartner.Name)); // TODO: Is this cached in memory, does it need updating?? //var client = await _clients.FindClientByIdAsync(clientId); @@ -173,10 +172,10 @@ public async Task RegenerateAllKeys([FromForm] string clientId) return Redirect($"/booking-partners/edit/{clientId}"); } - private static async Task BuildBookingPartnerViewModel(string clientId) + private static async Task BuildBookingPartnerModel(string clientId) { // var client = await _clients.FindClientByIdAsync(clientId); - var bookingPartner = await FakeBookingSystem.Database.GetBookingPartner(clientId); + var bookingPartner = await BookingPartnerTable.GetByClientId(clientId); if (bookingPartner == null) return null; @@ -189,22 +188,5 @@ private static async Task BuildBookingPartnerViewModel(stri BookingPartner = bookingPartner }; } - - private static async Task BuildViewModel(long? sellerUserId = null) - { - var bookingPartners = sellerUserId.HasValue - ? await FakeBookingSystem.Database.GetBookingPartners(sellerUserId.Value) - : await FakeBookingSystem.Database.GetBookingPartners(); - var list = bookingPartners.Select(bookingPartner => new BookingPartnerModel - { - ClientId = bookingPartner.ClientId, - ClientName = bookingPartner.Name, - ClientLogoUrl = bookingPartner.LogoUri, - ClientUrl = bookingPartner.ClientUri, - BookingPartner = bookingPartner - }).ToList(); - - return new BookingPartnerViewModel { BookingPartners = list }; - } } } \ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml index 19dffca5..97d40e57 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml @@ -28,7 +28,12 @@ { @bookingPartner.ClientName - + + @foreach (var (broker, orderCount) in bookingPartner.BookingsByBroker) + { +
@broker: @orderCount
+ } + Manage diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml index 85e4fea8..e836f3f3 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml @@ -31,8 +31,13 @@ { @bookingPartner.ClientName - - + @bookingPartner.SellersEnabled + + @foreach (var (broker, orderCount) in bookingPartner.BookingsByBroker) + { +
@broker: @orderCount
+ } +
@Html.AntiForgeryToken() diff --git a/Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs b/Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs deleted file mode 100644 index 476a8d68..00000000 --- a/Fakes/OpenActive.FakeDatabase.NET/DatabaseTables.cs +++ /dev/null @@ -1,265 +0,0 @@ -using System; -using System.Collections.Generic; -using ServiceStack.DataAnnotations; -using ServiceStack.OrmLite; - -namespace OpenActive.FakeDatabase.NET -{ - public enum BrokerRole { AgentBroker, ResellerBroker, NoBroker } - - public enum BookingStatus { None, CustomerCancelled, SellerCancelled, Confirmed, Attended, Absent } - - public enum ProposalStatus { AwaitingSellerConfirmation, SellerAccepted, SellerRejected, CustomerRejected } - - public enum FeedVisibility { None, Visible, Archived } - - public enum OrderMode { Lease, Proposal, Booking } - - public enum RequiredStatusType { Required, Optional, Unavailable } - - public enum AttendanceMode { Offline, Online, Mixed } - - public enum AdditionalDetailTypes { Age, PhotoConsent, Experience, Gender, FileUpload } - - [CompositeIndex(nameof(Modified), nameof(Id))] - public abstract class Table - { - [PrimaryKey] - [AutoIncrement] - public long Id { get; set; } - public bool Deleted { get; set; } - public long Modified { get; set; } = new DateTimeOffset(DateTime.Today).UtcTicks; - } - - public class ClassTable : Table - { - public string TestDatasetIdentifier { get; set; } - public string Title { get; set; } - [Reference] - public SellerTable SellerTable { get; set; } - [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] - public long SellerId { get; set; } - public decimal? Price { get; set; } - public RequiredStatusType? Prepayment { get; set; } - public bool RequiresAttendeeValidation { get; set; } - public bool RequiresAdditionalDetails { get; set; } - public List RequiredAdditionalDetails { get; set; } - public bool AllowCustomerCancellationFullRefund { get; set; } - public bool RequiresApproval { get; set; } - public TimeSpan? ValidFromBeforeStartDate { get; set; } - public TimeSpan? LatestCancellationBeforeStartDate { get; set; } - public decimal LocationLat { get; set; } - public decimal LocationLng { get; set; } - public AttendanceMode AttendanceMode { get; set; } - public bool AllowsProposalAmendment { get; set; } - } - - public class OccurrenceTable : Table - { - public string TestDatasetIdentifier { get; set; } - [Reference] - public ClassTable ClassTable { get; set; } - [ForeignKey(typeof(ClassTable), OnDelete = "CASCADE")] - public long ClassId { get; set; } - public DateTime Start { get; set; } - public DateTime End { get; set; } - public long TotalSpaces { get; set; } - public long LeasedSpaces { get; set; } - public long RemainingSpaces { get; set; } - } - - - public class OrderItemsTable : Table - { - public string ClientId { get; internal set; } - public Uri OpportunityJsonLdId { get; set; } - public Uri OfferJsonLdId { get; set; } - [Reference] - public OrderTable OrderTable { get; set; } - [ForeignKey(typeof(OrderTable), OnDelete = "CASCADE")] - public string OrderId { get; set; } - [Reference] - public OccurrenceTable OccurrenceTable { get; set; } - [ForeignKey(typeof(OccurrenceTable), OnDelete = "CASCADE")] - public long? OccurrenceId { get; set; } - [Reference] - public SlotTable SlotTable { get; set; } - [ForeignKey(typeof(SlotTable), OnDelete = "CASCADE")] - public long? SlotId { get; set; } - public BookingStatus Status { get; set; } - public string CancellationMessage { get; set; } - public decimal Price { get; set; } - public string PinCode { get; set; } - public string ImageUrl { get; set; } - public string BarCodeText { get; set; } - public string CustomerNotice { get; set; } - public Uri MeetingUrl { get; set; } - public string MeetingId { get; set; } - public string MeetingPassword { get; set; } - } - - [CompositeIndex(nameof(OrderModified), nameof(OrderId))] - [CompositeIndex(nameof(OrderProposalModified), nameof(OrderId))] - public class OrderTable - { - /** - * Note string type is used for the OrderId instead of Guid type to allow for correct ordering of GUIDs for the RPDE feed - */ - [PrimaryKey] - public string OrderId { get; set; } - - public bool Deleted { get; set; } - public long OrderModified { get; set; } = DateTimeOffset.Now.UtcTicks; - public long OrderProposalModified { get; set; } = DateTimeOffset.Now.UtcTicks; - public string ClientId { get; set; } - - [Reference] - public SellerTable SellerTable { get; set; } - [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] - public long SellerId { get; set; } - public bool? CustomerIsOrganization { get; set; } - public CustomerType CustomerType { get; set; } - public BrokerRole BrokerRole { get; set; } - public string BrokerName { get; set; } - public Uri BrokerUrl { get; set; } - public string BrokerTelephone { get; set; } - public string CustomerEmail { get; set; } - public string CustomerIdentifier { get; set; } - public string CustomerGivenName { get; set; } - public string CustomerFamilyName { get; set; } - public string CustomerTelephone { get; set; } - public string CustomerOrganizationName { get; set; } - public string PaymentIdentifier { get; set; } - public string PaymentName { get; set; } - public string PaymentProviderId { get; set; } - public string PaymentAccountId { get; set; } - public decimal TotalOrderPrice { get; set; } - public OrderMode OrderMode { get; set; } - public DateTime LeaseExpires { get; set; } - public FeedVisibility VisibleInOrdersFeed { get; set; } - public FeedVisibility VisibleInOrderProposalsFeed { get; set; } - public ProposalStatus? ProposalStatus { get; set; } - public Guid? ProposalVersionId { get; set; } - } - - public class SellerTable - { - [PrimaryKey] - public long Id { get; set; } - public string Name { get; set; } - public bool IsIndividual { get; set; } - public string Url { get; set; } - public bool IsTaxGross { get; set; } - public string LogoUrl { get; set; } - } - - public class SellerUserTable - { - [PrimaryKey] - public long Id { get; set; } - public string Username { get; set; } - public string PasswordHash { get; set; } - - [Reference] - public SellerTable SellerTable { get; set; } - [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] - public long SellerId { get; set; } - } - - public class SlotTable : Table - { - public string TestDatasetIdentifier { get; set; } - [Reference] - public FacilityUseTable FacilityUseTable { get; set; } - [ForeignKey(typeof(FacilityUseTable), OnDelete = "CASCADE")] - public long FacilityUseId { get; set; } - public DateTime Start { get; set; } - public DateTime End { get; set; } - public long MaximumUses { get; set; } - public long LeasedUses { get; set; } - public long RemainingUses { get; set; } - public decimal? Price { get; set; } - public bool AllowCustomerCancellationFullRefund { get; set; } - public RequiredStatusType? Prepayment { get; set; } - public bool RequiresAttendeeValidation { get; set; } - public bool RequiresApproval { get; set; } - public bool RequiresAdditionalDetails { get; set; } - public List RequiredAdditionalDetails { get; set; } - public TimeSpan? ValidFromBeforeStartDate { get; set; } - public TimeSpan? LatestCancellationBeforeStartDate { get; set; } - public bool AllowsProposalAmendment { get; set; } - } - - public class FacilityUseTable : Table - { - public string TestDatasetIdentifier { get; set; } - public string Name { get; set; } - public string Description { get; set; } - [Reference] - public SellerTable SellerTable { get; set; } - [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] - public long SellerId { get; set; } // Provider - public decimal LocationLat { get; set; } - public decimal LocationLng { get; set; } - } - - public class BookingPartnerTable - { - [PrimaryKey] - public string ClientId { get; set; } - public string Name { get; set; } - public string ClientSecret { get; set; } - public string ClientUri { get; set; } - public string LogoUri { get; set; } - public string[] GrantTypes { get; set; } - public string[] RedirectUris { get; set; } - public string Scope { get; set; } - public bool Registered { get; set; } - public DateTime CreatedDate { get; set; } - public string InitialAccessToken { get; set; } - public DateTime InitialAccessTokenKeyValidUntil { get; set; } - public bool BookingsSuspended { get; set; } - public string Email { get; set; } - } - - public class GrantTable - { - public string Key { get; set; } - public string Type { get; set; } - public string SubjectId { get; set; } - public string ClientId { get; set; } - public DateTime CreationTime { get; set; } - public DateTime? Expiration { get; set; } - public string Data { get; set; } - } - - public static class DatabaseCreator - { - public static void CreateTables(OrmLiteConnectionFactory dbFactory) - { - 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(); - } - } - } -} diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index ad348147..b6d3a937 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -33,7 +33,7 @@ public static void Initialise() // Initialise() must be called on startup of each to ensure they do not wipe the database // on the first call to it #pragma warning disable 4014 - Database.GetBookingPartners(); + BookingPartnerTable.Get().Wait(); #pragma warning restore 4014 } @@ -175,7 +175,7 @@ static FakeDatabase() Randomizer.Seed = new Random((int)(DateTime.Today - new DateTime(1970, 1, 1)).TotalDays); } - private const int OpportunityCount = 2000; + private const int OpportunityCount = 10; // ToDo: make this 2000 again /// /// TODO: Call this on a schedule from both .NET Core and .NET Framework reference implementations @@ -1372,7 +1372,7 @@ public static async Task GetPrepopulatedFakeDatabase() await CreateSellerUsers(db); await CreateFakeClasses(db); await CreateFakeFacilitiesAndSlots(db); - await CreateBookingPartners(db); + await BookingPartnerTable.Create(db); transaction.Commit(); } @@ -1549,144 +1549,6 @@ public async Task GetSellerUserById(long sellerUserId) } } - /* - public List GetBookingPartnerAdministrators() - { - return new List { - new BookingPartnerAdministratorTable - { - Username = "test", - Password = "test".Sha256(), - SubjectId = "TestSubjectId", - Claims = new List - { - new Claim("https://openactive.io/sellerName", "Example Seller"), - new Claim("https://openactive.io/sellerId", "https://localhost:5001/api/identifiers/sellers/1"), - new Claim("https://openactive.io/sellerUrl", "http://abc.com"), - new Claim("https://openactive.io/sellerLogo", "http://abc.com/logo.jpg"), - new Claim("https://openactive.io/bookingServiceName", "Example Sellers Booking Service"), - new Claim("https://openactive.io/bookingServiceUrl", "http://abc.com/booking-service") - } - } - }; - } - */ - - public static async Task CreateBookingPartners(IDbConnection db) - { - var bookingPartners = new List - { - new BookingPartnerTable { Name = "Test Suite 1", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "openactive_test_suite_client_12345xaq", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, - new BookingPartnerTable { Name = "Acmefitness", ClientId = "clientid_800", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98767", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, - Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", - GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, - ClientUri = "http://example.com", - LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", - RedirectUris = new[] { "http://localhost:3000/cb" } - }, - new BookingPartnerTable { Name = "Example app", ClientId = "clientid_709", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98768", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, - Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", - GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, - ClientUri = "http://example.com", - LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", - RedirectUris = new[] { "http://localhost:3000/cb" } - }, - new BookingPartnerTable { Name = "Test Suite 2", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-primary-745ddf2d13019ce8b69c", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, - new BookingPartnerTable { Name = "Test Suite 3", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-secondary-a21518cb57af7b6052df", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now } - }; - - await db.InsertAllAsync(bookingPartners); - // To populate GrantTable locally, run the tests, e.g. `NODE_APP_INSTANCE=dev npm run start auth non-free` - } - - public async Task> GetBookingPartners() - { - using (var db = await Mem.Database.OpenAsync()) - { - return (await db.SelectAsync()).AsList(); - } - } - - public async Task> GetBookingPartners(long sellerUserId) - { - using (var db = await Mem.Database.OpenAsync()) - { - var query = db.From() - .Join((bpt, gt) => bpt.ClientId == gt.ClientId) - .Join((gt, st) => gt.SubjectId == st.Id.ToString()) - .Where(st => st.Id == sellerUserId); - return (await db.SelectAsync(query)).AsList(); - } - } - - public async Task GetBookingPartnerByInitialAccessToken(string registrationKey) - { - using (var db = await Mem.Database.OpenAsync()) - { - var bookingPartner = await db.SingleAsync(x => x.InitialAccessToken == registrationKey); - return bookingPartner?.InitialAccessTokenKeyValidUntil > DateTime.Now ? bookingPartner : null; - } - } - - public async Task GetBookingPartner(string clientId) - { - using (var db = await Mem.Database.OpenAsync()) - { - return await db.SingleAsync(x => x.ClientId == clientId); - } - } - - public async Task SaveBookingPartner(BookingPartnerTable bookingPartnerTable) - { - using (var db = await Mem.Database.OpenAsync()) - { - await db.SaveAsync(bookingPartnerTable); - } - } - - public async Task ResetBookingPartnerKey(string clientId, string key) - { - using (var db = await Mem.Database.OpenAsync()) - { - var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); - bookingPartner.Registered = false; - bookingPartner.InitialAccessToken = key; - bookingPartner.InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(2); - bookingPartner.ClientSecret = null; - await db.SaveAsync(bookingPartner); - } - } - - public async Task SetBookingPartnerKey(string clientId, string key) - { - using (var db = await Mem.Database.OpenAsync()) - { - var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); - bookingPartner.InitialAccessToken = key; - bookingPartner.InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(2); - await db.SaveAsync(bookingPartner); - } - } - - public async Task UpdateBookingPartnerScope(string clientId, string scope, bool bookingsSuspended) - { - using (var db = await Mem.Database.OpenAsync()) - { - var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); - bookingPartner.Scope = scope; - bookingPartner.BookingsSuspended = true; - await db.SaveAsync(bookingPartner); - } - } - - public async Task AddBookingPartner(BookingPartnerTable newBookingPartner) - { - using (var db = await Mem.Database.OpenAsync()) - { - await db.SaveAsync(newBookingPartner); - } - } - public async Task GetGrant(string key) { using (var db = await Mem.Database.OpenAsync()) diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs new file mode 100644 index 00000000..bfb01a17 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Threading.Tasks; +using ServiceStack.DataAnnotations; +using ServiceStack.OrmLite; + +namespace OpenActive.FakeDatabase.NET +{ + public class BookingPartnerTable + { + [PrimaryKey] + public string ClientId { get; set; } + public string Name { get; set; } + public string ClientSecret { get; set; } + public string ClientUri { get; set; } + public string LogoUri { get; set; } + public string[] GrantTypes { get; set; } + public string[] RedirectUris { get; set; } + public string Scope { get; set; } + public bool Registered { get; set; } + public DateTime CreatedDate { get; set; } + public string InitialAccessToken { get; set; } + public DateTime InitialAccessTokenKeyValidUntil { get; set; } + public bool BookingsSuspended { get; set; } + public string Email { get; set; } + + public static async Task Create(IDbConnection db) + { + var bookingPartners = new List + { + new BookingPartnerTable { Name = "Test Suite 1", ClientId = "clientid_XXX", InitialAccessToken = "openactive_test_suite_client_12345xaq", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, + new BookingPartnerTable { Name = "Acmefitness", ClientId = "clientid_800", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98767", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, + Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", + GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, + ClientUri = "http://example.com", + LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", + RedirectUris = new[] { "http://localhost:3000/cb" } + }, + new BookingPartnerTable { Name = "Example app", ClientId = "clientid_709", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98768", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, + Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", + GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, + ClientUri = "http://example.com", + LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", + RedirectUris = new[] { "http://localhost:3000/cb" } + }, + new BookingPartnerTable { Name = "Test Suite 2", ClientId = "clientid_YYY", InitialAccessToken = "dynamic-primary-745ddf2d13019ce8b69c", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, + new BookingPartnerTable { Name = "Test Suite 3", ClientId = "clientid_ZZZ", InitialAccessToken = "dynamic-secondary-a21518cb57af7b6052df", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now } + }; + + await db.InsertAllAsync(bookingPartners); + // To populate GrantTable locally, run the tests, e.g. `NODE_APP_INSTANCE=dev npm run start auth non-free` + } + + public static async Task> Get() + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + return await db.SelectAsync(); + } + } + + public static async Task> GetBySellerUserId(long sellerUserId) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + var query = db.From() + .Join((bpt, gt) => bpt.ClientId == gt.ClientId) + .Join((gt, st) => gt.SubjectId == st.Id.ToString()) + .Where(st => st.Id == sellerUserId); + return await db.SelectAsync(query); + } + } + + public static async Task GetByInitialAccessToken(string registrationKey) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + var bookingPartner = await db.SingleAsync(x => x.InitialAccessToken == registrationKey); + return bookingPartner?.InitialAccessTokenKeyValidUntil > DateTime.Now ? bookingPartner : null; + } + } + + public static async Task GetByClientId(string clientId) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + return await db.SingleAsync(x => x.ClientId == clientId); + } + } + + public static async Task Save(BookingPartnerTable bookingPartnerTable) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + await db.SaveAsync(bookingPartnerTable); + } + } + + public static async Task ResetKey(string clientId, string key) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); + bookingPartner.Registered = false; + bookingPartner.InitialAccessToken = key; + bookingPartner.InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(2); + bookingPartner.ClientSecret = null; + await db.SaveAsync(bookingPartner); + } + } + + public static async Task SetKey(string clientId, string key) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); + bookingPartner.InitialAccessToken = key; + bookingPartner.InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(2); + await db.SaveAsync(bookingPartner); + } + } + + public static async Task UpdateScope(string clientId, string scope, bool bookingsSuspended) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + var bookingPartner = await db.SingleAsync(x => x.ClientId == clientId); + bookingPartner.Scope = scope; + bookingPartner.BookingsSuspended = true; + await db.SaveAsync(bookingPartner); + } + } + + public static async Task Add(BookingPartnerTable newBookingPartner) + { + using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) + { + await db.SaveAsync(newBookingPartner); + } + } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs new file mode 100644 index 00000000..3554591f --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ServiceStack.OrmLite; + +namespace OpenActive.FakeDatabase.NET +{ + public class BookingStatistics + { + public string ClientId { get; set; } + public Dictionary BookingsByBroker { get; set; } + public int SellersEnabled { get; set; } + + // 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()) + { + var thirtyDaysAgo = DateTimeOffset.Now.AddDays(-30); + var bookingsByBroker = (await db.SelectAsync(o => o.ClientId == clientId && o.OrderCreated > thirtyDaysAgo)) + .GroupBy(o => o.BrokerName) + .ToDictionary(g => g.Key, g => g.Count()); + var sellersQuery = db.From() + .Join((bpt, gt) => bpt.ClientId == gt.ClientId) + .Join((gt, st) => gt.SubjectId == st.Id.ToString()) + .Where(bp => bp.ClientId == clientId); + var sellersEnabled = await db.SelectAsync(sellersQuery); // ToDo: this is only used in one of the cases - should we split this into two classes? + return new BookingStatistics { ClientId = clientId, BookingsByBroker = bookingsByBroker, SellersEnabled = sellersEnabled.Count }; + } + } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/ClassTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/ClassTable.cs new file mode 100644 index 00000000..daac55f9 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/ClassTable.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + public class ClassTable : Table + { + public string TestDatasetIdentifier { get; set; } + public string Title { get; set; } + [Reference] + public SellerTable SellerTable { get; set; } + [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] + public long SellerId { get; set; } + public decimal? Price { get; set; } + public RequiredStatusType? Prepayment { get; set; } + public bool RequiresAttendeeValidation { get; set; } + public bool RequiresAdditionalDetails { get; set; } + public List RequiredAdditionalDetails { get; set; } + public bool AllowCustomerCancellationFullRefund { get; set; } + public bool RequiresApproval { get; set; } + public TimeSpan? ValidFromBeforeStartDate { get; set; } + public TimeSpan? LatestCancellationBeforeStartDate { get; set; } + public decimal LocationLat { get; set; } + public decimal LocationLng { get; set; } + public AttendanceMode AttendanceMode { get; set; } + public bool AllowsProposalAmendment { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs new file mode 100644 index 00000000..20630ff9 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/DatabaseTables.cs @@ -0,0 +1,62 @@ +using System; +using ServiceStack.DataAnnotations; +using ServiceStack.OrmLite; + +namespace OpenActive.FakeDatabase.NET +{ + public enum BrokerRole { AgentBroker, ResellerBroker, NoBroker } + + public enum BookingStatus { None, CustomerCancelled, SellerCancelled, Confirmed, Attended, Absent } + + public enum ProposalStatus { AwaitingSellerConfirmation, SellerAccepted, SellerRejected, CustomerRejected } + + public enum FeedVisibility { None, Visible, Archived } + + public enum OrderMode { Lease, Proposal, Booking } + + public enum RequiredStatusType { Required, Optional, Unavailable } + + public enum AttendanceMode { Offline, Online, Mixed } + + public enum AdditionalDetailTypes { Age, PhotoConsent, Experience, Gender, FileUpload } + + [CompositeIndex(nameof(Modified), nameof(Id))] + public abstract class Table + { + [PrimaryKey] + [AutoIncrement] + public long Id { get; set; } + public bool Deleted { get; set; } + public long Modified { get; set; } = new DateTimeOffset(DateTime.Today).UtcTicks; + } + + public static class DatabaseCreator + { + public static void CreateTables(OrmLiteConnectionFactory dbFactory) + { + 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(); + } + } + } +} diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/FacilityUseTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/FacilityUseTable.cs new file mode 100644 index 00000000..c875db32 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/FacilityUseTable.cs @@ -0,0 +1,17 @@ +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + public class FacilityUseTable : Table + { + public string TestDatasetIdentifier { get; set; } + public string Name { get; set; } + public string Description { get; set; } + [Reference] + public SellerTable SellerTable { get; set; } + [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] + public long SellerId { get; set; } // Provider + public decimal LocationLat { get; set; } + public decimal LocationLng { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/GrantTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/GrantTable.cs new file mode 100644 index 00000000..cff858f5 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/GrantTable.cs @@ -0,0 +1,15 @@ +using System; + +namespace OpenActive.FakeDatabase.NET +{ + public class GrantTable + { + public string Key { get; set; } + public string Type { get; set; } + public string SubjectId { get; set; } + public string ClientId { get; set; } + public DateTime CreationTime { get; set; } + public DateTime? Expiration { get; set; } + public string Data { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/OccurrenceTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/OccurrenceTable.cs new file mode 100644 index 00000000..f06b6761 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/OccurrenceTable.cs @@ -0,0 +1,19 @@ +using System; +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + public class OccurrenceTable : Table + { + public string TestDatasetIdentifier { get; set; } + [Reference] + public ClassTable ClassTable { get; set; } + [ForeignKey(typeof(ClassTable), OnDelete = "CASCADE")] + public long ClassId { get; set; } + public DateTime Start { get; set; } + public DateTime End { get; set; } + public long TotalSpaces { get; set; } + public long LeasedSpaces { get; set; } + public long RemainingSpaces { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs new file mode 100644 index 00000000..004c19f4 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderItemsTable.cs @@ -0,0 +1,34 @@ +using System; +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + public class OrderItemsTable : Table + { + public string ClientId { get; internal set; } + public Uri OpportunityJsonLdId { get; set; } + public Uri OfferJsonLdId { get; set; } + [Reference] + public OrderTable OrderTable { get; set; } + [ForeignKey(typeof(OrderTable), OnDelete = "CASCADE")] + public string OrderId { get; set; } + [Reference] + public OccurrenceTable OccurrenceTable { get; set; } + [ForeignKey(typeof(OccurrenceTable), OnDelete = "CASCADE")] + public long? OccurrenceId { get; set; } + [Reference] + public SlotTable SlotTable { get; set; } + [ForeignKey(typeof(SlotTable), OnDelete = "CASCADE")] + public long? SlotId { get; set; } + public BookingStatus Status { get; set; } + public string CancellationMessage { get; set; } + public decimal Price { get; set; } + public string PinCode { get; set; } + public string ImageUrl { get; set; } + public string BarCodeText { get; set; } + public string CustomerNotice { get; set; } + public Uri MeetingUrl { get; set; } + public string MeetingId { get; set; } + public string MeetingPassword { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs new file mode 100644 index 00000000..f83fd1a8 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/OrderTable.cs @@ -0,0 +1,50 @@ +using System; +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + [CompositeIndex(nameof(OrderModified), nameof(OrderId))] + [CompositeIndex(nameof(OrderProposalModified), nameof(OrderId))] + public class OrderTable + { + /** + * Note string type is used for the OrderId instead of Guid type to allow for correct ordering of GUIDs for the RPDE feed + */ + [PrimaryKey] + public string OrderId { get; set; } + + public bool Deleted { get; set; } + public DateTimeOffset OrderCreated { get; set; } = DateTimeOffset.Now; + public long OrderModified { get; set; } = DateTimeOffset.Now.UtcTicks; + public long OrderProposalModified { get; set; } = DateTimeOffset.Now.UtcTicks; + public string ClientId { get; set; } + + [Reference] + public SellerTable SellerTable { get; set; } + [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] + public long SellerId { get; set; } + public bool? CustomerIsOrganization { get; set; } + public CustomerType CustomerType { get; set; } + public BrokerRole BrokerRole { get; set; } + public string BrokerName { get; set; } + public Uri BrokerUrl { get; set; } + public string BrokerTelephone { get; set; } + public string CustomerEmail { get; set; } + public string CustomerIdentifier { get; set; } + public string CustomerGivenName { get; set; } + public string CustomerFamilyName { get; set; } + public string CustomerTelephone { get; set; } + public string CustomerOrganizationName { get; set; } + public string PaymentIdentifier { get; set; } + public string PaymentName { get; set; } + public string PaymentProviderId { get; set; } + public string PaymentAccountId { get; set; } + public decimal TotalOrderPrice { get; set; } + public OrderMode OrderMode { get; set; } + public DateTime LeaseExpires { get; set; } + public FeedVisibility VisibleInOrdersFeed { get; set; } + public FeedVisibility VisibleInOrderProposalsFeed { get; set; } + public ProposalStatus? ProposalStatus { get; set; } + public Guid? ProposalVersionId { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/SellerTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/SellerTable.cs new file mode 100644 index 00000000..14af6939 --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/SellerTable.cs @@ -0,0 +1,15 @@ +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + public class SellerTable + { + [PrimaryKey] + public long Id { get; set; } + public string Name { get; set; } + public bool IsIndividual { get; set; } + public string Url { get; set; } + public bool IsTaxGross { get; set; } + public string LogoUrl { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs new file mode 100644 index 00000000..73f9290b --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs @@ -0,0 +1,17 @@ +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + public class SellerUserTable + { + [PrimaryKey] + public long Id { get; set; } + public string Username { get; set; } + public string PasswordHash { get; set; } + + [Reference] + public SellerTable SellerTable { get; set; } + [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] + public long SellerId { get; set; } + } +} \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/SlotTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/SlotTable.cs new file mode 100644 index 00000000..35fea58e --- /dev/null +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/SlotTable.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using ServiceStack.DataAnnotations; + +namespace OpenActive.FakeDatabase.NET +{ + public class SlotTable : Table + { + public string TestDatasetIdentifier { get; set; } + [Reference] + public FacilityUseTable FacilityUseTable { get; set; } + [ForeignKey(typeof(FacilityUseTable), OnDelete = "CASCADE")] + public long FacilityUseId { get; set; } + public DateTime Start { get; set; } + public DateTime End { get; set; } + public long MaximumUses { get; set; } + public long LeasedUses { get; set; } + public long RemainingUses { get; set; } + public decimal? Price { get; set; } + public bool AllowCustomerCancellationFullRefund { get; set; } + public RequiredStatusType? Prepayment { get; set; } + public bool RequiresAttendeeValidation { get; set; } + public bool RequiresApproval { get; set; } + public bool RequiresAdditionalDetails { get; set; } + public List RequiredAdditionalDetails { get; set; } + public TimeSpan? ValidFromBeforeStartDate { get; set; } + public TimeSpan? LatestCancellationBeforeStartDate { get; set; } + public bool AllowsProposalAmendment { get; set; } + } +} \ No newline at end of file From 8b8e913cf550fb0336929d569130c3537c91623e Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 18:45:23 +0100 Subject: [PATCH 07/14] fix: sysadmin screen buttons --- .../Custom/Grants/BookingPartnersController.cs | 9 +-------- .../Views/BookingPartners/BookingPartnerEdit.cshtml | 2 +- .../Views/BookingPartners/SysAdmin.cshtml | 13 +++++-------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs index 873c346d..c88ae009 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs @@ -86,13 +86,6 @@ public async Task Create([FromForm] string email, [FromForm] stri return Redirect($"/booking-partners/edit/{newBookingPartner.ClientId}"); } - [HttpPost("manage-keys")] - [ValidateAntiForgeryToken] - public IActionResult ManageKeys([FromForm] string clientId) - { - return Redirect("/booking-partners/seller-admin"); - } - [HttpPost("restore")] [ValidateAntiForgeryToken] public IActionResult Restore([FromForm] string clientId) @@ -124,7 +117,7 @@ public async Task Delete([FromForm] string clientId) await FakeBookingSystem.Database.DeleteBookingPartner(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); - return Redirect("/booking-partners/seller-admin"); + return Redirect("/booking-partners/sys-admin"); } /// diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml index b7f00072..df5dc8cc 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml @@ -66,7 +66,7 @@
- Close + Close
\ No newline at end of file diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml index e836f3f3..37667dbf 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml @@ -45,14 +45,11 @@ API Key - @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) - { -
- @Html.AntiForgeryToken() - - -
- } +
+ @Html.AntiForgeryToken() + + +
} From b8bed8e60732dccd98473ac9be269ee4f7200fb4 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 18:54:56 +0100 Subject: [PATCH 08/14] feat: make opportunity count configurable --- Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index b6d3a937..d78935d6 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -175,7 +175,8 @@ static FakeDatabase() Randomizer.Seed = new Random((int)(DateTime.Today - new DateTime(1970, 1, 1)).TotalDays); } - private const int OpportunityCount = 10; // ToDo: make this 2000 again + private static readonly int OpportunityCount = + int.TryParse(Environment.GetEnvironmentVariable("OPPORTUNITY_COUNT"), out var opportunityCount) ? opportunityCount : 2000; /// /// TODO: Call this on a schedule from both .NET Core and .NET Framework reference implementations From 62418d5319ed1357e022c34b00065a7a4edf0704 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 19:18:50 +0100 Subject: [PATCH 09/14] fix: seller admin page fixes --- .../Custom/Client/ClientRegistrationController.cs | 1 + .../Custom/Grants/BookingPartnerViewModel.cs | 2 ++ .../Custom/Grants/BookingPartnersController.cs | 7 ------- .../Views/BookingPartners/SellerAdmin.cshtml | 8 ++------ .../wwwroot/css/site.css | 3 +++ .../Models/BookingPartnerTable.cs | 3 +++ 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs index 9f101165..aa176509 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs @@ -58,6 +58,7 @@ public async Task PostAsync([FromBody] ClientRegistrationModel mo bookingPartner.ClientSecret = key.Sha256(); bookingPartner.Name = model.ClientName; bookingPartner.ClientUri = model.ClientUri; + bookingPartner.RestoreAccessUri = model.ClientUri; // ToDo: is this right? bookingPartner.LogoUri = model.LogoUri; bookingPartner.GrantTypes = model.GrantTypes; bookingPartner.RedirectUris = model.RedirectUris; diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs index 62112c36..127dcdeb 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnerViewModel.cs @@ -27,6 +27,7 @@ public static async Task Build(long? sellerUserId = nul ClientName = bookingPartner.Name, ClientLogoUrl = bookingPartner.LogoUri, ClientUrl = bookingPartner.ClientUri, + RestoreAccessUrl = bookingPartner.RestoreAccessUri, BookingPartner = bookingPartner, SellersEnabled = bookingStatistics.SellersEnabled, BookingsByBroker = bookingStatistics.BookingsByBroker @@ -42,6 +43,7 @@ public class BookingPartnerModel public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } + public string RestoreAccessUrl { get; set; } public string ClientLogoUrl { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs index c88ae009..ff4e22a1 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Grants/BookingPartnersController.cs @@ -86,13 +86,6 @@ public async Task Create([FromForm] string email, [FromForm] stri return Redirect($"/booking-partners/edit/{newBookingPartner.ClientId}"); } - [HttpPost("restore")] - [ValidateAntiForgeryToken] - public IActionResult Restore([FromForm] string clientId) - { - return Redirect("/booking-partners/seller-admin"); - } - /// /// Handle postback to remove a client /// diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml index 97d40e57..cdf1524f 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml @@ -42,17 +42,13 @@
@Html.AntiForgeryToken() - +
} @if (bookingPartner.BookingPartner.Registered && bookingPartner.BookingPartner.BookingsSuspended) { -
- @Html.AntiForgeryToken() - - -
+ Restore } @if (!bookingPartner.BookingPartner.Registered || bookingPartner.BookingPartner.BookingsSuspended) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/wwwroot/css/site.css b/Examples/BookingSystem.AspNetCore.IdentityServer/wwwroot/css/site.css index a0bbbeda..b101bbb8 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/wwwroot/css/site.css +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/wwwroot/css/site.css @@ -79,4 +79,7 @@ } .grants .grant li:last-child:after { content: ''; +} +.btn-suspend-bookings { + width: 157px; /* ToDo: do this better */ } \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs index bfb01a17..c081a3c1 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs @@ -14,6 +14,7 @@ public class BookingPartnerTable public string Name { get; set; } public string ClientSecret { get; set; } public string ClientUri { get; set; } + public string RestoreAccessUri { get; set; } public string LogoUri { get; set; } public string[] GrantTypes { get; set; } public string[] RedirectUris { get; set; } @@ -34,6 +35,7 @@ public static async Task Create(IDbConnection db) Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, ClientUri = "http://example.com", + RestoreAccessUri = "http://example.com", LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", RedirectUris = new[] { "http://localhost:3000/cb" } }, @@ -41,6 +43,7 @@ public static async Task Create(IDbConnection db) Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, ClientUri = "http://example.com", + RestoreAccessUri = "http://example.com", LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", RedirectUris = new[] { "http://localhost:3000/cb" } }, From 2f04723cde15703b1582c629ad609ca30810b1d3 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 19:30:31 +0100 Subject: [PATCH 10/14] fix: seller admin page query --- .../Models/BookingPartnerTable.cs | 8 ++++---- .../Models/BookingStatistics.cs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs index c081a3c1..857d892f 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs @@ -52,7 +52,7 @@ public static async Task Create(IDbConnection db) }; await db.InsertAllAsync(bookingPartners); - // To populate GrantTable locally, run the tests, e.g. `NODE_APP_INSTANCE=dev npm run start auth non-free` + // To populate GrantTable locally, run the tests, e.g. `NODE_APP_INSTANCE=dev npm run start auth non-free` } public static async Task> Get() @@ -68,9 +68,9 @@ public static async Task> GetBySellerUserId(long selle using (var db = await FakeBookingSystem.Database.Mem.Database.OpenAsync()) { var query = db.From() - .Join((bpt, gt) => bpt.ClientId == gt.ClientId) - .Join((gt, st) => gt.SubjectId == st.Id.ToString()) - .Where(st => st.Id == sellerUserId); + .Join((b, g) => b.ClientId == g.ClientId && g.Type == "user_consent") + .Join((g, s) => g.SubjectId == s.Id.ToString()) + .Where(s => s.Id == sellerUserId); return await db.SelectAsync(query); } } diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs index 3554591f..af61cb1a 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingStatistics.cs @@ -22,9 +22,9 @@ public static async Task Get(string clientId) .GroupBy(o => o.BrokerName) .ToDictionary(g => g.Key, g => g.Count()); var sellersQuery = db.From() - .Join((bpt, gt) => bpt.ClientId == gt.ClientId) - .Join((gt, st) => gt.SubjectId == st.Id.ToString()) - .Where(bp => bp.ClientId == clientId); + .Join((b, g) => b.ClientId == g.ClientId) + .Join((g, s) => g.SubjectId == s.Id.ToString()) + .Where(b => b.ClientId == clientId); var sellersEnabled = await db.SelectAsync(sellersQuery); // ToDo: this is only used in one of the cases - should we split this into two classes? return new BookingStatistics { ClientId = clientId, BookingsByBroker = bookingsByBroker, SellersEnabled = sellersEnabled.Count }; } From 3c1c1311ae3ec608370c14c29daf7b66a99f2455 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 19:47:24 +0100 Subject: [PATCH 11/14] feat: add test creds to login page --- .../Custom/Account/AccountController.cs | 2 +- .../Custom/Account/AccountOptions.cs | 1 - .../Views/Account/LoggedOut.cshtml | 34 ------------------- .../Views/Account/Login.cshtml | 19 +++++++++-- .../FakeBookingSystem.cs | 22 ++++++------ .../Models/SellerUserTable.cs | 15 ++++++++ 6 files changed, 44 insertions(+), 49 deletions(-) delete mode 100644 Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/LoggedOut.cshtml diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs index 992134c4..227df37a 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountController.cs @@ -208,7 +208,7 @@ public async Task Logout(LogoutInputModel model) return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } - return View("LoggedOut", vm); + return Redirect("/"); } [HttpGet] diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountOptions.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountOptions.cs index f191706c..dc2e640e 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountOptions.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Account/AccountOptions.cs @@ -1,7 +1,6 @@ // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - using System; namespace src diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/LoggedOut.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/LoggedOut.cshtml deleted file mode 100644 index 99599c0c..00000000 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/LoggedOut.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@model LoggedOutViewModel - -@{ - // set this so the layout rendering sees an anonymous user - ViewData["signed-out"] = true; -} - - - -@section scripts -{ - @if (Model.AutomaticRedirectAfterSignOut) - { - - } -} diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/Login.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/Login.cshtml index 5905626e..1e69d86c 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/Login.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Account/Login.cshtml @@ -1,3 +1,4 @@ +@using OpenActive.FakeDatabase.NET @model LoginViewModel \ No newline at end of file diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index d78935d6..0df4c6be 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -1510,19 +1510,19 @@ public static async Task CreateSellers(IDbConnection db) await db.InsertAllAsync(sellers); } + public static readonly SellerUserTable[] DefaultSellerUsers = + { + new SellerUserTable {Id = 100, Username = "test1", PasswordRaw = "test1", SellerId = 1}, + new SellerUserTable {Id = 101, Username = "test1b", PasswordRaw = "test1b", SellerId = 1}, + new SellerUserTable {Id = 102, Username = "test2", PasswordRaw = "test2", SellerId = 2}, + new SellerUserTable {Id = 103, Username = "test3", PasswordRaw = "test3", SellerId = 3}, + new SellerUserTable {Id = 104, Username = "test4", PasswordRaw = "test4", SellerId = 4}, + new SellerUserTable {Id = 105, Username = "test5", PasswordRaw = "test5", SellerId = 5}, + }; + public static async Task CreateSellerUsers(IDbConnection db) { - var sellerUsers = new List - { - new SellerUserTable { Id = 100, Username = "test1", PasswordHash = "test1".Sha256(), SellerId = 1 }, - new SellerUserTable { Id = 101, Username = "test1b", PasswordHash = "test1b".Sha256(), SellerId = 1 }, - new SellerUserTable { Id = 102, Username = "test2", PasswordHash = "test2".Sha256(), SellerId = 2 }, - new SellerUserTable { Id = 103, Username = "test3", PasswordHash = "test3".Sha256(), SellerId = 3 }, - new SellerUserTable { Id = 104, Username = "test4", PasswordHash = "test4".Sha256(), SellerId = 4 }, - new SellerUserTable { Id = 105, Username = "test5", PasswordHash = "test5".Sha256(), SellerId = 5 }, - }; - - await db.InsertAllAsync(sellerUsers); + await db.InsertAllAsync(DefaultSellerUsers); } public async Task ValidateSellerUserCredentials(string username, string password) diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs index 73f9290b..0eb75628 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/SellerUserTable.cs @@ -6,11 +6,26 @@ public class SellerUserTable { [PrimaryKey] public long Id { get; set; } + public string Username { get; set; } + + [Ignore] + public string PasswordRaw + { + get => _passwordRaw; + set + { + _passwordRaw = value; + PasswordHash = value.Sha256(); + } + } + private string _passwordRaw; + public string PasswordHash { get; set; } [Reference] public SellerTable SellerTable { get; set; } + [ForeignKey(typeof(SellerTable), OnDelete = "CASCADE")] public long SellerId { get; set; } } From 0cfd625464e2e8579e940fab5705c6bfabf7ed3d Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 20:07:25 +0100 Subject: [PATCH 12/14] feat: some test data --- .../Views/Home/Index.cshtml | 5 +- .../FakeBookingSystem.cs | 103 ++++++++++++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml index 827059e4..c625bc55 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml @@ -6,9 +6,8 @@ diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index 0df4c6be..3e3bc58a 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -1373,6 +1373,8 @@ public static async Task GetPrepopulatedFakeDatabase() await CreateSellerUsers(db); await CreateFakeClasses(db); await CreateFakeFacilitiesAndSlots(db); + await CreateOrders(db); + await CreateGrants(db); await BookingPartnerTable.Create(db); transaction.Commit(); } @@ -1510,6 +1512,107 @@ public static async Task CreateSellers(IDbConnection db) await db.InsertAllAsync(sellers); } + public static async Task CreateOrders(IDbConnection db) + { + var orders = new List + { + new OrderTable + { + OrderId = Guid.NewGuid().ToString(), + Deleted = false, + OrderCreated = DateTimeOffset.Now, + OrderModified = DateTimeOffset.Now.Ticks, + OrderProposalModified = DateTimeOffset.Now.Ticks, + ClientId = "clientid_XXX", + SellerId = 1, + CustomerType = CustomerType.Person, + BrokerRole = BrokerRole.AgentBroker, + BrokerName = "MyFitnessApp", + BrokerUrl = new Uri("https://myfitnessapp.example.com/"), + CustomerEmail = "Ardith72@hotmail.com", + CustomerIdentifier = Guid.NewGuid().ToString(), + CustomerGivenName = "Hills", + CustomerFamilyName = "Modesta", + CustomerTelephone = "731.403.0727", + PaymentIdentifier = "dyulZE-Kt", + PaymentName = "AcmeBroker Points", + PaymentProviderId = "STRIPE", + PaymentAccountId = "SN1593", + TotalOrderPrice = 14.99M, + OrderMode = OrderMode.Booking, + LeaseExpires = DateTime.Now.AddDays(10), + VisibleInOrdersFeed = FeedVisibility.None + }, + new OrderTable + { + OrderId = Guid.NewGuid().ToString(), + Deleted = false, + OrderCreated = DateTimeOffset.Now, + OrderModified = DateTimeOffset.Now.Ticks, + OrderProposalModified = DateTimeOffset.Now.Ticks, + ClientId = "clientid_XXX", + SellerId = 1, + CustomerType = CustomerType.Person, + BrokerRole = BrokerRole.AgentBroker, + BrokerName = "MyFitnessApp", + BrokerUrl = new Uri("https://myfitnessapp.example.com/"), + CustomerEmail = "Zelma.Pacocha79@gmail.com", + CustomerIdentifier = Guid.NewGuid().ToString(), + CustomerGivenName = "Boyer", + CustomerFamilyName = "Santos", + CustomerTelephone = "1-346-608-5991 x53561", + PaymentIdentifier = "JU1ktRR7U", + PaymentName = "AcmeBroker Points", + PaymentProviderId = "STRIPE", + PaymentAccountId = "SN1593", + TotalOrderPrice = 14.99M, + OrderMode = OrderMode.Booking, + LeaseExpires = DateTime.Now.AddDays(10), + VisibleInOrdersFeed = FeedVisibility.None + }, + new OrderTable + { + OrderId = Guid.NewGuid().ToString(), + Deleted = false, + OrderCreated = DateTimeOffset.Now, + OrderModified = DateTimeOffset.Now.Ticks, + OrderProposalModified = DateTimeOffset.Now.Ticks, + ClientId = "clientid_XXX", + SellerId = 1, + CustomerType = CustomerType.Person, + BrokerRole = BrokerRole.AgentBroker, + BrokerName = "MyFitnessApp", + BrokerUrl = new Uri("https://myfitnessapp.example.com/"), + CustomerEmail = "Regan_Moen4@gmail.com", + CustomerIdentifier = Guid.NewGuid().ToString(), + CustomerGivenName = "Kohler", + CustomerFamilyName = "Toby", + CustomerTelephone = "1-585-849-0456", + PaymentIdentifier = "Lr4GW6MNQ", + PaymentName = "AcmeBroker Points", + PaymentProviderId = "STRIPE", + PaymentAccountId = "SN1593", + TotalOrderPrice = 59.96M, + OrderMode = OrderMode.Booking, + LeaseExpires = DateTime.Now.AddDays(10), + VisibleInOrdersFeed = FeedVisibility.None + } + }; + + await db.InsertAllAsync(orders); + } + + 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" } + }; + + await db.InsertAllAsync(grants); + } + public static readonly SellerUserTable[] DefaultSellerUsers = { new SellerUserTable {Id = 100, Username = "test1", PasswordRaw = "test1", SellerId = 1}, From f245db91e49956805057ba499fa09aa39ca825df Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 21:01:08 +0100 Subject: [PATCH 13/14] fix: client registration uri --- .../Custom/Client/ClientRegistrationController.cs | 5 ++++- Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs index aa176509..39ff6a4c 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs @@ -58,7 +58,7 @@ public async Task PostAsync([FromBody] ClientRegistrationModel mo bookingPartner.ClientSecret = key.Sha256(); bookingPartner.Name = model.ClientName; bookingPartner.ClientUri = model.ClientUri; - bookingPartner.RestoreAccessUri = model.ClientUri; // ToDo: is this right? + bookingPartner.RestoreAccessUri = model.ClientRegistrationUri; bookingPartner.LogoUri = model.LogoUri; bookingPartner.GrantTypes = model.GrantTypes; bookingPartner.RedirectUris = model.RedirectUris; @@ -96,6 +96,9 @@ public class ClientRegistrationModel [JsonPropertyName(OidcConstants.ClientMetadata.ClientUri)] public string ClientUri { get; set; } + [JsonPropertyName("registration_client_uri")] + public string ClientRegistrationUri { get; set; } + [JsonPropertyName(OidcConstants.ClientMetadata.LogoUri)] public string LogoUri { get; set; } diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index 3e3bc58a..2fafab53 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -1373,8 +1373,8 @@ public static async Task GetPrepopulatedFakeDatabase() await CreateSellerUsers(db); await CreateFakeClasses(db); await CreateFakeFacilitiesAndSlots(db); - await CreateOrders(db); - await CreateGrants(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(); } From 814ab693bac4b28b672f8f63f7cd13f88cc174e9 Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Wed, 30 Jun 2021 22:03:39 +0100 Subject: [PATCH 14/14] fix: test data --- .../Client/ClientRegistrationController.cs | 2 +- .../BookingPartners/BookingPartnerEdit.cshtml | 3 --- .../Views/BookingPartners/SellerAdmin.cshtml | 2 +- .../Views/BookingPartners/SysAdmin.cshtml | 2 +- .../Views/Home/Index.cshtml | 4 ++-- .../Views/Shared/_Layout.cshtml | 6 ++--- .../FakeBookingSystem.cs | 17 ++++++------- .../Models/BookingPartnerTable.cs | 24 +++++++++++++++---- 8 files changed, 37 insertions(+), 23 deletions(-) diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs index 39ff6a4c..ba0201d5 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Custom/Client/ClientRegistrationController.cs @@ -96,7 +96,7 @@ public class ClientRegistrationModel [JsonPropertyName(OidcConstants.ClientMetadata.ClientUri)] public string ClientUri { get; set; } - [JsonPropertyName("registration_client_uri")] + [JsonPropertyName(OidcConstants.ClientMetadata.InitiateLoginUris)] public string ClientRegistrationUri { get; set; } [JsonPropertyName(OidcConstants.ClientMetadata.LogoUri)] diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml index df5dc8cc..c6183e01 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/BookingPartnerEdit.cshtml @@ -33,9 +33,6 @@
-
- -
diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml index cdf1524f..e055a271 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SellerAdmin.cshtml @@ -10,7 +10,7 @@ @if (Model.BookingPartners.Any() == false) {
- You have not given access to any applications + You have not given access to any booking partners
} else diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml index 37667dbf..91c8eca7 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/BookingPartners/SysAdmin.cshtml @@ -10,7 +10,7 @@ @if (!Model.BookingPartners.Any()) {
- You have not given access to any applications + There are no booking partners in the system
} else diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml index c625bc55..94d6f659 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Home/Index.cshtml @@ -6,8 +6,8 @@ diff --git a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml index 32fd70f1..95350008 100644 --- a/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml +++ b/Examples/BookingSystem.AspNetCore.IdentityServer/Views/Shared/_Layout.cshtml @@ -12,7 +12,7 @@ - OpenActive + Lorem Fitsum @@ -31,8 +31,8 @@ - OpenActive - OpenActive + Lorem Fitsum + Lorem Fitsum
diff --git a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs index 2fafab53..f02925ee 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/FakeBookingSystem.cs @@ -1373,8 +1373,8 @@ public static async Task GetPrepopulatedFakeDatabase() 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 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(); } @@ -1527,7 +1527,7 @@ public static async Task CreateOrders(IDbConnection db) SellerId = 1, CustomerType = CustomerType.Person, BrokerRole = BrokerRole.AgentBroker, - BrokerName = "MyFitnessApp", + BrokerName = "Adult Fitness Challenge", BrokerUrl = new Uri("https://myfitnessapp.example.com/"), CustomerEmail = "Ardith72@hotmail.com", CustomerIdentifier = Guid.NewGuid().ToString(), @@ -1550,11 +1550,11 @@ public static async Task CreateOrders(IDbConnection db) OrderCreated = DateTimeOffset.Now, OrderModified = DateTimeOffset.Now.Ticks, OrderProposalModified = DateTimeOffset.Now.Ticks, - ClientId = "clientid_XXX", + ClientId = "clientid_YYY", SellerId = 1, CustomerType = CustomerType.Person, BrokerRole = BrokerRole.AgentBroker, - BrokerName = "MyFitnessApp", + BrokerName = "Healthy Steps App", BrokerUrl = new Uri("https://myfitnessapp.example.com/"), CustomerEmail = "Zelma.Pacocha79@gmail.com", CustomerIdentifier = Guid.NewGuid().ToString(), @@ -1577,11 +1577,11 @@ public static async Task CreateOrders(IDbConnection db) OrderCreated = DateTimeOffset.Now, OrderModified = DateTimeOffset.Now.Ticks, OrderProposalModified = DateTimeOffset.Now.Ticks, - ClientId = "clientid_XXX", + ClientId = "clientid_YYY", SellerId = 1, CustomerType = CustomerType.Person, BrokerRole = BrokerRole.AgentBroker, - BrokerName = "MyFitnessApp", + BrokerName = "Healthy Steps Website", BrokerUrl = new Uri("https://myfitnessapp.example.com/"), CustomerEmail = "Regan_Moen4@gmail.com", CustomerIdentifier = Guid.NewGuid().ToString(), @@ -1607,7 +1607,8 @@ 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_YYY", SubjectId = "100", Type = "user_consent" }, + new GrantTable { ClientId = "clientid_ZZZ", SubjectId = "100", Type = "user_consent" }, }; await db.InsertAllAsync(grants); diff --git a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs index 857d892f..bc4b18ef 100644 --- a/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs +++ b/Fakes/OpenActive.FakeDatabase.NET/Models/BookingPartnerTable.cs @@ -30,7 +30,7 @@ public static async Task Create(IDbConnection db) { var bookingPartners = new List { - new BookingPartnerTable { Name = "Test Suite 1", ClientId = "clientid_XXX", InitialAccessToken = "openactive_test_suite_client_12345xaq", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, + new BookingPartnerTable { Name = "RED January", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "openactive_test_suite_client_12345xaq", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, new BookingPartnerTable { Name = "Acmefitness", ClientId = "clientid_800", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98767", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, @@ -39,7 +39,25 @@ public static async Task Create(IDbConnection db) LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", RedirectUris = new[] { "http://localhost:3000/cb" } }, - new BookingPartnerTable { Name = "Example app", ClientId = "clientid_709", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98768", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, + new BookingPartnerTable { Name = "Mum's runs", ClientId = "clientid_801", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "98768", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, + Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", + GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, + ClientUri = "http://example.com", + RestoreAccessUri = "http://example.com", + LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", + RedirectUris = new[] { "http://localhost:3000/cb" } + }, + new BookingPartnerTable { Name = "Vivacity Insurance", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-primary-745ddf2d13019ce8b69c", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, + new BookingPartnerTable { Name = "Active with Friends", ClientId = Guid.NewGuid().ToString(), InitialAccessToken = "dynamic-secondary-a21518cb57af7b6052df", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, + new BookingPartnerTable { Name = "Ultimate Fitness Challenge", ClientId = "clientid_XXX", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "123123", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, + Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", + GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, + ClientUri = "http://example.com", + RestoreAccessUri = "http://example.com", + LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", + RedirectUris = new[] { "http://localhost:3000/cb" } + }, + new BookingPartnerTable { Name = "Healthy Steps Every Day", ClientId = "clientid_YYY", ClientSecret = "secret".Sha256(), Email="garden@health.com", Registered = true, InitialAccessToken = "456456", InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now, BookingsSuspended = false, Scope = "openid profile openactive-openbooking openactive-ordersfeed openactive-identity", GrantTypes = new[] { "client_credentials", "refresh_token", "authorization_code" }, ClientUri = "http://example.com", @@ -47,8 +65,6 @@ public static async Task Create(IDbConnection db) LogoUri = "https://via.placeholder.com/512x256.png?text=Logo", RedirectUris = new[] { "http://localhost:3000/cb" } }, - new BookingPartnerTable { Name = "Test Suite 2", ClientId = "clientid_YYY", InitialAccessToken = "dynamic-primary-745ddf2d13019ce8b69c", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now }, - new BookingPartnerTable { Name = "Test Suite 3", ClientId = "clientid_ZZZ", InitialAccessToken = "dynamic-secondary-a21518cb57af7b6052df", Registered = false, InitialAccessTokenKeyValidUntil = DateTime.Now.AddDays(1), CreatedDate = DateTime.Now } }; await db.InsertAllAsync(bookingPartners);