From ee2273e65a651bd8472707c04fb070db267a716e Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Wed, 6 Sep 2023 11:56:23 -0400 Subject: [PATCH 01/53] Shared lib updated with Nullable context. Partially fixes issue #105. --- .../BlazingPizza.Shared/Address.cs | 12 ++++++------ .../BlazingPizza.Shared.csproj | 17 +++++++++-------- .../NotificationSubscription.cs | 10 +++++----- .../00-get-started/BlazingPizza.Shared/Order.cs | 6 ++++-- .../BlazingPizza.Shared/OrderWithStatus.cs | 10 +++++++--- .../00-get-started/BlazingPizza.Shared/Pizza.cs | 8 +++++--- .../BlazingPizza.Shared/PizzaSpecial.cs | 6 +++--- .../BlazingPizza.Shared/PizzaTopping.cs | 2 +- .../BlazingPizza.Shared/Topping.cs | 2 +- .../BlazingPizza.Shared/UserInfo.cs | 2 +- 10 files changed, 42 insertions(+), 33 deletions(-) diff --git a/save-points/00-get-started/BlazingPizza.Shared/Address.cs b/save-points/00-get-started/BlazingPizza.Shared/Address.cs index 528622e1..d1a53375 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/Address.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/Address.cs @@ -4,15 +4,15 @@ public class Address { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; - public string City { get; set; } + public string City { get; set; } = string.Empty; - public string Region { get; set; } + public string Region { get; set; } = string.Empty; - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/save-points/00-get-started/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/00-get-started/BlazingPizza.Shared/BlazingPizza.Shared.csproj index fb17e53b..785c204e 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/00-get-started/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,17 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - - + + + + diff --git a/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/00-get-started/BlazingPizza.Shared/Order.cs b/save-points/00-get-started/BlazingPizza.Shared/Order.cs index b47efad0..cba9a17d 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/Order.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); diff --git a/save-points/00-get-started/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/00-get-started/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/00-get-started/BlazingPizza.Shared/Pizza.cs b/save-points/00-get-started/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/Pizza.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/00-get-started/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/00-get-started/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/00-get-started/BlazingPizza.Shared/PizzaTopping.cs b/save-points/00-get-started/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/00-get-started/BlazingPizza.Shared/Topping.cs b/save-points/00-get-started/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/Topping.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/00-get-started/BlazingPizza.Shared/UserInfo.cs b/save-points/00-get-started/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From d2669b4f40bc0567fec527203535d60924542d90 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Wed, 6 Sep 2023 12:30:00 -0400 Subject: [PATCH 02/53] Server updated with Nullable context. Partially fixes issue #105. --- .../Areas/Identity/Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server/BlazingPizza.Server.csproj | 1 + .../BlazingPizza.Server/OrdersController.cs | 11 +++-------- .../BlazingPizza.Server/PizzaApiExtensions.cs | 6 ++++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/save-points/00-get-started/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/00-get-started/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/00-get-started/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/00-get-started/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
- @User.Identity.Name + @User?.Identity?.Name
diff --git a/save-points/00-get-started/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/00-get-started/BlazingPizza.Server/BlazingPizza.Server.csproj index 227890e1..a1043cb6 100644 --- a/save-points/00-get-started/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/00-get-started/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs b/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs index 37e37cc5..a0a99b09 100644 --- a/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs +++ b/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs @@ -61,12 +61,12 @@ public async Task> PlaceOrder(Order order) // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -75,7 +75,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -84,11 +84,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track diff --git a/save-points/00-get-started/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/00-get-started/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..e82b077b 100644 --- a/save-points/00-get-started/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/00-get-started/BlazingPizza.Server/PizzaApiExtensions.cs @@ -49,9 +49,11 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) + public static string GetUserId(HttpContext context) { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); + string? result = context.User.FindFirstValue(ClaimTypes.NameIdentifier); + if (result is null) throw new UnauthorizedAccessException("User claim was not found."); + return result; } } \ No newline at end of file From c8e742f507fd1a195f1fe725e18749b2a40a537d Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Wed, 6 Sep 2023 12:35:47 -0400 Subject: [PATCH 03/53] Razor projects updated with Nullable context. Partially fixes issue #105. --- .../BlazingPizza.Client/BlazingPizza.Client.csproj | 1 + .../BlazingPizza.ComponentsLibrary.csproj | 1 + .../BlazingPizza.ComponentsLibrary/Map/Map.razor | 4 ++-- .../BlazingPizza.ComponentsLibrary/Map/Marker.cs | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/save-points/00-get-started/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/00-get-started/BlazingPizza.Client/BlazingPizza.Client.csproj index 08e409ff..cb4c8f20 100644 --- a/save-points/00-get-started/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/00-get-started/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/00-get-started/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/00-get-started/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index b50155b9..a907e880 100644 --- a/save-points/00-get-started/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/00-get-started/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Marker.cs index cae70a4e..d4dcdbad 100644 --- a/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/00-get-started/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -2,7 +2,7 @@ public class Marker { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; public double X { get; set; } From b3207411e07306c6dff5867150f3ac0cd836c6c9 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Tue, 12 Sep 2023 12:11:14 -0400 Subject: [PATCH 04/53] 01 updated with Nullable context, including instructions (md). Partially fixes issue #105. --- docs/01-components-and-layout.md | 6 ++--- .../BlazingPizza.Client.csproj | 27 ++++++++++--------- .../BlazingPizza.Client/Pages/Index.razor | 4 +-- .../BlazingPizza.ComponentsLibrary.csproj | 3 ++- .../Map/Map.razor | 4 +-- .../Map/Marker.cs | 3 ++- .../Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server.csproj | 1 + .../BlazingPizza.Server/OrdersController.cs | 11 +++----- .../BlazingPizza.Server/PizzaApiExtensions.cs | 6 +++-- .../BlazingPizza.Server/PizzaStoreContext.cs | 2 +- .../BlazingPizza.Server/Program.cs | 3 +-- .../BlazingPizza.Shared/Address.cs | 12 ++++----- .../BlazingPizza.Shared.csproj | 17 ++++++------ .../NotificationSubscription.cs | 10 +++---- .../BlazingPizza.Shared/Order.cs | 6 +++-- .../BlazingPizza.Shared/OrderWithStatus.cs | 10 ++++--- .../BlazingPizza.Shared/Pizza.cs | 8 +++--- .../BlazingPizza.Shared/PizzaSpecial.cs | 6 ++--- .../BlazingPizza.Shared/PizzaTopping.cs | 2 +- .../BlazingPizza.Shared/Topping.cs | 2 +- .../BlazingPizza.Shared/UserInfo.cs | 2 +- 22 files changed, 78 insertions(+), 69 deletions(-) diff --git a/docs/01-components-and-layout.md b/docs/01-components-and-layout.md index d6f8843a..107e54b4 100644 --- a/docs/01-components-and-layout.md +++ b/docs/01-components-and-layout.md @@ -42,7 +42,7 @@ Add a `@code` block to *Index.razor* with a list field to keep track of the avai ```csharp @code { - List specials; + List? specials; } ``` @@ -61,7 +61,7 @@ Override the `OnInitializedAsync` method in the `@code` block to retrieve the li ```csharp @code { - List specials; + List? specials; protected override async Task OnInitializedAsync() { @@ -81,7 +81,7 @@ Once the component is initialized it will render its markup. Replace the markup ```html
    - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { diff --git a/save-points/01-Components-and-layout/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/01-Components-and-layout/BlazingPizza.Client/BlazingPizza.Client.csproj index f9ebe283..48b9455e 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/01-Components-and-layout/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -1,20 +1,21 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - - - - - + + + + + + - - - - + + + + diff --git a/save-points/01-Components-and-layout/BlazingPizza.Client/Pages/Index.razor b/save-points/01-Components-and-layout/BlazingPizza.Client/Pages/Index.razor index 03d6e160..c9ac3103 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/01-Components-and-layout/BlazingPizza.Client/Pages/Index.razor @@ -3,7 +3,7 @@
      - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { @@ -20,7 +20,7 @@
    @code { - List specials; + List? specials; protected override async Task OnInitializedAsync() { diff --git a/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index cb996b1b..a907e880 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - true + true + enable diff --git a/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Marker.cs index 4761dcd6..d4dcdbad 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -1,7 +1,8 @@ namespace BlazingPizza.ComponentsLibrary.Map; + public class Marker { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; public double X { get; set; } diff --git a/save-points/01-Components-and-layout/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/01-Components-and-layout/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/01-Components-and-layout/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
    - @User.Identity.Name + @User?.Identity?.Name
    diff --git a/save-points/01-Components-and-layout/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/01-Components-and-layout/BlazingPizza.Server/BlazingPizza.Server.csproj index 227890e1..a1043cb6 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/01-Components-and-layout/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs b/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs index 37e37cc5..a0a99b09 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs @@ -61,12 +61,12 @@ public async Task> PlaceOrder(Order order) // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -75,7 +75,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -84,11 +84,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track diff --git a/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..e82b077b 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaApiExtensions.cs @@ -49,9 +49,11 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) + public static string GetUserId(HttpContext context) { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); + string? result = context.User.FindFirstValue(ClaimTypes.NameIdentifier); + if (result is null) throw new UnauthorizedAccessException("User claim was not found."); + return result; } } \ No newline at end of file diff --git a/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaStoreContext.cs b/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaStoreContext.cs index 50ac87e4..1505c7af 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaStoreContext.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Server/PizzaStoreContext.cs @@ -35,4 +35,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) // Inline the Lat-Long pairs in Order rather than having a FK to another table modelBuilder.Entity().OwnsOne(o => o.DeliveryLocation); } -} +} \ No newline at end of file diff --git a/save-points/01-Components-and-layout/BlazingPizza.Server/Program.cs b/save-points/01-Components-and-layout/BlazingPizza.Server/Program.cs index b83debb4..e185db50 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Server/Program.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Server/Program.cs @@ -11,8 +11,7 @@ builder.Services.AddRazorPages(); builder.Services.AddDbContext(options => - options.UseSqlite("Data Source=pizza.db") - .UseModel(BlazingPizza.Server.Models.PizzaStoreContextModel.Instance)); + options.UseSqlite("Data Source=pizza.db")); builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores(); diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/Address.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/Address.cs index 528622e1..d1a53375 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/Address.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/Address.cs @@ -4,15 +4,15 @@ public class Address { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; - public string City { get; set; } + public string City { get; set; } = string.Empty; - public string Region { get; set; } + public string Region { get; set; } = string.Empty; - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/01-Components-and-layout/BlazingPizza.Shared/BlazingPizza.Shared.csproj index fb17e53b..785c204e 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,17 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - - + + + + diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs index b47efad0..cba9a17d 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/Pizza.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/Pizza.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaTopping.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/Topping.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/Topping.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/UserInfo.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From 560bd5c6b9c33f3b22108348e09f24b6a7e27c7f Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 12:45:40 -0400 Subject: [PATCH 05/53] Updated 02. Dependencies to use Nullable --- .../BlazingPizza.ComponentsLibrary.csproj | 3 ++- .../LocalStorage.cs | 2 -- .../Map/Map.razor | 4 ++-- .../Map/Marker.cs | 17 ++++++++--------- .../Map/Point.cs | 12 ++++++------ .../Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server.csproj | 1 + .../BlazingPizza.Server/OrdersController.cs | 11 +++-------- .../BlazingPizza.Server/PizzaApiExtensions.cs | 6 ++++-- .../BlazingPizza.Server/PizzaStoreContext.cs | 2 +- .../BlazingPizza.Server/Program.cs | 3 +-- .../BlazingPizza.Shared/Address.cs | 16 +++++++--------- .../BlazingPizza.Shared.csproj | 19 ++++++++++++------- .../NotificationSubscription.cs | 10 +++++----- .../BlazingPizza.Shared/Order.cs | 7 ++++--- .../BlazingPizza.Shared/OrderWithStatus.cs | 10 +++++++--- .../BlazingPizza.Shared/Pizza.cs | 8 +++++--- .../BlazingPizza.Shared/PizzaSpecial.cs | 6 +++--- .../BlazingPizza.Shared/PizzaTopping.cs | 2 +- .../BlazingPizza.Shared/Topping.cs | 2 +- .../BlazingPizza.Shared/UserInfo.cs | 2 +- 21 files changed, 75 insertions(+), 70 deletions(-) diff --git a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index cb996b1b..a907e880 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - true + true + enable diff --git a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/LocalStorage.cs b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/LocalStorage.cs index 8995307f..be8226ec 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/LocalStorage.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/LocalStorage.cs @@ -4,7 +4,6 @@ namespace BlazingPizza.ComponentsLibrary; public static class LocalStorage { - public static ValueTask GetAsync(IJSRuntime jsRuntime, string key) => jsRuntime.InvokeAsync("blazorLocalStorage.get", key); @@ -13,5 +12,4 @@ public static ValueTask SetAsync(IJSRuntime jsRuntime, string key, object value) public static ValueTask DeleteAsync(IJSRuntime jsRuntime, string key) => jsRuntime.InvokeVoidAsync("blazorLocalStorage.delete", key); - } \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Marker.cs index 63006067..d4dcdbad 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -1,13 +1,12 @@ -namespace BlazingPizza.ComponentsLibrary.Map +namespace BlazingPizza.ComponentsLibrary.Map; + +public class Marker { - public class Marker - { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public double X { get; set; } + public double X { get; set; } - public double Y { get; set; } + public double Y { get; set; } - public bool ShowPopup { get; set; } - } -} + public bool ShowPopup { get; set; } +} \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Point.cs b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Point.cs index 03841826..054e56ec 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Point.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.ComponentsLibrary/Map/Point.cs @@ -1,9 +1,9 @@ -namespace BlazingPizza.ComponentsLibrary.Map +namespace BlazingPizza.ComponentsLibrary.Map; + +public class Point { - public class Point - { - public double X { get; set; } + public double X { get; set; } - public double Y { get; set; } - } + public double Y { get; set; } } +// Compare this snippet from save-points\01-Components-and-layout\BlazingPizza.ComponentsLibrary\Map\Marker.cs: \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/02-customize-a-pizza/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/02-customize-a-pizza/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
    - @User.Identity.Name + @User?.Identity?.Name
    diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/02-customize-a-pizza/BlazingPizza.Server/BlazingPizza.Server.csproj index 227890e1..a1043cb6 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/02-customize-a-pizza/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs b/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs index 37e37cc5..a0a99b09 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs @@ -61,12 +61,12 @@ public async Task> PlaceOrder(Order order) // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -75,7 +75,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -84,11 +84,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..e82b077b 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaApiExtensions.cs @@ -49,9 +49,11 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) + public static string GetUserId(HttpContext context) { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); + string? result = context.User.FindFirstValue(ClaimTypes.NameIdentifier); + if (result is null) throw new UnauthorizedAccessException("User claim was not found."); + return result; } } \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaStoreContext.cs b/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaStoreContext.cs index 50ac87e4..1505c7af 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaStoreContext.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Server/PizzaStoreContext.cs @@ -35,4 +35,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) // Inline the Lat-Long pairs in Order rather than having a FK to another table modelBuilder.Entity().OwnsOne(o => o.DeliveryLocation); } -} +} \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Server/Program.cs b/save-points/02-customize-a-pizza/BlazingPizza.Server/Program.cs index b83debb4..e185db50 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Server/Program.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Server/Program.cs @@ -11,8 +11,7 @@ builder.Services.AddRazorPages(); builder.Services.AddDbContext(options => - options.UseSqlite("Data Source=pizza.db") - .UseModel(BlazingPizza.Server.Models.PizzaStoreContextModel.Instance)); + options.UseSqlite("Data Source=pizza.db")); builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores(); diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Address.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Address.cs index f2d1daa4..d1a53375 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Address.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Address.cs @@ -1,20 +1,18 @@ -using System.ComponentModel.DataAnnotations; - -namespace BlazingPizza; +namespace BlazingPizza; public class Address { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; - public string City { get; set; } + public string City { get; set; } = string.Empty; - public string Region { get; set; } + public string Region { get; set; } = string.Empty; - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/02-customize-a-pizza/BlazingPizza.Shared/BlazingPizza.Shared.csproj index 075ddd60..785c204e 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,13 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - + + + + + + + diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs index 180ec55b..cba9a17d 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); @@ -29,5 +31,4 @@ public class Order [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Topping))] -[JsonSerializable(typeof(Dictionary))] public partial class OrderContext : JsonSerializerContext { } \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Pizza.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Pizza.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaTopping.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Topping.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Topping.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/UserInfo.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From 1e558acf143daedd263f24f43c940e2d9277a56b Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 12:51:36 -0400 Subject: [PATCH 06/53] Updated code to include nullable considerations. --- .../BlazingPizza.Client.csproj | 1 + .../BlazingPizza.Client/Pages/Index.razor | 29 ++++++++------- .../Shared/ConfigurePizzaDialog.razor | 36 +++++++++++-------- .../Shared/ConfiguredPizzaItem.razor | 11 +++--- 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/02-customize-a-pizza/BlazingPizza.Client/BlazingPizza.Client.csproj index 8f007b31..1e68d10a 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/02-customize-a-pizza/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Client/Pages/Index.razor b/save-points/02-customize-a-pizza/BlazingPizza.Client/Pages/Index.razor index 808b7307..e5aeb14e 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/02-customize-a-pizza/BlazingPizza.Client/Pages/Index.razor @@ -3,7 +3,7 @@
      - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { @@ -49,13 +49,13 @@ { + OnCancel="CancelConfigurePizzaDialog" + OnConfirm="ConfirmConfigurePizzaDialog" /> } @code { - List specials; - Pizza configuringPizza; + List? specials; + Pizza? configuringPizza; bool showingConfigureDialog; Order order = new Order(); @@ -67,12 +67,12 @@ void ShowConfigurePizzaDialog(PizzaSpecial special) { configuringPizza = new Pizza() - { - Special = special, - SpecialId = special.Id, - Size = Pizza.DefaultSize, - Toppings = new List(), - }; + { + Special = special, + SpecialId = special.Id, + Size = Pizza.DefaultSize, + Toppings = new List(), + }; showingConfigureDialog = true; } @@ -85,8 +85,11 @@ void ConfirmConfigurePizzaDialog() { - order.Pizzas.Add(configuringPizza); - configuringPizza = null; + if (configuringPizza is not null) + { + order.Pizzas.Add(configuringPizza); + configuringPizza = null; + } showingConfigureDialog = false; } diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 56549454..d1788226 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -3,8 +3,8 @@
      -

      @Pizza.Special.Name

      - @Pizza.Special.Description +

      @Pizza.Special?.Name

      + @Pizza.Special?.Description
      @@ -16,7 +16,7 @@
      - @if (toppings == null) + @if (toppings is null) { @@ -221,11 +224,14 @@ Add the following markup in the dialog body for displaying a drop down list with
      @foreach (var topping in Pizza.Toppings) { -
      - @topping.Topping.Name - @topping.Topping.GetFormattedPrice() - -
      + if (topping?.Topping is not null) + { +
      + @topping.Topping.Name + @topping.Topping.GetFormattedPrice() + +
      + } }
      ``` @@ -235,7 +241,7 @@ Also add the following event handlers for topping selection and removal: ```csharp void ToppingSelected(ChangeEventArgs e) { - if (int.TryParse((string)e.Value, out var index) && index >= 0) + if (int.TryParse((string?)e.Value, out var index) && index >= 0) { AddTopping(toppings[index]); } @@ -243,7 +249,7 @@ void ToppingSelected(ChangeEventArgs e) void AddTopping(Topping topping) { - if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null) + if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null) { Pizza.Toppings.Add(new PizzaTopping() { Topping = topping }); } @@ -267,8 +273,8 @@ The Cancel and Order buttons don't do anything yet. We need some way to communic Add two parameters to the `ConfigurePizzaDialog` component: `OnCancel` and `OnConfirm`. Both parameters should be of type `EventCallback`. ```csharp -[Parameter] public EventCallback OnCancel { get; set; } -[Parameter] public EventCallback OnConfirm { get; set; } +[Parameter, EditorRequired] public EventCallback OnCancel { get; set; } +[Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } ``` Add `@onclick` event handlers to the `ConfigurePizzaDialog` that trigger the `OnCancel` and `OnConfirm` events. @@ -308,8 +314,8 @@ Run the app and verify that the dialog now disappears when the Cancel button is When the `OnConfirm` event is fired, the customized pizza should be added to the user's order. Add an `Order` field to the `Index` component to track the user's order. ```csharp -List specials; -Pizza configuringPizza; +List? specials; +Pizza? configuringPizza; bool showingConfigureDialog; Order order = new Order(); ``` @@ -344,11 +350,11 @@ Create a new `ConfiguredPizzaItem` component for displaying a configured pizza. ```html
      x -
      @(Pizza.Size)" @Pizza.Special.Name
      +
      @(Pizza.Size)" @Pizza.Special?.Name
        @foreach (var topping in Pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      @@ -357,8 +363,9 @@ Create a new `ConfiguredPizzaItem` component for displaying a configured pizza.
      @code { - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnRemoved { get; set; } + // Set by Order.Pizzas which is never null + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = null!; + [Parameter, EditorRequired] public EventCallback OnRemoved { get; set; } } ``` From c240c05201a8d62d9a316cbda87791be673c8134 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 12:58:17 -0400 Subject: [PATCH 08/53] Updated dependencies to use nullable context --- .../BlazingPizza.ComponentsLibrary.csproj | 3 +- .../Map/Map.razor | 4 +- .../Map/Marker.cs | 4 +- .../Map/Point.cs | 3 +- .../Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server.csproj | 1 + .../BlazingPizza.Server/OrdersController.cs | 11 +-- .../BlazingPizza.Server/PizzaApiExtensions.cs | 6 +- .../BlazingPizza.Server/PizzaStoreContext.cs | 2 +- .../BlazingPizza.Server/Startup.cs | 69 +++++++++++++++++++ .../BlazingPizza.Shared/Address.cs | 16 ++--- .../BlazingPizza.Shared.csproj | 19 +++-- .../NotificationSubscription.cs | 10 +-- .../BlazingPizza.Shared/Order.cs | 6 +- .../BlazingPizza.Shared/OrderWithStatus.cs | 10 ++- .../BlazingPizza.Shared/Pizza.cs | 8 ++- .../BlazingPizza.Shared/PizzaSpecial.cs | 6 +- .../BlazingPizza.Shared/PizzaTopping.cs | 2 +- .../BlazingPizza.Shared/Topping.cs | 2 +- .../BlazingPizza.Shared/UserInfo.cs | 2 +- 20 files changed, 133 insertions(+), 53 deletions(-) create mode 100644 save-points/03-show-order-status/BlazingPizza.Server/Startup.cs diff --git a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index cb996b1b..a907e880 100644 --- a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - true + true + enable diff --git a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Marker.cs index b067719c..d4dcdbad 100644 --- a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -2,11 +2,11 @@ public class Marker { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; public double X { get; set; } public double Y { get; set; } public bool ShowPopup { get; set; } -} +} \ No newline at end of file diff --git a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Point.cs b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Point.cs index 34194163..054e56ec 100644 --- a/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Point.cs +++ b/save-points/03-show-order-status/BlazingPizza.ComponentsLibrary/Map/Point.cs @@ -5,4 +5,5 @@ public class Point public double X { get; set; } public double Y { get; set; } -} \ No newline at end of file +} +// Compare this snippet from save-points\01-Components-and-layout\BlazingPizza.ComponentsLibrary\Map\Marker.cs: \ No newline at end of file diff --git a/save-points/03-show-order-status/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/03-show-order-status/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/03-show-order-status/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/03-show-order-status/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
      - @User.Identity.Name + @User?.Identity?.Name diff --git a/save-points/03-show-order-status/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/03-show-order-status/BlazingPizza.Server/BlazingPizza.Server.csproj index 227890e1..a1043cb6 100644 --- a/save-points/03-show-order-status/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/03-show-order-status/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs b/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs index 37e37cc5..a0a99b09 100644 --- a/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs +++ b/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs @@ -61,12 +61,12 @@ public async Task> PlaceOrder(Order order) // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -75,7 +75,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -84,11 +84,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track diff --git a/save-points/03-show-order-status/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/03-show-order-status/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..e82b077b 100644 --- a/save-points/03-show-order-status/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/03-show-order-status/BlazingPizza.Server/PizzaApiExtensions.cs @@ -49,9 +49,11 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) + public static string GetUserId(HttpContext context) { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); + string? result = context.User.FindFirstValue(ClaimTypes.NameIdentifier); + if (result is null) throw new UnauthorizedAccessException("User claim was not found."); + return result; } } \ No newline at end of file diff --git a/save-points/03-show-order-status/BlazingPizza.Server/PizzaStoreContext.cs b/save-points/03-show-order-status/BlazingPizza.Server/PizzaStoreContext.cs index 50ac87e4..1505c7af 100644 --- a/save-points/03-show-order-status/BlazingPizza.Server/PizzaStoreContext.cs +++ b/save-points/03-show-order-status/BlazingPizza.Server/PizzaStoreContext.cs @@ -35,4 +35,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) // Inline the Lat-Long pairs in Order rather than having a FK to another table modelBuilder.Entity().OwnsOne(o => o.DeliveryLocation); } -} +} \ No newline at end of file diff --git a/save-points/03-show-order-status/BlazingPizza.Server/Startup.cs b/save-points/03-show-order-status/BlazingPizza.Server/Startup.cs new file mode 100644 index 00000000..4a35b695 --- /dev/null +++ b/save-points/03-show-order-status/BlazingPizza.Server/Startup.cs @@ -0,0 +1,69 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace BlazingPizza.Server +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddControllersWithViews(); + services.AddRazorPages(); + + services.AddDbContext(options => + options.UseSqlite("Data Source=pizza.db")); + + services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) + .AddEntityFrameworkStores(); + + services.AddIdentityServer() + .AddApiAuthorization(); + + services.AddAuthentication() + .AddIdentityServerJwt(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseWebAssemblyDebugging(); + } + else + { + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + + app.UseHttpsRedirection(); + app.UseBlazorFrameworkFiles(); + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseAuthentication(); + app.UseIdentityServer(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { + endpoints.MapControllers(); + endpoints.MapRazorPages(); + endpoints.MapFallbackToFile("index.html"); + }); + } + } +} \ No newline at end of file diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/Address.cs b/save-points/03-show-order-status/BlazingPizza.Shared/Address.cs index f2d1daa4..d1a53375 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/Address.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/Address.cs @@ -1,20 +1,18 @@ -using System.ComponentModel.DataAnnotations; - -namespace BlazingPizza; +namespace BlazingPizza; public class Address { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; - public string City { get; set; } + public string City { get; set; } = string.Empty; - public string Region { get; set; } + public string Region { get; set; } = string.Empty; - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/03-show-order-status/BlazingPizza.Shared/BlazingPizza.Shared.csproj index 075ddd60..785c204e 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/03-show-order-status/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,13 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - + + + + + + + diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/Order.cs b/save-points/03-show-order-status/BlazingPizza.Shared/Order.cs index 180ec55b..f1211588 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/Order.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/03-show-order-status/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/Pizza.cs b/save-points/03-show-order-status/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/Pizza.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/03-show-order-status/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/PizzaTopping.cs b/save-points/03-show-order-status/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/Topping.cs b/save-points/03-show-order-status/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/Topping.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/UserInfo.cs b/save-points/03-show-order-status/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From 1e5e283fc7913f5086cd796faceb229aea0be476 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 13:00:59 -0400 Subject: [PATCH 09/53] Fixes issue #358 and Fixes issue #238 --- save-points/00-get-started/BlazingPizza.Shared/Order.cs | 1 + .../01-Components-and-layout/BlazingPizza.Shared/Order.cs | 1 + save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/save-points/00-get-started/BlazingPizza.Shared/Order.cs b/save-points/00-get-started/BlazingPizza.Shared/Order.cs index cba9a17d..f1211588 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/Order.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/Order.cs @@ -31,4 +31,5 @@ public class Order [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Topping))] +[JsonSerializable(typeof(Dictionary))] public partial class OrderContext : JsonSerializerContext { } \ No newline at end of file diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs index cba9a17d..f1211588 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/Order.cs @@ -31,4 +31,5 @@ public class Order [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Topping))] +[JsonSerializable(typeof(Dictionary))] public partial class OrderContext : JsonSerializerContext { } \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs index cba9a17d..f1211588 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/Order.cs @@ -31,4 +31,5 @@ public class Order [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Topping))] +[JsonSerializable(typeof(Dictionary))] public partial class OrderContext : JsonSerializerContext { } \ No newline at end of file From ad569aeb2a609838a57b6fed072eab628183006b Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 13:07:49 -0400 Subject: [PATCH 10/53] Sync with previous lesson --- .../BlazingPizza.Client.csproj | 27 +++++++------- .../BlazingPizza.Client/Pages/Index.razor | 11 +++--- .../Shared/ConfigurePizzaDialog.razor | 36 +++++++++++-------- .../Shared/ConfiguredPizzaItem.razor | 8 ++--- 4 files changed, 46 insertions(+), 36 deletions(-) diff --git a/save-points/03-show-order-status/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/03-show-order-status/BlazingPizza.Client/BlazingPizza.Client.csproj index f9ebe283..48b9455e 100644 --- a/save-points/03-show-order-status/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/03-show-order-status/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -1,20 +1,21 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - - - - - + + + + + + - - - - + + + + diff --git a/save-points/03-show-order-status/BlazingPizza.Client/Pages/Index.razor b/save-points/03-show-order-status/BlazingPizza.Client/Pages/Index.razor index 1914e003..3d2588fc 100644 --- a/save-points/03-show-order-status/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/03-show-order-status/BlazingPizza.Client/Pages/Index.razor @@ -55,8 +55,8 @@ } @code { - List specials; - Pizza configuringPizza; + List? specials; + Pizza? configuringPizza; bool showingConfigureDialog; Order order = new Order(); @@ -86,8 +86,11 @@ void ConfirmConfigurePizzaDialog() { - order.Pizzas.Add(configuringPizza); - configuringPizza = null; + if (configuringPizza is not null) + { + order.Pizzas.Add(configuringPizza); + configuringPizza = null; + } showingConfigureDialog = false; } diff --git a/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 56549454..fa52ca09 100644 --- a/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -3,8 +3,8 @@
      -

      @Pizza.Special.Name

      - @Pizza.Special.Description +

      @Pizza.Special?.Name

      + @Pizza.Special?.Description
      @@ -16,7 +16,7 @@
      - @if (toppings == null) + @if (toppings is null) { @@ -41,11 +41,14 @@
      @foreach (var topping in Pizza.Toppings) { -
      - @topping.Topping.Name - @topping.Topping.GetFormattedPrice() - -
      + if (topping?.Topping is not null) + { +
      + @topping.Topping.Name + @topping.Topping.GetFormattedPrice() + +
      + } }
      @@ -61,20 +64,23 @@
      @code { - List toppings; + // toppings is only null while loading + List? toppings; - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnCancel { get; set; } - [Parameter] public EventCallback OnConfirm { get; set; } + // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } + [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } protected async override Task OnInitializedAsync() { - toppings = await HttpClient.GetFromJsonAsync>("toppings"); + toppings = await HttpClient.GetFromJsonAsync>("toppings") ?? new(); } void ToppingSelected(ChangeEventArgs e) { - if (int.TryParse((string)e.Value, out var index) && index >= 0) + if (toppings is null) return; + if (int.TryParse((string?)e.Value, out var index) && index >= 0) { AddTopping(toppings[index]); } @@ -82,7 +88,7 @@ void AddTopping(Topping topping) { - if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null) + if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null) { Pizza.Toppings.Add(new PizzaTopping() { Topping = topping }); } diff --git a/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor b/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor index e0e9f41d..f7f7a31d 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor +++ b/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor @@ -1,10 +1,10 @@ 
      x -
      @(Pizza.Size)" @Pizza.Special.Name
      +
      @(Pizza.Size)" @Pizza.Special?.Name
        @foreach (var topping in Pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      @@ -13,6 +13,6 @@
      @code { - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnRemoved { get; set; } + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnRemoved { get; set; } } \ No newline at end of file diff --git a/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/OrderReview.razor b/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/OrderReview.razor index e1424934..56668721 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/OrderReview.razor +++ b/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/OrderReview.razor @@ -3,7 +3,7 @@

      @(pizza.Size)" - @pizza.Special.Name + @pizza.Special?.Name (£@pizza.GetFormattedTotalPrice())

      @@ -11,7 +11,7 @@
        @foreach (var topping in pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      } @@ -24,5 +24,5 @@

      @code { - [Parameter] public Order Order { get; set; } + [Parameter, EditorRequired] public Order Order { get; set; } = new(); } From a13e07ad0e7016e3057b2ca931a6a0ca5becddec Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 16:17:02 -0400 Subject: [PATCH 19/53] Updated client with nullable context enabled --- .../BlazingPizza.Client/OrderState.cs | 9 ++++++--- .../BlazingPizza.Client/Pages/Index.razor | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/save-points/04-refactor-state-management/BlazingPizza.Client/OrderState.cs b/save-points/04-refactor-state-management/BlazingPizza.Client/OrderState.cs index ae82f76c..daa0f4db 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Client/OrderState.cs +++ b/save-points/04-refactor-state-management/BlazingPizza.Client/OrderState.cs @@ -4,7 +4,7 @@ public class OrderState { public bool ShowingConfigureDialog { get; private set; } - public Pizza ConfiguringPizza { get; private set; } + public Pizza? ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); @@ -29,8 +29,11 @@ public void CancelConfigurePizzaDialog() public void ConfirmConfigurePizzaDialog() { - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; + if (ConfiguringPizza is not null) + { + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; + } ShowingConfigureDialog = false; } diff --git a/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor b/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor index 69bcedd9..fead86ef 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor @@ -56,7 +56,7 @@ } @code { - List specials; + List? specials; Order Order => OrderState.Order; protected override async Task OnInitializedAsync() From f9ade8f3ef805ae6e1729d0bcfe492989e847de3 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 16:22:44 -0400 Subject: [PATCH 20/53] Sync dependencies 05 with previous changes --- .../BlazingPizza.ComponentsLibrary.csproj | 3 ++- .../Map/Map.razor | 4 ++-- .../Map/Marker.cs | 4 ++-- .../BlazingPizza.ComponentsLibrary/Map/Point.cs | 3 ++- .../Identity/Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server.csproj | 2 +- .../BlazingPizza.Shared.csproj | 17 +++++++++-------- .../NotificationSubscription.cs | 10 +++++----- .../BlazingPizza.Shared/OrderWithStatus.cs | 10 +++++++--- .../BlazingPizza.Shared/Pizza.cs | 8 +++++--- .../BlazingPizza.Shared/PizzaSpecial.cs | 6 +++--- .../BlazingPizza.Shared/PizzaTopping.cs | 2 +- .../BlazingPizza.Shared/Topping.cs | 2 +- .../BlazingPizza.Shared/UserInfo.cs | 2 +- 14 files changed, 42 insertions(+), 33 deletions(-) diff --git a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index cb996b1b..a907e880 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - true + true + enable diff --git a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Marker.cs index b067719c..d4dcdbad 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -2,11 +2,11 @@ public class Marker { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; public double X { get; set; } public double Y { get; set; } public bool ShowPopup { get; set; } -} +} \ No newline at end of file diff --git a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Point.cs b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Point.cs index 34194163..054e56ec 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Point.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.ComponentsLibrary/Map/Point.cs @@ -5,4 +5,5 @@ public class Point public double X { get; set; } public double Y { get; set; } -} \ No newline at end of file +} +// Compare this snippet from save-points\01-Components-and-layout\BlazingPizza.ComponentsLibrary\Map\Marker.cs: \ No newline at end of file diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/05-checkout-with-validation/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/05-checkout-with-validation/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
      - @User.Identity.Name + @User?.Identity?.Name
      diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/05-checkout-with-validation/BlazingPizza.Server/BlazingPizza.Server.csproj index abd2317b..a1043cb6 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/05-checkout-with-validation/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable @@ -14,7 +15,6 @@ - diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/05-checkout-with-validation/BlazingPizza.Shared/BlazingPizza.Shared.csproj index fb17e53b..785c204e 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,17 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - - + + + + diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/Pizza.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/Pizza.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaTopping.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/Topping.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/Topping.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/UserInfo.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From 6fda4d3817265fb0658974bc5340478b0c880080 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 16:23:15 -0400 Subject: [PATCH 21/53] Fixed step 05 controller is from step 06 --- .../BlazingPizza.Server/OrdersController.cs | 46 ++++--------------- .../BlazingPizza.Server/PizzaApiExtensions.cs | 9 ++-- .../BlazingPizza.Shared/Order.cs | 7 ++- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs b/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs index 087bbfe9..a0a99b09 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs @@ -1,9 +1,6 @@ using System.Security.Claims; -using System.Text.Json; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using WebPush; namespace BlazingPizza.Server; @@ -23,7 +20,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - //.Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == GetUserId()) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -38,7 +35,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - //.Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == GetUserId()) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -57,19 +54,19 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - //order.UserId = GetUserId(); + // order.UserId = GetUserId(); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -78,7 +75,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -87,11 +84,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track @@ -104,27 +96,9 @@ private static async Task TrackAndSendNotificationsAsync(Order order, Notificati await SendNotificationAsync(order, subscription, "Your order is now delivered. Enjoy!"); } - private static async Task SendNotificationAsync(Order order, NotificationSubscription subscription, string message) + private static Task SendNotificationAsync(Order order, NotificationSubscription subscription, string message) { - // For a real application, generate your own - var publicKey = "BLC8GOevpcpjQiLkO7JmVClQjycvTCYWm6Cq_a7wJZlstGTVZvwGFFHMYfXt6Njyvgx_GlXJeo5cSiZ1y4JOx1o"; - var privateKey = "OrubzSz3yWACscZXjFQrrtDwCKg-TGFuWhluQ2wLXDo"; - - var pushSubscription = new PushSubscription(subscription.Url, subscription.P256dh, subscription.Auth); - var vapidDetails = new VapidDetails("mailto:", publicKey, privateKey); - var webPushClient = new WebPushClient(); - try - { - var payload = JsonSerializer.Serialize(new - { - message, - url = $"myorders/{order.OrderId}", - }); - await webPushClient.SendNotificationAsync(pushSubscription, payload, vapidDetails); - } - catch (Exception ex) - { - Console.Error.WriteLine("Error sending push notification: " + ex.Message); - } + // This will be implemented later + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/05-checkout-with-validation/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..d277597f 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Server/PizzaApiExtensions.cs @@ -19,6 +19,10 @@ public static WebApplication MapPizzaApi(this WebApplication app) // We're storing at most one subscription per user, so delete old ones. // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. var userId = GetUserId(context); + if (userId is null) + { + return Results.Unauthorized(); + } var oldSubscriptions = db.NotificationSubscriptions.Where(e => e.UserId == userId); db.NotificationSubscriptions.RemoveRange(oldSubscriptions); @@ -49,9 +53,6 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) - { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); - } + public static string? GetUserId(HttpContext context) => context.User.FindFirstValue(ClaimTypes.NameIdentifier); } \ No newline at end of file diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/Order.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/Order.cs index 17ed2e26..f1211588 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/Order.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); @@ -28,5 +30,6 @@ public class Order [JsonSerializable(typeof(Pizza))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] +[JsonSerializable(typeof(Topping))] [JsonSerializable(typeof(Dictionary))] public partial class OrderContext : JsonSerializerContext { } \ No newline at end of file From acce7accf1d7df162b4322bacbecc436589008c4 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 16:31:59 -0400 Subject: [PATCH 22/53] Sync with client with previous step --- .../BlazingPizza.Client.csproj | 28 +++++++-------- .../BlazingPizza.Client/OrderState.cs | 14 ++++---- .../BlazingPizza.Client/Pages/Index.razor | 6 ++-- .../BlazingPizza.Client/Pages/MyOrders.razor | 4 +-- .../Pages/OrderDetails.razor | 8 ++--- .../Shared/ConfigurePizzaDialog.razor | 36 +++++++++++-------- .../Shared/ConfiguredPizzaItem.razor | 8 ++--- .../Shared/OrderReview.razor | 6 ++-- 8 files changed, 57 insertions(+), 53 deletions(-) diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/05-checkout-with-validation/BlazingPizza.Client/BlazingPizza.Client.csproj index 1847e754..48b9455e 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -1,21 +1,21 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - - - - - - + + + + + + - - - - + + + + diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/OrderState.cs b/save-points/05-checkout-with-validation/BlazingPizza.Client/OrderState.cs index 00dd3627..daa0f4db 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/OrderState.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/OrderState.cs @@ -4,7 +4,7 @@ public class OrderState { public bool ShowingConfigureDialog { get; private set; } - public Pizza ConfiguringPizza { get; private set; } + public Pizza? ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); @@ -29,8 +29,11 @@ public void CancelConfigurePizzaDialog() public void ConfirmConfigurePizzaDialog() { - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; + if (ConfiguringPizza is not null) + { + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; + } ShowingConfigureDialog = false; } @@ -44,9 +47,4 @@ public void ResetOrder() { Order = new Order(); } - - public void ReplaceOrder(Order order) - { - Order = order; - } } diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor index e3dbe2ce..b79fec6e 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor @@ -41,9 +41,9 @@
      Total: @Order.GetFormattedTotalPrice() - +
      @@ -56,7 +56,7 @@ } @code { - List specials; + List? specials; Order Order => OrderState.Order; protected override async Task OnInitializedAsync() diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/MyOrders.razor index 36d3902e..3c5aefdd 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/MyOrders.razor @@ -4,7 +4,7 @@ Blazing Pizza - My Orders
      - @if (ordersWithStatus == null) + @if (ordersWithStatus is null) { Loading... } @@ -41,7 +41,7 @@
      @code { - IEnumerable ordersWithStatus; + IEnumerable? ordersWithStatus; protected override async Task OnParametersSetAsync() { diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/OrderDetails.razor b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/OrderDetails.razor index 25e06141..ced45b7c 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/OrderDetails.razor @@ -11,7 +11,7 @@

      Nope

      Sorry, this order could not be loaded.

      } - else if (orderWithStatus == null) + else if (orderWithStatus is null) { Loading... } @@ -38,9 +38,9 @@ @code { [Parameter] public int OrderId { get; set; } - OrderWithStatus orderWithStatus; + OrderWithStatus? orderWithStatus; bool invalidOrder; - CancellationTokenSource pollingCancellationToken; + CancellationTokenSource? pollingCancellationToken; protected override void OnParametersSet() { @@ -59,7 +59,7 @@ try { invalidOrder = false; - orderWithStatus = await HttpClient.GetFromJsonAsync($"orders/{OrderId}"); + orderWithStatus = await HttpClient.GetFromJsonAsync($"orders/{OrderId}") ?? throw new NullReferenceException(); StateHasChanged(); if (orderWithStatus.IsDelivered) diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 56549454..fa52ca09 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -3,8 +3,8 @@
      -

      @Pizza.Special.Name

      - @Pizza.Special.Description +

      @Pizza.Special?.Name

      + @Pizza.Special?.Description
      @@ -16,7 +16,7 @@
      - @if (toppings == null) + @if (toppings is null) { @@ -41,11 +41,14 @@
      @foreach (var topping in Pizza.Toppings) { -
      - @topping.Topping.Name - @topping.Topping.GetFormattedPrice() - -
      + if (topping?.Topping is not null) + { +
      + @topping.Topping.Name + @topping.Topping.GetFormattedPrice() + +
      + } }
      @@ -61,20 +64,23 @@
      @code { - List toppings; + // toppings is only null while loading + List? toppings; - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnCancel { get; set; } - [Parameter] public EventCallback OnConfirm { get; set; } + // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } + [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } protected async override Task OnInitializedAsync() { - toppings = await HttpClient.GetFromJsonAsync>("toppings"); + toppings = await HttpClient.GetFromJsonAsync>("toppings") ?? new(); } void ToppingSelected(ChangeEventArgs e) { - if (int.TryParse((string)e.Value, out var index) && index >= 0) + if (toppings is null) return; + if (int.TryParse((string?)e.Value, out var index) && index >= 0) { AddTopping(toppings[index]); } @@ -82,7 +88,7 @@ void AddTopping(Topping topping) { - if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null) + if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null) { Pizza.Toppings.Add(new PizzaTopping() { Topping = topping }); } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor index e0e9f41d..f7f7a31d 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor @@ -1,10 +1,10 @@ 
      x -
      @(Pizza.Size)" @Pizza.Special.Name
      +
      @(Pizza.Size)" @Pizza.Special?.Name
        @foreach (var topping in Pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      @@ -13,6 +13,6 @@
      @code { - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnRemoved { get; set; } + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnRemoved { get; set; } } \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/OrderReview.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/OrderReview.razor index e1424934..56668721 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/OrderReview.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/OrderReview.razor @@ -3,7 +3,7 @@

      @(pizza.Size)" - @pizza.Special.Name + @pizza.Special?.Name (£@pizza.GetFormattedTotalPrice())

      @@ -11,7 +11,7 @@
        @foreach (var topping in pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      } @@ -24,5 +24,5 @@

      @code { - [Parameter] public Order Order { get; set; } + [Parameter, EditorRequired] public Order Order { get; set; } = new(); } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index cb996b1b..a907e880 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - true + true + enable diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Marker.cs index b067719c..d4dcdbad 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -2,11 +2,11 @@ public class Marker { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; public double X { get; set; } public double Y { get; set; } public bool ShowPopup { get; set; } -} +} \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Point.cs b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Point.cs index 34194163..054e56ec 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Point.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.ComponentsLibrary/Map/Point.cs @@ -5,4 +5,5 @@ public class Point public double X { get; set; } public double Y { get; set; } -} \ No newline at end of file +} +// Compare this snippet from save-points\01-Components-and-layout\BlazingPizza.ComponentsLibrary\Map\Marker.cs: \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/06-authentication-and-authorization/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
      - @User.Identity.Name + @User?.Identity?.Name
      diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/06-authentication-and-authorization/BlazingPizza.Server/BlazingPizza.Server.csproj index 227890e1..a1043cb6 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -3,6 +3,7 @@ $(TargetFrameworkVersion) true + enable diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OidcConfigurationController.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OidcConfigurationController.cs index 50a0a8ad..1431697e 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OidcConfigurationController.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OidcConfigurationController.cs @@ -1,26 +1,21 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.ApiAuthorization.IdentityServer; +using Microsoft.AspNetCore.ApiAuthorization.IdentityServer; using Microsoft.AspNetCore.Mvc; -namespace BlazingPizza.Server +namespace BlazingPizza.Server; + +public class OidcConfigurationController : Controller { - public class OidcConfigurationController : Controller + public OidcConfigurationController(IClientRequestParametersProvider clientRequestParametersProvider) { - public OidcConfigurationController(IClientRequestParametersProvider clientRequestParametersProvider) - { - ClientRequestParametersProvider = clientRequestParametersProvider; - } + ClientRequestParametersProvider = clientRequestParametersProvider; + } - public IClientRequestParametersProvider ClientRequestParametersProvider { get; } + public IClientRequestParametersProvider ClientRequestParametersProvider { get; } - [HttpGet("_configuration/{clientId}")] - public IActionResult GetClientRequestParameters([FromRoute] string clientId) - { - var parameters = ClientRequestParametersProvider.GetClientParameters(HttpContext, clientId); - return Ok(parameters); - } + [HttpGet("_configuration/{clientId}")] + public IActionResult GetClientRequestParameters([FromRoute] string clientId) + { + var parameters = ClientRequestParametersProvider.GetClientParameters(HttpContext, clientId); + return Ok(parameters); } } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs index 72b59890..8d61a843 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs @@ -7,24 +7,24 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace BlazingPizza.Server +namespace BlazingPizza.Server; + +[Route("orders")] +[ApiController] +[Authorize] +public class OrdersController : Controller { - [Route("orders")] - [ApiController] - [Authorize] - public class OrdersController : Controller - { - private readonly PizzaStoreContext _db; + private readonly PizzaStoreContext _db; - public OrdersController(PizzaStoreContext db) - { - _db = db; - } + public OrdersController(PizzaStoreContext db) + { + _db = db; + } - [HttpGet] - public async Task>> GetOrders() - { - var orders = await _db.Orders + [HttpGet] + public async Task>> GetOrders() + { + var orders = await _db.Orders .Where(o => o.UserId == GetUserId()) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) @@ -32,13 +32,13 @@ public async Task>> GetOrders() .OrderByDescending(o => o.CreatedTime) .ToListAsync(); - return orders.Select(o => OrderWithStatus.FromOrder(o)).ToList(); - } + return orders.Select(o => OrderWithStatus.FromOrder(o)).ToList(); + } - [HttpGet("{orderId}")] - public async Task> GetOrderWithStatus(int orderId) - { - var order = await _db.Orders + [HttpGet("{orderId}")] + public async Task> GetOrderWithStatus(int orderId) + { + var order = await _db.Orders .Where(o => o.OrderId == orderId) .Where(o => o.UserId == GetUserId()) .Include(o => o.DeliveryLocation) @@ -46,70 +46,64 @@ public async Task> GetOrderWithStatus(int orderId) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) .SingleOrDefaultAsync(); - if (order == null) - { - return NotFound(); - } - - return OrderWithStatus.FromOrder(order); + if (order == null) + { + return NotFound(); } - [HttpPost] - public async Task> PlaceOrder(Order order) - { - order.CreatedTime = DateTime.Now; - order.DeliveryLocation = new LatLong(51.5001, -0.1239); - order.UserId = GetUserId(); + return OrderWithStatus.FromOrder(order); + } - // Enforce existence of Pizza.SpecialId and Topping.ToppingId - // in the database - prevent the submitter from making up - // new specials and toppings - foreach (var pizza in order.Pizzas) - { - pizza.SpecialId = pizza.Special.Id; - pizza.Special = null; - - foreach (var topping in pizza.Toppings) - { - topping.ToppingId = topping.Topping.Id; - topping.Topping = null; - } - } + [HttpPost] + public async Task> PlaceOrder(Order order) + { + order.CreatedTime = DateTime.Now; + order.DeliveryLocation = new LatLong(51.5001, -0.1239); + order.UserId = GetUserId(); - _db.Orders.Attach(order); - await _db.SaveChangesAsync(); + // Enforce existence of Pizza.SpecialId and Topping.ToppingId + // in the database - prevent the submitter from making up + // new specials and toppings + foreach (var pizza in order.Pizzas) + { + pizza.SpecialId = pizza.Special?.Id ?? 0; + pizza.Special = null; - // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); - if (subscription != null) + foreach (var topping in pizza.Toppings) { - _ = TrackAndSendNotificationsAsync(order, subscription); + topping.ToppingId = topping.Topping?.Id ?? 0; + topping.Topping = null; } - - return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } + _db.Orders.Attach(order); + await _db.SaveChangesAsync(); - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) + // In the background, send push notifications if possible + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); + if (subscription != null) { - // In a realistic case, some other backend process would track - // order delivery progress and send us notifications when it - // changes. Since we don't have any such process here, fake it. - await Task.Delay(OrderWithStatus.PreparationDuration); - await SendNotificationAsync(order, subscription, "Your order has been dispatched!"); - - await Task.Delay(OrderWithStatus.DeliveryDuration); - await SendNotificationAsync(order, subscription, "Your order is now delivered. Enjoy!"); + _ = TrackAndSendNotificationsAsync(order, subscription); } - private static Task SendNotificationAsync(Order order, NotificationSubscription subscription, string message) - { - // This will be implemented later - return Task.CompletedTask; - } + return order.OrderId; + } + + private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) + { + // In a realistic case, some other backend process would track + // order delivery progress and send us notifications when it + // changes. Since we don't have any such process here, fake it. + await Task.Delay(OrderWithStatus.PreparationDuration); + await SendNotificationAsync(order, subscription, "Your order has been dispatched!"); + + await Task.Delay(OrderWithStatus.DeliveryDuration); + await SendNotificationAsync(order, subscription, "Your order is now delivered. Enjoy!"); + } + + private static Task SendNotificationAsync(Order order, NotificationSubscription subscription, string message) + { + // This will be implemented later + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..d277597f 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaApiExtensions.cs @@ -19,6 +19,10 @@ public static WebApplication MapPizzaApi(this WebApplication app) // We're storing at most one subscription per user, so delete old ones. // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. var userId = GetUserId(context); + if (userId is null) + { + return Results.Unauthorized(); + } var oldSubscriptions = db.NotificationSubscriptions.Where(e => e.UserId == userId); db.NotificationSubscriptions.RemoveRange(oldSubscriptions); @@ -49,9 +53,6 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) - { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); - } + public static string? GetUserId(HttpContext context) => context.User.FindFirstValue(ClaimTypes.NameIdentifier); } \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreContext.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreContext.cs index 4830d1a4..50ac87e4 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreContext.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreContext.cs @@ -3,37 +3,36 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -namespace BlazingPizza.Server +namespace BlazingPizza.Server; + +public class PizzaStoreContext : ApiAuthorizationDbContext { - public class PizzaStoreContext : ApiAuthorizationDbContext - { - public PizzaStoreContext( + public PizzaStoreContext( DbContextOptions options, IOptions operationalStoreOptions) : base(options, operationalStoreOptions) - { - } + { + } - public DbSet Orders { get; set; } + public DbSet Orders { get; set; } - public DbSet Pizzas { get; set; } + public DbSet Pizzas { get; set; } - public DbSet Specials { get; set; } + public DbSet Specials { get; set; } - public DbSet Toppings { get; set; } + public DbSet Toppings { get; set; } - public DbSet NotificationSubscriptions { get; set; } + public DbSet NotificationSubscriptions { get; set; } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); - // Configuring a many-to-many special -> topping relationship that is friendly for serialization - modelBuilder.Entity().HasKey(pst => new { pst.PizzaId, pst.ToppingId }); - modelBuilder.Entity().HasOne().WithMany(ps => ps.Toppings); - modelBuilder.Entity().HasOne(pst => pst.Topping).WithMany(); + // Configuring a many-to-many special -> topping relationship that is friendly for serialization + modelBuilder.Entity().HasKey(pst => new { pst.PizzaId, pst.ToppingId }); + modelBuilder.Entity().HasOne().WithMany(ps => ps.Toppings); + modelBuilder.Entity().HasOne(pst => pst.Topping).WithMany(); - // Inline the Lat-Long pairs in Order rather than having a FK to another table - modelBuilder.Entity().OwnsOne(o => o.DeliveryLocation); - } + // Inline the Lat-Long pairs in Order rather than having a FK to another table + modelBuilder.Entity().OwnsOne(o => o.DeliveryLocation); } -} \ No newline at end of file +} diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreUser.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreUser.cs index 171ce83d..7ab40235 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreUser.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/PizzaStoreUser.cs @@ -1,8 +1,5 @@ using Microsoft.AspNetCore.Identity; -namespace BlazingPizza.Server -{ - public class PizzaStoreUser : IdentityUser - { - } -} \ No newline at end of file +namespace BlazingPizza.Server; + +public class PizzaStoreUser : IdentityUser { } \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Address.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Address.cs index a671359a..15e546c1 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Address.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Address.cs @@ -5,20 +5,20 @@ public class Address public int Id { get; set; } [Required, MaxLength(100)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required, MaxLength(100)] - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; [MaxLength(100)] - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; [Required, MaxLength(50)] - public string City { get; set; } + public string City { get; set; } = string.Empty; [Required, MaxLength(20)] - public string Region { get; set; } + public string Region { get; set; } = string.Empty; [Required, MaxLength(20)] - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/BlazingPizza.Shared.csproj index fb17e53b..785c204e 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,17 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - - + + + + diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Order.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Order.cs index 180ec55b..f1211588 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Order.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Pizza.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Pizza.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaTopping.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Topping.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Topping.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/UserInfo.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From c7d2d48d61dd3981dd381f8914eed3801926ed32 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Fri, 15 Sep 2023 17:10:16 -0400 Subject: [PATCH 25/53] Updated 06 with nullable context enabled. --- .../BlazingPizza.Client/App.razor | 18 +++++++++--------- .../BlazingPizza.Client/OrderState.cs | 6 +++--- .../BlazingPizza.Client/OrdersClient.cs | 4 ++-- .../Pages/Authentication.razor | 2 +- .../BlazingPizza.Client/Pages/Checkout.razor | 6 +++--- .../PizzaAuthenticationState.cs | 2 +- .../Shared/LoginDisplay.razor | 2 +- .../NotificationsController.cs | 10 +++++----- .../BlazingPizza.Server/OrdersController.cs | 6 +++--- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/App.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/App.razor index 815659c4..5b6fc0fa 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/App.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/App.razor @@ -1,6 +1,6 @@  - - + + @@ -9,11 +9,11 @@
      Please wait...
      -
      - - -
      Sorry, there's nothing at this address.
      -
      -
      -
      +
      + + +
      Sorry, there's nothing at this address.
      +
      +
      +
      diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrderState.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrderState.cs index e47c6627..991a7512 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrderState.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrderState.cs @@ -4,7 +4,7 @@ public class OrderState { public bool ShowingConfigureDialog { get; private set; } - public Pizza ConfiguringPizza { get; private set; } + public Pizza? ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); @@ -31,8 +31,8 @@ public void ConfirmConfigurePizzaDialog() { if (ConfiguringPizza is not null) { - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; } ShowingConfigureDialog = false; diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrdersClient.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrdersClient.cs index 5e9f9858..8dd911c3 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrdersClient.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/OrdersClient.cs @@ -12,11 +12,11 @@ public OrdersClient(HttpClient httpClient) } public async Task> GetOrders() => - await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus); + await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus) ?? new(); public async Task GetOrder(int orderId) => - await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus); + await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus) ?? new(); public async Task PlaceOrder(Order order) diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor index abed0b1f..c2f633e1 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor @@ -9,7 +9,7 @@ Action="@Action" /> @code{ - [Parameter] public string Action { get; set; } + [Parameter] public string Action { get; set; } = string.Empty; public PizzaAuthenticationState RemoteAuthenticationState { get; set; } = new PizzaAuthenticationState(); diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Checkout.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Checkout.razor index 44b01467..e7a194f5 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Checkout.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Checkout.razor @@ -38,9 +38,9 @@ try { var newOrderId = await OrdersClient.PlaceOrder(OrderState.Order); - OrderState.ResetOrder(); - NavigationManager.NavigateTo($"myorders/{newOrderId}"); - } + OrderState.ResetOrder(); + NavigationManager.NavigateTo($"myorders/{newOrderId}"); + } catch (AccessTokenNotAvailableException ex) { ex.Redirect(); diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/PizzaAuthenticationState.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Client/PizzaAuthenticationState.cs index 78627625..589d0620 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/PizzaAuthenticationState.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/PizzaAuthenticationState.cs @@ -4,5 +4,5 @@ namespace BlazingPizza.Client; public class PizzaAuthenticationState : RemoteAuthenticationState { - public Order Order { get; set; } + public Order? Order { get; set; } } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor index 4d483eb9..e1d9367f 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -9,7 +9,7 @@ diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/NotificationsController.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/NotificationsController.cs index bb3b96ff..43633d88 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/NotificationsController.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/NotificationsController.cs @@ -23,7 +23,11 @@ public async Task Subscribe(NotificationSubscription s { // We're storing at most one subscription per user, so delete old ones. // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. - var userId = GetUserId(); + var userId = PizzaApiExtensions.GetUserId(HttpContext); + if (userId is null) + { + throw new UnauthorizedAccessException(); + } var oldSubscriptions = _db.NotificationSubscriptions.Where(e => e.UserId == userId); _db.NotificationSubscriptions.RemoveRange(oldSubscriptions); @@ -35,9 +39,5 @@ public async Task Subscribe(NotificationSubscription s return subscription; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } } } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs index 8d61a843..c80b1c34 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs @@ -25,7 +25,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -40,7 +40,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -59,7 +59,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - order.UserId = GetUserId(); + order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up From 1279c3f221363c2cb3a4face48d3247b3063a883 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Mon, 18 Sep 2023 15:47:29 -0400 Subject: [PATCH 26/53] Synced dependencies with previous lesson --- .../BlazingPizza.ComponentsLibrary.csproj | 3 +- .../Map/Map.razor | 4 +- .../Map/Marker.cs | 4 +- .../Map/Point.cs | 3 +- .../Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server.csproj | 37 ++++++++-------- .../NotificationsController.cs | 43 +++++++++++++++++++ .../BlazingPizza.Server/OrdersController.cs | 21 ++++----- .../BlazingPizza.Server/PizzaApiExtensions.cs | 9 ++-- .../BlazingPizza.Shared/Address.cs | 12 +++--- .../BlazingPizza.Shared.csproj | 17 ++++---- .../NotificationSubscription.cs | 10 ++--- .../BlazingPizza.Shared/Order.cs | 6 ++- .../BlazingPizza.Shared/OrderWithStatus.cs | 10 +++-- .../BlazingPizza.Shared/Pizza.cs | 8 ++-- .../BlazingPizza.Shared/PizzaSpecial.cs | 6 +-- .../BlazingPizza.Shared/PizzaTopping.cs | 2 +- .../BlazingPizza.Shared/Topping.cs | 2 +- .../BlazingPizza.Shared/UserInfo.cs | 2 +- 19 files changed, 126 insertions(+), 75 deletions(-) create mode 100644 save-points/07-javascript-interop/BlazingPizza.Server/NotificationsController.cs diff --git a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index cb996b1b..a907e880 100644 --- a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - true + true + enable diff --git a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Marker.cs index b067719c..d4dcdbad 100644 --- a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -2,11 +2,11 @@ public class Marker { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; public double X { get; set; } public double Y { get; set; } public bool ShowPopup { get; set; } -} +} \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Point.cs b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Point.cs index 34194163..054e56ec 100644 --- a/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Point.cs +++ b/save-points/07-javascript-interop/BlazingPizza.ComponentsLibrary/Map/Point.cs @@ -5,4 +5,5 @@ public class Point public double X { get; set; } public double Y { get; set; } -} \ No newline at end of file +} +// Compare this snippet from save-points\01-Components-and-layout\BlazingPizza.ComponentsLibrary\Map\Marker.cs: \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/07-javascript-interop/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/07-javascript-interop/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
      - @User.Identity.Name + @User?.Identity?.Name
      diff --git a/save-points/07-javascript-interop/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/07-javascript-interop/BlazingPizza.Server/BlazingPizza.Server.csproj index abd2317b..b028c771 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/07-javascript-interop/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -1,25 +1,26 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + diff --git a/save-points/07-javascript-interop/BlazingPizza.Server/NotificationsController.cs b/save-points/07-javascript-interop/BlazingPizza.Server/NotificationsController.cs new file mode 100644 index 00000000..43633d88 --- /dev/null +++ b/save-points/07-javascript-interop/BlazingPizza.Server/NotificationsController.cs @@ -0,0 +1,43 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BlazingPizza.Server +{ + [Route("notifications")] + [ApiController] + [Authorize] + public class NotificationsController : Controller + { + private readonly PizzaStoreContext _db; + + public NotificationsController(PizzaStoreContext db) + { + _db = db; + } + + [HttpPut("subscribe")] + public async Task Subscribe(NotificationSubscription subscription) + { + // We're storing at most one subscription per user, so delete old ones. + // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. + var userId = PizzaApiExtensions.GetUserId(HttpContext); + if (userId is null) + { + throw new UnauthorizedAccessException(); + } + var oldSubscriptions = _db.NotificationSubscriptions.Where(e => e.UserId == userId); + _db.NotificationSubscriptions.RemoveRange(oldSubscriptions); + + // Store new subscription + subscription.UserId = userId; + _db.NotificationSubscriptions.Attach(subscription); + + await _db.SaveChangesAsync(); + return subscription; + } + + } +} diff --git a/save-points/07-javascript-interop/BlazingPizza.Server/OrdersController.cs b/save-points/07-javascript-interop/BlazingPizza.Server/OrdersController.cs index 3d2ba63f..adf19b41 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Server/OrdersController.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Server/OrdersController.cs @@ -23,7 +23,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -38,7 +38,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -57,19 +57,19 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - order.UserId = GetUserId(); + order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -78,7 +78,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -87,11 +87,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track @@ -123,8 +118,8 @@ private static async Task SendNotificationAsync(Order order, NotificationSubscri await webPushClient.SendNotificationAsync(pushSubscription, payload, vapidDetails); } catch (Exception ex) - { + { Console.Error.WriteLine("Error sending push notification: " + ex.Message); } } -} +} \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/07-javascript-interop/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..d277597f 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Server/PizzaApiExtensions.cs @@ -19,6 +19,10 @@ public static WebApplication MapPizzaApi(this WebApplication app) // We're storing at most one subscription per user, so delete old ones. // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. var userId = GetUserId(context); + if (userId is null) + { + return Results.Unauthorized(); + } var oldSubscriptions = db.NotificationSubscriptions.Where(e => e.UserId == userId); db.NotificationSubscriptions.RemoveRange(oldSubscriptions); @@ -49,9 +53,6 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) - { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); - } + public static string? GetUserId(HttpContext context) => context.User.FindFirstValue(ClaimTypes.NameIdentifier); } \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/Address.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/Address.cs index a671359a..15e546c1 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/Address.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/Address.cs @@ -5,20 +5,20 @@ public class Address public int Id { get; set; } [Required, MaxLength(100)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required, MaxLength(100)] - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; [MaxLength(100)] - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; [Required, MaxLength(50)] - public string City { get; set; } + public string City { get; set; } = string.Empty; [Required, MaxLength(20)] - public string Region { get; set; } + public string Region { get; set; } = string.Empty; [Required, MaxLength(20)] - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/07-javascript-interop/BlazingPizza.Shared/BlazingPizza.Shared.csproj index fb17e53b..785c204e 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,17 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - - + + + + diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/Order.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/Order.cs index 180ec55b..f1211588 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/Order.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/Pizza.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/Pizza.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaTopping.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/Topping.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/Topping.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/UserInfo.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From dcd325d855d5f83b2daaf67226534436b93ba233 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Mon, 18 Sep 2023 16:02:37 -0400 Subject: [PATCH 27/53] Sync client changes with previous step --- .../BlazingPizza.Client/App.razor | 18 +++++----- .../BlazingPizza.Client.csproj | 28 +++++++-------- .../BlazingPizza.Client/OrderState.cs | 9 +++-- .../BlazingPizza.Client/OrdersClient.cs | 10 ++---- .../Pages/Authentication.razor | 4 +-- .../BlazingPizza.Client/Pages/Checkout.razor | 6 ++-- .../BlazingPizza.Client/Pages/Index.razor | 2 +- .../BlazingPizza.Client/Pages/MyOrders.razor | 4 +-- .../Pages/OrderDetails.razor | 4 +-- .../Shared/ConfigurePizzaDialog.razor | 36 +++++++++++-------- .../Shared/ConfiguredPizzaItem.razor | 8 ++--- .../Shared/LoginDisplay.razor | 2 +- .../Shared/OrderReview.razor | 6 ++-- .../BlazingPizza.Client/_Imports.razor | 1 - 14 files changed, 70 insertions(+), 68 deletions(-) diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/App.razor b/save-points/07-javascript-interop/BlazingPizza.Client/App.razor index 815659c4..5b6fc0fa 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/App.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/App.razor @@ -1,6 +1,6 @@  - - + + @@ -9,11 +9,11 @@
      Please wait...
      -
      - - -
      Sorry, there's nothing at this address.
      -
      -
      -
      +
      + + +
      Sorry, there's nothing at this address.
      +
      +
      +
      diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.csproj index 1847e754..48b9455e 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -1,21 +1,21 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - - - - - - + + + + + + - - - - + + + + diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/OrderState.cs b/save-points/07-javascript-interop/BlazingPizza.Client/OrderState.cs index 00dd3627..991a7512 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/OrderState.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Client/OrderState.cs @@ -4,7 +4,7 @@ public class OrderState { public bool ShowingConfigureDialog { get; private set; } - public Pizza ConfiguringPizza { get; private set; } + public Pizza? ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); @@ -29,8 +29,11 @@ public void CancelConfigurePizzaDialog() public void ConfirmConfigurePizzaDialog() { - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; + if (ConfiguringPizza is not null) + { + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; + } ShowingConfigureDialog = false; } diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/OrdersClient.cs b/save-points/07-javascript-interop/BlazingPizza.Client/OrdersClient.cs index b4a08f1e..8dd911c3 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/OrdersClient.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Client/OrdersClient.cs @@ -1,5 +1,4 @@ using System.Net.Http.Json; -using System.Text.Json; namespace BlazingPizza.Client; @@ -13,11 +12,11 @@ public OrdersClient(HttpClient httpClient) } public async Task> GetOrders() => - await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus); + await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus) ?? new(); public async Task GetOrder(int orderId) => - await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus); + await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus) ?? new(); public async Task PlaceOrder(Order order) @@ -28,9 +27,4 @@ public async Task PlaceOrder(Order order) return orderId; } - public async Task SubscribeToNotifications(NotificationSubscription subscription) - { - var response = await httpClient.PutAsJsonAsync("notifications/subscribe", subscription); - response.EnsureSuccessStatusCode(); - } } diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor index 5691b4b0..c2f633e1 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor @@ -9,7 +9,7 @@ Action="@Action" /> @code{ - [Parameter] public string Action { get; set; } + [Parameter] public string Action { get; set; } = string.Empty; public PizzaAuthenticationState RemoteAuthenticationState { get; set; } = new PizzaAuthenticationState(); @@ -29,4 +29,4 @@ OrderState.ReplaceOrder(pizzaState.Order); } } -} \ No newline at end of file +} diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Checkout.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Checkout.razor index 44b01467..e7a194f5 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Checkout.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Checkout.razor @@ -38,9 +38,9 @@ try { var newOrderId = await OrdersClient.PlaceOrder(OrderState.Order); - OrderState.ResetOrder(); - NavigationManager.NavigateTo($"myorders/{newOrderId}"); - } + OrderState.ResetOrder(); + NavigationManager.NavigateTo($"myorders/{newOrderId}"); + } catch (AccessTokenNotAvailableException ex) { ex.Redirect(); diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor index 26b1fe38..2c3085a5 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor @@ -57,7 +57,7 @@ } @code { - List specials; + List? specials; Order Order => OrderState.Order; protected override async Task OnInitializedAsync() diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor index b28dedeb..e739c243 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor @@ -6,7 +6,7 @@ Blazing Pizza - My Orders
      - @if (ordersWithStatus == null) + @if (ordersWithStatus is null) { Loading... } @@ -43,7 +43,7 @@
      @code { - IEnumerable ordersWithStatus; + IEnumerable? ordersWithStatus; protected override async Task OnParametersSetAsync() { diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor index 548bbd59..1dde78fb 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor @@ -13,7 +13,7 @@

      Nope

      Sorry, this order could not be loaded.

      } - else if (orderWithStatus == null) + else if (orderWithStatus is null) { Loading... } @@ -34,9 +34,9 @@
      -
      +
      }
      diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 56549454..fa52ca09 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -3,8 +3,8 @@
      -

      @Pizza.Special.Name

      - @Pizza.Special.Description +

      @Pizza.Special?.Name

      + @Pizza.Special?.Description
      @@ -16,7 +16,7 @@
      - @if (toppings == null) + @if (toppings is null) { - - @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice())) - -
      -
      - - @if (toppings == null) - { - - } - else if (Pizza.Toppings.Count >= 6) - { -
      (maximum reached)
      - } - else - { - + + @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice())) + +
      +
      + + @if (toppings is null) { - + } - - } -
      + else if (Pizza.Toppings.Count >= 6) + { +
      (maximum reached)
      + } + else + { + + } +
      -
      - @foreach (var topping in Pizza.Toppings) - { -
      - @topping.Topping.Name - @topping.Topping.GetFormattedPrice() - +
      + @foreach (var topping in Pizza.Toppings) + { + if (topping?.Topping is not null) + { +
      + @topping.Topping.Name + @topping.Topping.GetFormattedPrice() + +
      + } + }
      - } -
      - + -
      - - - Price: @(Pizza.GetFormattedTotalPrice()) - - +
      + + + Price: @(Pizza.GetFormattedTotalPrice()) + + +
      +
      @code { - List toppings; + // toppings is only null while loading + List? toppings; - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnCancel { get; set; } - [Parameter] public EventCallback OnConfirm { get; set; } + // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } + [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } protected async override Task OnInitializedAsync() { - toppings = await HttpClient.GetFromJsonAsync>("toppings"); + toppings = await HttpClient.GetFromJsonAsync>("toppings") ?? new(); } void ToppingSelected(ChangeEventArgs e) { - if (int.TryParse((string)e.Value, out var index) && index >= 0) + if (toppings is null) return; + if (int.TryParse((string?)e.Value, out var index) && index >= 0) { AddTopping(toppings[index]); } @@ -78,7 +88,7 @@ void AddTopping(Topping topping) { - if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null) + if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null) { Pizza.Toppings.Add(new PizzaTopping() { Topping = topping }); } diff --git a/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor b/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor index e0e9f41d..f7f7a31d 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor @@ -1,10 +1,10 @@ 
      x -
      @(Pizza.Size)" @Pizza.Special.Name
      +
      @(Pizza.Size)" @Pizza.Special?.Name
        @foreach (var topping in Pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      @@ -13,6 +13,6 @@
      @code { - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnRemoved { get; set; } + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnRemoved { get; set; } } \ No newline at end of file diff --git a/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor b/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor index 4d483eb9..e1d9367f 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -9,7 +9,7 @@ diff --git a/save-points/08-templated-components/BlazingPizza.Client/Shared/OrderReview.razor b/save-points/08-templated-components/BlazingPizza.Client/Shared/OrderReview.razor index e1424934..56668721 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Shared/OrderReview.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Shared/OrderReview.razor @@ -3,7 +3,7 @@

      @(pizza.Size)" - @pizza.Special.Name + @pizza.Special?.Name (£@pizza.GetFormattedTotalPrice())

      @@ -11,7 +11,7 @@
        @foreach (var topping in pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      } @@ -24,5 +24,5 @@

      @code { - [Parameter] public Order Order { get; set; } + [Parameter, EditorRequired] public Order Order { get; set; } = new(); } From 038e53c99c8736b51fb773ee379afe04d41293a4 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Mon, 18 Sep 2023 16:23:18 -0400 Subject: [PATCH 31/53] Updated 08 with nullable conetext enabled --- .../BlazingComponents.csproj | 1 + .../BlazingComponents/TemplatedDialog.razor | 2 +- .../BlazingComponents/TemplatedList.razor | 24 ++++++++++++------- .../BlazingPizza.Client.csproj | 24 +++++++++---------- .../BlazingPizza.Client/Pages/Index.razor | 4 ++-- .../BlazingPizza.Client/Pages/MyOrders.razor | 2 +- .../Shared/ConfigurePizzaDialog.razor | 6 ++--- .../BlazingPizza.Client/_Imports.razor | 2 +- 8 files changed, 35 insertions(+), 30 deletions(-) diff --git a/save-points/08-templated-components/BlazingComponents/BlazingComponents.csproj b/save-points/08-templated-components/BlazingComponents/BlazingComponents.csproj index e0a4b76a..9864380a 100644 --- a/save-points/08-templated-components/BlazingComponents/BlazingComponents.csproj +++ b/save-points/08-templated-components/BlazingComponents/BlazingComponents.csproj @@ -2,6 +2,7 @@ $(TargetFrameworkVersion) + enable diff --git a/save-points/08-templated-components/BlazingComponents/TemplatedDialog.razor b/save-points/08-templated-components/BlazingComponents/TemplatedDialog.razor index 52d3dd95..be590222 100644 --- a/save-points/08-templated-components/BlazingComponents/TemplatedDialog.razor +++ b/save-points/08-templated-components/BlazingComponents/TemplatedDialog.razor @@ -8,6 +8,6 @@ } @code { - [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter, EditorRequired] public RenderFragment? ChildContent { get; set; } [Parameter] public bool Show { get; set; } } diff --git a/save-points/08-templated-components/BlazingComponents/TemplatedList.razor b/save-points/08-templated-components/BlazingComponents/TemplatedList.razor index 7961600e..c75e411f 100644 --- a/save-points/08-templated-components/BlazingComponents/TemplatedList.razor +++ b/save-points/08-templated-components/BlazingComponents/TemplatedList.razor @@ -1,6 +1,6 @@ @typeparam TItem -@if (items == null) +@if (items is null) { @Loading } @@ -14,23 +14,29 @@ else @foreach (var item in items) {
      - @Item(item) + @if (Item is not null) + { + @Item(item) + }
      }
      } @code { - IEnumerable items; + IEnumerable? items; - [Parameter] public Func>> Loader { get; set; } - [Parameter] public RenderFragment Loading { get; set; } - [Parameter] public RenderFragment Empty { get; set; } - [Parameter] public RenderFragment Item { get; set; } - [Parameter] public string ListGroupClass { get; set; } + [Parameter, EditorRequired] public Func>>? Loader { get; set; } + [Parameter] public RenderFragment? Loading { get; set; } + [Parameter] public RenderFragment? Empty { get; set; } + [Parameter, EditorRequired] public RenderFragment? Item { get; set; } + [Parameter] public string? ListGroupClass { get; set; } protected override async Task OnParametersSetAsync() { - items = await Loader(); + if (Loader is not null) + { + items = await Loader(); + } } } diff --git a/save-points/08-templated-components/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/08-templated-components/BlazingPizza.Client/BlazingPizza.Client.csproj index d960c3f9..79dc4606 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/08-templated-components/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -1,23 +1,23 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true enable - + - - + + - + - - + + - + - - - + + + diff --git a/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor b/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor index ca5ac084..3c1c882c 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor @@ -56,7 +56,7 @@ @code { - List specials; + List? specials; Order Order => OrderState.Order; protected override async Task OnInitializedAsync() @@ -66,7 +66,7 @@ async Task RemovePizza(Pizza configuredPizza) { - if (await JS.Confirm($"Remove {configuredPizza.Special.Name} pizza from the order?")) + if (await JS.Confirm($"Remove {configuredPizza.Special?.Name} pizza from the order?")) { OrderState.RemoveConfiguredPizza(configuredPizza); } diff --git a/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor index 8d97c52d..300b84fd 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor @@ -46,4 +46,4 @@ } return ordersWithStatus; } -} +} \ No newline at end of file diff --git a/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index fa52ca09..87eb782b 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -1,7 +1,6 @@ @inject HttpClient HttpClient -
      -
      +

      @Pizza.Special?.Name

      @Pizza.Special?.Description @@ -60,8 +59,7 @@
      -
      -
      + @code { // toppings is only null while loading diff --git a/save-points/08-templated-components/BlazingPizza.Client/_Imports.razor b/save-points/08-templated-components/BlazingPizza.Client/_Imports.razor index b575d7f7..af8847ae 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/_Imports.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/_Imports.razor @@ -12,4 +12,4 @@ @using BlazingPizza.Client.Shared @using BlazingPizza.ComponentsLibrary @using BlazingPizza.ComponentsLibrary.Map -@using BlazingComponents +@using BlazingComponents \ No newline at end of file From c3ea048cc2b1ab02fb8b40b47788331cff43692e Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Mon, 18 Sep 2023 16:42:25 -0400 Subject: [PATCH 32/53] Sync with prevous lesson --- .../BlazingComponents.csproj | 2 +- .../BlazingComponents/TemplatedDialog.razor | 2 +- .../BlazingComponents/TemplatedList.razor | 24 ++-- .../BlazingPizza.Client/App.razor | 18 +-- .../BlazingPizza.Client.csproj | 30 +++-- .../BlazingPizza.Client/OrderState.cs | 9 +- .../BlazingPizza.Client/OrdersClient.cs | 4 +- .../Pages/Authentication.razor | 4 +- .../BlazingPizza.Client/Pages/Index.razor | 4 +- .../BlazingPizza.Client/Pages/MyOrders.razor | 3 +- .../Pages/OrderDetails.razor | 7 +- .../PizzaAuthenticationState.cs | 2 +- .../Shared/AddressEditor.razor | 4 +- .../Shared/ConfigurePizzaDialog.razor | 122 ++++++++++-------- .../Shared/ConfiguredPizzaItem.razor | 8 +- .../Shared/LoginDisplay.razor | 2 +- .../Shared/OrderReview.razor | 6 +- .../BlazingPizza.Client/_Imports.razor | 2 +- .../BlazingPizza.ComponentsLibrary.csproj | 4 +- .../Map/Map.razor | 4 +- .../Map/Marker.cs | 17 ++- .../Map/Point.cs | 12 +- .../Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server.csproj | 37 +++--- .../NotificationsController.cs | 10 +- .../BlazingPizza.Server/OrdersController.cs | 21 ++- .../BlazingPizza.Server/PizzaApiExtensions.cs | 9 +- .../BlazingPizza.Shared/Address.cs | 12 +- .../BlazingPizza.Shared.csproj | 17 +-- .../NotificationSubscription.cs | 10 +- .../BlazingPizza.Shared/Order.cs | 6 +- .../BlazingPizza.Shared/OrderWithStatus.cs | 10 +- .../BlazingPizza.Shared/Pizza.cs | 8 +- .../BlazingPizza.Shared/PizzaSpecial.cs | 6 +- .../BlazingPizza.Shared/PizzaTopping.cs | 2 +- .../BlazingPizza.Shared/Topping.cs | 2 +- .../BlazingPizza.Shared/UserInfo.cs | 2 +- 37 files changed, 235 insertions(+), 209 deletions(-) diff --git a/save-points/09-progressive-web-app/BlazingComponents/BlazingComponents.csproj b/save-points/09-progressive-web-app/BlazingComponents/BlazingComponents.csproj index 89335194..9864380a 100644 --- a/save-points/09-progressive-web-app/BlazingComponents/BlazingComponents.csproj +++ b/save-points/09-progressive-web-app/BlazingComponents/BlazingComponents.csproj @@ -2,7 +2,7 @@ $(TargetFrameworkVersion) - 3.0 + enable diff --git a/save-points/09-progressive-web-app/BlazingComponents/TemplatedDialog.razor b/save-points/09-progressive-web-app/BlazingComponents/TemplatedDialog.razor index 52d3dd95..be590222 100644 --- a/save-points/09-progressive-web-app/BlazingComponents/TemplatedDialog.razor +++ b/save-points/09-progressive-web-app/BlazingComponents/TemplatedDialog.razor @@ -8,6 +8,6 @@ } @code { - [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter, EditorRequired] public RenderFragment? ChildContent { get; set; } [Parameter] public bool Show { get; set; } } diff --git a/save-points/09-progressive-web-app/BlazingComponents/TemplatedList.razor b/save-points/09-progressive-web-app/BlazingComponents/TemplatedList.razor index 7961600e..c75e411f 100644 --- a/save-points/09-progressive-web-app/BlazingComponents/TemplatedList.razor +++ b/save-points/09-progressive-web-app/BlazingComponents/TemplatedList.razor @@ -1,6 +1,6 @@ @typeparam TItem -@if (items == null) +@if (items is null) { @Loading } @@ -14,23 +14,29 @@ else @foreach (var item in items) {
      - @Item(item) + @if (Item is not null) + { + @Item(item) + }
      }
      } @code { - IEnumerable items; + IEnumerable? items; - [Parameter] public Func>> Loader { get; set; } - [Parameter] public RenderFragment Loading { get; set; } - [Parameter] public RenderFragment Empty { get; set; } - [Parameter] public RenderFragment Item { get; set; } - [Parameter] public string ListGroupClass { get; set; } + [Parameter, EditorRequired] public Func>>? Loader { get; set; } + [Parameter] public RenderFragment? Loading { get; set; } + [Parameter] public RenderFragment? Empty { get; set; } + [Parameter, EditorRequired] public RenderFragment? Item { get; set; } + [Parameter] public string? ListGroupClass { get; set; } protected override async Task OnParametersSetAsync() { - items = await Loader(); + if (Loader is not null) + { + items = await Loader(); + } } } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/App.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/App.razor index 815659c4..5b6fc0fa 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/App.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/App.razor @@ -1,6 +1,6 @@  - - + + @@ -9,11 +9,11 @@
      Please wait...
      -
      - - -
      Sorry, there's nothing at this address.
      -
      -
      -
      +
      + + +
      Sorry, there's nothing at this address.
      +
      +
      +
      diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/BlazingPizza.Client.csproj b/save-points/09-progressive-web-app/BlazingPizza.Client/BlazingPizza.Client.csproj index 3573ae56..79dc4606 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -1,21 +1,23 @@  - - $(TargetFrameworkVersion) - enable - + + $(TargetFrameworkVersion) + true + enable + - - - - - - + + + + + + + - + - - - + + + diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/OrderState.cs b/save-points/09-progressive-web-app/BlazingPizza.Client/OrderState.cs index 00dd3627..991a7512 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/OrderState.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/OrderState.cs @@ -4,7 +4,7 @@ public class OrderState { public bool ShowingConfigureDialog { get; private set; } - public Pizza ConfiguringPizza { get; private set; } + public Pizza? ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); @@ -29,8 +29,11 @@ public void CancelConfigurePizzaDialog() public void ConfirmConfigurePizzaDialog() { - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; + if (ConfiguringPizza is not null) + { + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; + } ShowingConfigureDialog = false; } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/OrdersClient.cs b/save-points/09-progressive-web-app/BlazingPizza.Client/OrdersClient.cs index c2c1a398..a24fc52d 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/OrdersClient.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/OrdersClient.cs @@ -12,11 +12,11 @@ public OrdersClient(HttpClient httpClient) } public async Task> GetOrders() => - await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus); + await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus) ?? new(); public async Task GetOrder(int orderId) => - await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus); + await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus) ?? new(); public async Task PlaceOrder(Order order) diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor index 5691b4b0..c2f633e1 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor @@ -9,7 +9,7 @@ Action="@Action" /> @code{ - [Parameter] public string Action { get; set; } + [Parameter] public string Action { get; set; } = string.Empty; public PizzaAuthenticationState RemoteAuthenticationState { get; set; } = new PizzaAuthenticationState(); @@ -29,4 +29,4 @@ OrderState.ReplaceOrder(pizzaState.Order); } } -} \ No newline at end of file +} diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor index ca5ac084..3c1c882c 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor @@ -56,7 +56,7 @@ @code { - List specials; + List? specials; Order Order => OrderState.Order; protected override async Task OnInitializedAsync() @@ -66,7 +66,7 @@ async Task RemovePizza(Pizza configuredPizza) { - if (await JS.Confirm($"Remove {configuredPizza.Special.Name} pizza from the order?")) + if (await JS.Confirm($"Remove {configuredPizza.Special?.Name} pizza from the order?")) { OrderState.RemoveConfiguredPizza(configuredPizza); } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor index ad99fb4f..300b84fd 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor @@ -1,6 +1,7 @@ @page "/myorders" @attribute [Authorize] @inject OrdersClient OrdersClient +@inject NavigationManager NavigationManager Blazing Pizza - My Orders @@ -45,4 +46,4 @@ } return ordersWithStatus; } -} +} \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor index b9500427..4393f07a 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor @@ -2,6 +2,7 @@ @attribute [Authorize] @using System.Threading @inject OrdersClient OrdersClient +@inject NavigationManager NavigationManager @implements IDisposable Blazing Pizza - Order Details @@ -12,7 +13,7 @@

      Nope

      Sorry, this order could not be loaded.

      } - else if (orderWithStatus == null) + else if (orderWithStatus is null) { Loading... } @@ -42,9 +43,9 @@ @code { [Parameter] public int OrderId { get; set; } - OrderWithStatus orderWithStatus; + OrderWithStatus? orderWithStatus; bool invalidOrder; - CancellationTokenSource pollingCancellationToken; + CancellationTokenSource? pollingCancellationToken; protected override void OnParametersSet() { diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/PizzaAuthenticationState.cs b/save-points/09-progressive-web-app/BlazingPizza.Client/PizzaAuthenticationState.cs index 78627625..589d0620 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/PizzaAuthenticationState.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/PizzaAuthenticationState.cs @@ -4,5 +4,5 @@ namespace BlazingPizza.Client; public class PizzaAuthenticationState : RemoteAuthenticationState { - public Order Order { get; set; } + public Order? Order { get; set; } } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/AddressEditor.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/AddressEditor.razor index 05e76410..fb36ff3a 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/AddressEditor.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/AddressEditor.razor @@ -47,5 +47,5 @@
      @code { - [Parameter] public Address Address { get; set; } -} + [Parameter, EditorRequired] public Address Address { get; set; } = new(); +} \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 67096b01..87eb782b 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -1,76 +1,84 @@ @inject HttpClient HttpClient -
      -

      @Pizza.Special.Name

      - @Pizza.Special.Description -
      -
      -
      - - - - @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice())) - -
      -
      - - @if (toppings == null) - { - - } - else if (Pizza.Toppings.Count >= 6) - { -
      (maximum reached)
      - } - else - { - + + @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice())) + +
      +
      + + @if (toppings is null) { - + } - - } -
      + else if (Pizza.Toppings.Count >= 6) + { +
      (maximum reached)
      + } + else + { + + } +
      -
      - @foreach (var topping in Pizza.Toppings) - { -
      - @topping.Topping.Name - @topping.Topping.GetFormattedPrice() - +
      + @foreach (var topping in Pizza.Toppings) + { + if (topping?.Topping is not null) + { +
      + @topping.Topping.Name + @topping.Topping.GetFormattedPrice() + +
      + } + }
      - } -
      - + + +
      + + + Price: @(Pizza.GetFormattedTotalPrice()) + + +
      -
      - - - Price: @(Pizza.GetFormattedTotalPrice()) - - -
      @code { - List toppings; + // toppings is only null while loading + List? toppings; - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnCancel { get; set; } - [Parameter] public EventCallback OnConfirm { get; set; } + // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } + [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } protected async override Task OnInitializedAsync() { - toppings = await HttpClient.GetFromJsonAsync>("toppings"); + toppings = await HttpClient.GetFromJsonAsync>("toppings") ?? new(); } void ToppingSelected(ChangeEventArgs e) { - if (int.TryParse((string)e.Value, out var index) && index >= 0) + if (toppings is null) return; + if (int.TryParse((string?)e.Value, out var index) && index >= 0) { AddTopping(toppings[index]); } @@ -78,7 +86,7 @@ void AddTopping(Topping topping) { - if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null) + if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null) { Pizza.Toppings.Add(new PizzaTopping() { Topping = topping }); } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor index e0e9f41d..f7f7a31d 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor @@ -1,10 +1,10 @@ 
      x -
      @(Pizza.Size)" @Pizza.Special.Name
      +
      @(Pizza.Size)" @Pizza.Special?.Name
        @foreach (var topping in Pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      @@ -13,6 +13,6 @@
      @code { - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnRemoved { get; set; } + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnRemoved { get; set; } } \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor index 4d483eb9..e1d9367f 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -9,7 +9,7 @@ diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/OrderReview.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/OrderReview.razor index e1424934..56668721 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/OrderReview.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/OrderReview.razor @@ -3,7 +3,7 @@

      @(pizza.Size)" - @pizza.Special.Name + @pizza.Special?.Name (£@pizza.GetFormattedTotalPrice())

      @@ -11,7 +11,7 @@
        @foreach (var topping in pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      } @@ -24,5 +24,5 @@

      @code { - [Parameter] public Order Order { get; set; } + [Parameter, EditorRequired] public Order Order { get; set; } = new(); } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/_Imports.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/_Imports.razor index b575d7f7..af8847ae 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/_Imports.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/_Imports.razor @@ -12,4 +12,4 @@ @using BlazingPizza.Client.Shared @using BlazingPizza.ComponentsLibrary @using BlazingPizza.ComponentsLibrary.Map -@using BlazingComponents +@using BlazingComponents \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index edb7908d..a907e880 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - enable + true + enable @@ -15,4 +16,3 @@ - diff --git a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Map.razor b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Marker.cs index 63006067..d4dcdbad 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -1,13 +1,12 @@ -namespace BlazingPizza.ComponentsLibrary.Map +namespace BlazingPizza.ComponentsLibrary.Map; + +public class Marker { - public class Marker - { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public double X { get; set; } + public double X { get; set; } - public double Y { get; set; } + public double Y { get; set; } - public bool ShowPopup { get; set; } - } -} + public bool ShowPopup { get; set; } +} \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Point.cs b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Point.cs index 03841826..054e56ec 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Point.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.ComponentsLibrary/Map/Point.cs @@ -1,9 +1,9 @@ -namespace BlazingPizza.ComponentsLibrary.Map +namespace BlazingPizza.ComponentsLibrary.Map; + +public class Point { - public class Point - { - public double X { get; set; } + public double X { get; set; } - public double Y { get; set; } - } + public double Y { get; set; } } +// Compare this snippet from save-points\01-Components-and-layout\BlazingPizza.ComponentsLibrary\Map\Marker.cs: \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/save-points/09-progressive-web-app/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/save-points/09-progressive-web-app/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
      - @User.Identity.Name + @User?.Identity?.Name
      diff --git a/save-points/09-progressive-web-app/BlazingPizza.Server/BlazingPizza.Server.csproj b/save-points/09-progressive-web-app/BlazingPizza.Server/BlazingPizza.Server.csproj index abd2317b..b028c771 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/save-points/09-progressive-web-app/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -1,25 +1,26 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + diff --git a/save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs b/save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs index bb3b96ff..43633d88 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs @@ -23,7 +23,11 @@ public async Task Subscribe(NotificationSubscription s { // We're storing at most one subscription per user, so delete old ones. // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. - var userId = GetUserId(); + var userId = PizzaApiExtensions.GetUserId(HttpContext); + if (userId is null) + { + throw new UnauthorizedAccessException(); + } var oldSubscriptions = _db.NotificationSubscriptions.Where(e => e.UserId == userId); _db.NotificationSubscriptions.RemoveRange(oldSubscriptions); @@ -35,9 +39,5 @@ public async Task Subscribe(NotificationSubscription s return subscription; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } } } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Server/OrdersController.cs b/save-points/09-progressive-web-app/BlazingPizza.Server/OrdersController.cs index 3d2ba63f..adf19b41 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Server/OrdersController.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Server/OrdersController.cs @@ -23,7 +23,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -38,7 +38,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -57,19 +57,19 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - order.UserId = GetUserId(); + order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -78,7 +78,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -87,11 +87,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track @@ -123,8 +118,8 @@ private static async Task SendNotificationAsync(Order order, NotificationSubscri await webPushClient.SendNotificationAsync(pushSubscription, payload, vapidDetails); } catch (Exception ex) - { + { Console.Error.WriteLine("Error sending push notification: " + ex.Message); } } -} +} \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Server/PizzaApiExtensions.cs b/save-points/09-progressive-web-app/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..d277597f 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Server/PizzaApiExtensions.cs @@ -19,6 +19,10 @@ public static WebApplication MapPizzaApi(this WebApplication app) // We're storing at most one subscription per user, so delete old ones. // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. var userId = GetUserId(context); + if (userId is null) + { + return Results.Unauthorized(); + } var oldSubscriptions = db.NotificationSubscriptions.Where(e => e.UserId == userId); db.NotificationSubscriptions.RemoveRange(oldSubscriptions); @@ -49,9 +53,6 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) - { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); - } + public static string? GetUserId(HttpContext context) => context.User.FindFirstValue(ClaimTypes.NameIdentifier); } \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/Address.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/Address.cs index a671359a..15e546c1 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/Address.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/Address.cs @@ -5,20 +5,20 @@ public class Address public int Id { get; set; } [Required, MaxLength(100)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required, MaxLength(100)] - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; [MaxLength(100)] - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; [Required, MaxLength(50)] - public string City { get; set; } + public string City { get; set; } = string.Empty; [Required, MaxLength(20)] - public string Region { get; set; } + public string Region { get; set; } = string.Empty; [Required, MaxLength(20)] - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/save-points/09-progressive-web-app/BlazingPizza.Shared/BlazingPizza.Shared.csproj index fb17e53b..785c204e 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,17 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - - + + + + diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/Order.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/Order.cs index 180ec55b..f1211588 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/Order.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/OrderWithStatus.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/OrderWithStatus.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/Pizza.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/Pizza.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaSpecial.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaSpecial.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaTopping.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaTopping.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/Topping.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/Topping.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/UserInfo.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/UserInfo.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From f3539c4bd169e8d398d51e4dc80c3ac957b74899 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Mon, 18 Sep 2023 17:50:09 -0400 Subject: [PATCH 33/53] Updated 09 with nullable context enabled --- .../BlazingPizza.Client/Pages/Checkout.razor | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor index c78cacd1..f3c3a0fe 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor @@ -61,9 +61,9 @@ try { var newOrderId = await OrdersClient.PlaceOrder(OrderState.Order); - OrderState.ResetOrder(); - NavigationManager.NavigateTo($"myorders/{newOrderId}"); - } + OrderState.ResetOrder(); + NavigationManager.NavigateTo($"myorders/{newOrderId}"); + } catch (AccessTokenNotAvailableException ex) { ex.Redirect(); From 9ea1c99bd5d5c4cd09b33175e3605749eed53e8f Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Tue, 19 Sep 2023 16:52:21 -0400 Subject: [PATCH 34/53] Updated src with nullable context enabled --- .../BlazingComponents.csproj | 1 + src/BlazingComponents/TemplatedDialog.razor | 2 +- src/BlazingComponents/TemplatedList.razor | 24 ++-- src/BlazingPizza.Client/App.razor | 18 +-- .../BlazingPizza.Client.csproj | 25 ++-- src/BlazingPizza.Client/OrderState.cs | 9 +- src/BlazingPizza.Client/OrdersClient.cs | 5 +- .../Pages/Authentication.razor | 2 +- src/BlazingPizza.Client/Pages/Checkout.razor | 6 +- src/BlazingPizza.Client/Pages/Index.razor | 6 +- src/BlazingPizza.Client/Pages/MyOrders.razor | 2 +- .../Pages/OrderDetails.razor | 6 +- .../PizzaAuthenticationState.cs | 2 +- .../Shared/AddressEditor.razor | 4 +- .../Shared/ConfigurePizzaDialog.razor | 122 ++++++++++-------- .../Shared/ConfiguredPizzaItem.razor | 8 +- .../Shared/LoginDisplay.razor | 2 +- .../Shared/OrderReview.razor | 6 +- src/BlazingPizza.Client/_Imports.razor | 2 +- .../BlazingPizza.ComponentsLibrary.csproj | 3 +- .../Map/Map.razor | 4 +- .../Map/Marker.cs | 4 +- .../Map/Point.cs | 3 +- .../Pages/Shared/_LoginPartial.cshtml | 2 +- .../BlazingPizza.Server.csproj | 37 +++--- src/BlazingPizza.Server/OrdersController.cs | 21 ++- src/BlazingPizza.Server/PizzaApiExtensions.cs | 9 +- .../Properties/launchSettings.json | 10 +- src/BlazingPizza.Shared/Address.cs | 12 +- .../BlazingPizza.Shared.csproj | 17 +-- .../NotificationSubscription.cs | 10 +- src/BlazingPizza.Shared/Order.cs | 6 +- src/BlazingPizza.Shared/OrderWithStatus.cs | 10 +- src/BlazingPizza.Shared/Pizza.cs | 8 +- src/BlazingPizza.Shared/PizzaSpecial.cs | 6 +- src/BlazingPizza.Shared/PizzaTopping.cs | 2 +- src/BlazingPizza.Shared/Topping.cs | 2 +- src/BlazingPizza.Shared/UserInfo.cs | 2 +- 38 files changed, 223 insertions(+), 197 deletions(-) diff --git a/src/BlazingComponents/BlazingComponents.csproj b/src/BlazingComponents/BlazingComponents.csproj index e0a4b76a..9864380a 100644 --- a/src/BlazingComponents/BlazingComponents.csproj +++ b/src/BlazingComponents/BlazingComponents.csproj @@ -2,6 +2,7 @@ $(TargetFrameworkVersion) + enable diff --git a/src/BlazingComponents/TemplatedDialog.razor b/src/BlazingComponents/TemplatedDialog.razor index 52d3dd95..be590222 100644 --- a/src/BlazingComponents/TemplatedDialog.razor +++ b/src/BlazingComponents/TemplatedDialog.razor @@ -8,6 +8,6 @@ } @code { - [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter, EditorRequired] public RenderFragment? ChildContent { get; set; } [Parameter] public bool Show { get; set; } } diff --git a/src/BlazingComponents/TemplatedList.razor b/src/BlazingComponents/TemplatedList.razor index 7961600e..c75e411f 100644 --- a/src/BlazingComponents/TemplatedList.razor +++ b/src/BlazingComponents/TemplatedList.razor @@ -1,6 +1,6 @@ @typeparam TItem -@if (items == null) +@if (items is null) { @Loading } @@ -14,23 +14,29 @@ else @foreach (var item in items) {
      - @Item(item) + @if (Item is not null) + { + @Item(item) + }
      }
      } @code { - IEnumerable items; + IEnumerable? items; - [Parameter] public Func>> Loader { get; set; } - [Parameter] public RenderFragment Loading { get; set; } - [Parameter] public RenderFragment Empty { get; set; } - [Parameter] public RenderFragment Item { get; set; } - [Parameter] public string ListGroupClass { get; set; } + [Parameter, EditorRequired] public Func>>? Loader { get; set; } + [Parameter] public RenderFragment? Loading { get; set; } + [Parameter] public RenderFragment? Empty { get; set; } + [Parameter, EditorRequired] public RenderFragment? Item { get; set; } + [Parameter] public string? ListGroupClass { get; set; } protected override async Task OnParametersSetAsync() { - items = await Loader(); + if (Loader is not null) + { + items = await Loader(); + } } } diff --git a/src/BlazingPizza.Client/App.razor b/src/BlazingPizza.Client/App.razor index 21d5a455..5b6fc0fa 100644 --- a/src/BlazingPizza.Client/App.razor +++ b/src/BlazingPizza.Client/App.razor @@ -1,6 +1,6 @@  - - + + @@ -9,11 +9,11 @@
      Please wait...
      -
      - - -
      Sorry, there's nothing at this address.
      -
      -
      -
      +
      + + +
      Sorry, there's nothing at this address.
      +
      +
      +
      diff --git a/src/BlazingPizza.Client/BlazingPizza.Client.csproj b/src/BlazingPizza.Client/BlazingPizza.Client.csproj index 44802d79..79dc4606 100644 --- a/src/BlazingPizza.Client/BlazingPizza.Client.csproj +++ b/src/BlazingPizza.Client/BlazingPizza.Client.csproj @@ -1,22 +1,23 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - + + - + - - + + - + - - - + + + diff --git a/src/BlazingPizza.Client/OrderState.cs b/src/BlazingPizza.Client/OrderState.cs index 00dd3627..991a7512 100644 --- a/src/BlazingPizza.Client/OrderState.cs +++ b/src/BlazingPizza.Client/OrderState.cs @@ -4,7 +4,7 @@ public class OrderState { public bool ShowingConfigureDialog { get; private set; } - public Pizza ConfiguringPizza { get; private set; } + public Pizza? ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); @@ -29,8 +29,11 @@ public void CancelConfigurePizzaDialog() public void ConfirmConfigurePizzaDialog() { - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; + if (ConfiguringPizza is not null) + { + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; + } ShowingConfigureDialog = false; } diff --git a/src/BlazingPizza.Client/OrdersClient.cs b/src/BlazingPizza.Client/OrdersClient.cs index b4a08f1e..a24fc52d 100644 --- a/src/BlazingPizza.Client/OrdersClient.cs +++ b/src/BlazingPizza.Client/OrdersClient.cs @@ -1,5 +1,4 @@ using System.Net.Http.Json; -using System.Text.Json; namespace BlazingPizza.Client; @@ -13,11 +12,11 @@ public OrdersClient(HttpClient httpClient) } public async Task> GetOrders() => - await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus); + await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus) ?? new(); public async Task GetOrder(int orderId) => - await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus); + await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus) ?? new(); public async Task PlaceOrder(Order order) diff --git a/src/BlazingPizza.Client/Pages/Authentication.razor b/src/BlazingPizza.Client/Pages/Authentication.razor index abed0b1f..c2f633e1 100644 --- a/src/BlazingPizza.Client/Pages/Authentication.razor +++ b/src/BlazingPizza.Client/Pages/Authentication.razor @@ -9,7 +9,7 @@ Action="@Action" /> @code{ - [Parameter] public string Action { get; set; } + [Parameter] public string Action { get; set; } = string.Empty; public PizzaAuthenticationState RemoteAuthenticationState { get; set; } = new PizzaAuthenticationState(); diff --git a/src/BlazingPizza.Client/Pages/Checkout.razor b/src/BlazingPizza.Client/Pages/Checkout.razor index c78cacd1..f3c3a0fe 100644 --- a/src/BlazingPizza.Client/Pages/Checkout.razor +++ b/src/BlazingPizza.Client/Pages/Checkout.razor @@ -61,9 +61,9 @@ try { var newOrderId = await OrdersClient.PlaceOrder(OrderState.Order); - OrderState.ResetOrder(); - NavigationManager.NavigateTo($"myorders/{newOrderId}"); - } + OrderState.ResetOrder(); + NavigationManager.NavigateTo($"myorders/{newOrderId}"); + } catch (AccessTokenNotAvailableException ex) { ex.Redirect(); diff --git a/src/BlazingPizza.Client/Pages/Index.razor b/src/BlazingPizza.Client/Pages/Index.razor index f8ff79df..3c1c882c 100644 --- a/src/BlazingPizza.Client/Pages/Index.razor +++ b/src/BlazingPizza.Client/Pages/Index.razor @@ -56,17 +56,17 @@ @code { - List specials; + List? specials; Order Order => OrderState.Order; protected override async Task OnInitializedAsync() { - specials = await HttpClient.GetFromJsonAsync("specials", BlazingPizza.OrderContext.Default.ListPizzaSpecial); + specials = await HttpClient.GetFromJsonAsync>("specials"); } async Task RemovePizza(Pizza configuredPizza) { - if (await JS.Confirm($"Remove {configuredPizza.Special.Name} pizza from the order?")) + if (await JS.Confirm($"Remove {configuredPizza.Special?.Name} pizza from the order?")) { OrderState.RemoveConfiguredPizza(configuredPizza); } diff --git a/src/BlazingPizza.Client/Pages/MyOrders.razor b/src/BlazingPizza.Client/Pages/MyOrders.razor index ad99fb4f..1efeb194 100644 --- a/src/BlazingPizza.Client/Pages/MyOrders.razor +++ b/src/BlazingPizza.Client/Pages/MyOrders.razor @@ -45,4 +45,4 @@ } return ordersWithStatus; } -} +} \ No newline at end of file diff --git a/src/BlazingPizza.Client/Pages/OrderDetails.razor b/src/BlazingPizza.Client/Pages/OrderDetails.razor index b9500427..79c18741 100644 --- a/src/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/src/BlazingPizza.Client/Pages/OrderDetails.razor @@ -12,7 +12,7 @@

      Nope

      Sorry, this order could not be loaded.

      } - else if (orderWithStatus == null) + else if (orderWithStatus is null) { Loading... } @@ -42,9 +42,9 @@ @code { [Parameter] public int OrderId { get; set; } - OrderWithStatus orderWithStatus; + OrderWithStatus? orderWithStatus; bool invalidOrder; - CancellationTokenSource pollingCancellationToken; + CancellationTokenSource? pollingCancellationToken; protected override void OnParametersSet() { diff --git a/src/BlazingPizza.Client/PizzaAuthenticationState.cs b/src/BlazingPizza.Client/PizzaAuthenticationState.cs index 78627625..589d0620 100644 --- a/src/BlazingPizza.Client/PizzaAuthenticationState.cs +++ b/src/BlazingPizza.Client/PizzaAuthenticationState.cs @@ -4,5 +4,5 @@ namespace BlazingPizza.Client; public class PizzaAuthenticationState : RemoteAuthenticationState { - public Order Order { get; set; } + public Order? Order { get; set; } } diff --git a/src/BlazingPizza.Client/Shared/AddressEditor.razor b/src/BlazingPizza.Client/Shared/AddressEditor.razor index 05e76410..fb36ff3a 100644 --- a/src/BlazingPizza.Client/Shared/AddressEditor.razor +++ b/src/BlazingPizza.Client/Shared/AddressEditor.razor @@ -47,5 +47,5 @@
      @code { - [Parameter] public Address Address { get; set; } -} + [Parameter, EditorRequired] public Address Address { get; set; } = new(); +} \ No newline at end of file diff --git a/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index f10c7990..87eb782b 100644 --- a/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -1,76 +1,84 @@ @inject HttpClient HttpClient -
      -

      @Pizza.Special.Name

      - @Pizza.Special.Description -
      -
      -
      - - - - @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice())) - -
      -
      - - @if (toppings == null) - { - - } - else if (Pizza.Toppings.Count >= 6) - { -
      (maximum reached)
      - } - else - { - + + @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice())) + +
      +
      + + @if (toppings is null) { - + } - - } -
      + else if (Pizza.Toppings.Count >= 6) + { +
      (maximum reached)
      + } + else + { + + } +
      -
      - @foreach (var topping in Pizza.Toppings) - { -
      - @topping.Topping.Name - @topping.Topping.GetFormattedPrice() - +
      + @foreach (var topping in Pizza.Toppings) + { + if (topping?.Topping is not null) + { +
      + @topping.Topping.Name + @topping.Topping.GetFormattedPrice() + +
      + } + }
      - } -
      - + + +
      + + + Price: @(Pizza.GetFormattedTotalPrice()) + + +
      -
      - - - Price: @(Pizza.GetFormattedTotalPrice()) - - -
      @code { - List toppings; + // toppings is only null while loading + List? toppings; - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnCancel { get; set; } - [Parameter] public EventCallback OnConfirm { get; set; } + // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } + [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } protected async override Task OnInitializedAsync() { - toppings = await HttpClient.GetFromJsonAsync("toppings", BlazingPizza.OrderContext.Default.ListTopping); + toppings = await HttpClient.GetFromJsonAsync>("toppings") ?? new(); } void ToppingSelected(ChangeEventArgs e) { - if (int.TryParse((string)e.Value, out var index) && index >= 0) + if (toppings is null) return; + if (int.TryParse((string?)e.Value, out var index) && index >= 0) { AddTopping(toppings[index]); } @@ -78,7 +86,7 @@ void AddTopping(Topping topping) { - if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null) + if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null) { Pizza.Toppings.Add(new PizzaTopping() { Topping = topping }); } diff --git a/src/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor b/src/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor index e0e9f41d..f7f7a31d 100644 --- a/src/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor +++ b/src/BlazingPizza.Client/Shared/ConfiguredPizzaItem.razor @@ -1,10 +1,10 @@ 
      x -
      @(Pizza.Size)" @Pizza.Special.Name
      +
      @(Pizza.Size)" @Pizza.Special?.Name
        @foreach (var topping in Pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      @@ -13,6 +13,6 @@
      @code { - [Parameter] public Pizza Pizza { get; set; } - [Parameter] public EventCallback OnRemoved { get; set; } + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); + [Parameter, EditorRequired] public EventCallback OnRemoved { get; set; } } \ No newline at end of file diff --git a/src/BlazingPizza.Client/Shared/LoginDisplay.razor b/src/BlazingPizza.Client/Shared/LoginDisplay.razor index 4d483eb9..e1d9367f 100644 --- a/src/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/src/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -9,7 +9,7 @@ diff --git a/src/BlazingPizza.Client/Shared/OrderReview.razor b/src/BlazingPizza.Client/Shared/OrderReview.razor index e1424934..56668721 100644 --- a/src/BlazingPizza.Client/Shared/OrderReview.razor +++ b/src/BlazingPizza.Client/Shared/OrderReview.razor @@ -3,7 +3,7 @@

      @(pizza.Size)" - @pizza.Special.Name + @pizza.Special?.Name (£@pizza.GetFormattedTotalPrice())

      @@ -11,7 +11,7 @@
        @foreach (var topping in pizza.Toppings) { -
      • + @topping.Topping.Name
      • +
      • + @topping.Topping?.Name
      • }
      } @@ -24,5 +24,5 @@

      @code { - [Parameter] public Order Order { get; set; } + [Parameter, EditorRequired] public Order Order { get; set; } = new(); } diff --git a/src/BlazingPizza.Client/_Imports.razor b/src/BlazingPizza.Client/_Imports.razor index b575d7f7..af8847ae 100644 --- a/src/BlazingPizza.Client/_Imports.razor +++ b/src/BlazingPizza.Client/_Imports.razor @@ -12,4 +12,4 @@ @using BlazingPizza.Client.Shared @using BlazingPizza.ComponentsLibrary @using BlazingPizza.ComponentsLibrary.Map -@using BlazingComponents +@using BlazingComponents \ No newline at end of file diff --git a/src/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj b/src/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj index cb996b1b..a907e880 100644 --- a/src/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj +++ b/src/BlazingPizza.ComponentsLibrary/BlazingPizza.ComponentsLibrary.csproj @@ -2,7 +2,8 @@ $(TargetFrameworkVersion) - true + true + enable diff --git a/src/BlazingPizza.ComponentsLibrary/Map/Map.razor b/src/BlazingPizza.ComponentsLibrary/Map/Map.razor index c2413ac4..4325755e 100644 --- a/src/BlazingPizza.ComponentsLibrary/Map/Map.razor +++ b/src/BlazingPizza.ComponentsLibrary/Map/Map.razor @@ -5,9 +5,9 @@ @code { string elementId = $"map-{Guid.NewGuid().ToString("D")}"; - + [Parameter] public double Zoom { get; set; } - [Parameter] public List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) { diff --git a/src/BlazingPizza.ComponentsLibrary/Map/Marker.cs b/src/BlazingPizza.ComponentsLibrary/Map/Marker.cs index b067719c..d4dcdbad 100644 --- a/src/BlazingPizza.ComponentsLibrary/Map/Marker.cs +++ b/src/BlazingPizza.ComponentsLibrary/Map/Marker.cs @@ -2,11 +2,11 @@ public class Marker { - public string Description { get; set; } + public string Description { get; set; } = string.Empty; public double X { get; set; } public double Y { get; set; } public bool ShowPopup { get; set; } -} +} \ No newline at end of file diff --git a/src/BlazingPizza.ComponentsLibrary/Map/Point.cs b/src/BlazingPizza.ComponentsLibrary/Map/Point.cs index 34194163..054e56ec 100644 --- a/src/BlazingPizza.ComponentsLibrary/Map/Point.cs +++ b/src/BlazingPizza.ComponentsLibrary/Map/Point.cs @@ -5,4 +5,5 @@ public class Point public double X { get; set; } public double Y { get; set; } -} \ No newline at end of file +} +// Compare this snippet from save-points\01-Components-and-layout\BlazingPizza.ComponentsLibrary\Map\Marker.cs: \ No newline at end of file diff --git a/src/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/src/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml index a1b61965..3f740c74 100644 --- a/src/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml +++ b/src/BlazingPizza.Server/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -17,7 +17,7 @@ {
      - @User.Identity.Name + @User?.Identity?.Name
      diff --git a/src/BlazingPizza.Server/BlazingPizza.Server.csproj b/src/BlazingPizza.Server/BlazingPizza.Server.csproj index 339bbf85..b028c771 100644 --- a/src/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/src/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -1,25 +1,26 @@  - - $(TargetFrameworkVersion) + + $(TargetFrameworkVersion) true - + enable + - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + diff --git a/src/BlazingPizza.Server/OrdersController.cs b/src/BlazingPizza.Server/OrdersController.cs index 3d2ba63f..adf19b41 100644 --- a/src/BlazingPizza.Server/OrdersController.cs +++ b/src/BlazingPizza.Server/OrdersController.cs @@ -23,7 +23,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -38,7 +38,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - .Where(o => o.UserId == GetUserId()) + .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -57,19 +57,19 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - order.UserId = GetUserId(); + order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up // new specials and toppings foreach (var pizza in order.Pizzas) { - pizza.SpecialId = pizza.Special.Id; + pizza.SpecialId = pizza.Special?.Id ?? 0; pizza.Special = null; foreach (var topping in pizza.Toppings) { - topping.ToppingId = topping.Topping.Id; + topping.ToppingId = topping.Topping?.Id ?? 0; topping.Topping = null; } } @@ -78,7 +78,7 @@ public async Task> PlaceOrder(Order order) await _db.SaveChangesAsync(); // In the background, send push notifications if possible - var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == GetUserId()).SingleOrDefaultAsync(); + var subscription = await _db.NotificationSubscriptions.Where(e => e.UserId == PizzaApiExtensions.GetUserId(HttpContext)).SingleOrDefaultAsync(); if (subscription != null) { _ = TrackAndSendNotificationsAsync(order, subscription); @@ -87,11 +87,6 @@ public async Task> PlaceOrder(Order order) return order.OrderId; } - private string GetUserId() - { - return HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); - } - private static async Task TrackAndSendNotificationsAsync(Order order, NotificationSubscription subscription) { // In a realistic case, some other backend process would track @@ -123,8 +118,8 @@ private static async Task SendNotificationAsync(Order order, NotificationSubscri await webPushClient.SendNotificationAsync(pushSubscription, payload, vapidDetails); } catch (Exception ex) - { + { Console.Error.WriteLine("Error sending push notification: " + ex.Message); } } -} +} \ No newline at end of file diff --git a/src/BlazingPizza.Server/PizzaApiExtensions.cs b/src/BlazingPizza.Server/PizzaApiExtensions.cs index 524ca759..d277597f 100644 --- a/src/BlazingPizza.Server/PizzaApiExtensions.cs +++ b/src/BlazingPizza.Server/PizzaApiExtensions.cs @@ -19,6 +19,10 @@ public static WebApplication MapPizzaApi(this WebApplication app) // We're storing at most one subscription per user, so delete old ones. // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. var userId = GetUserId(context); + if (userId is null) + { + return Results.Unauthorized(); + } var oldSubscriptions = db.NotificationSubscriptions.Where(e => e.UserId == userId); db.NotificationSubscriptions.RemoveRange(oldSubscriptions); @@ -49,9 +53,6 @@ public static WebApplication MapPizzaApi(this WebApplication app) } - private static string GetUserId(HttpContext context) - { - return context.User.FindFirstValue(ClaimTypes.NameIdentifier); - } + public static string? GetUserId(HttpContext context) => context.User.FindFirstValue(ClaimTypes.NameIdentifier); } \ No newline at end of file diff --git a/src/BlazingPizza.Server/Properties/launchSettings.json b/src/BlazingPizza.Server/Properties/launchSettings.json index c90e4fdb..f82b9563 100644 --- a/src/BlazingPizza.Server/Properties/launchSettings.json +++ b/src/BlazingPizza.Server/Properties/launchSettings.json @@ -11,19 +11,19 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" + } }, "BlazingPizza.Server": { "commandName": "Project", "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" + } } } } \ No newline at end of file diff --git a/src/BlazingPizza.Shared/Address.cs b/src/BlazingPizza.Shared/Address.cs index a671359a..15e546c1 100644 --- a/src/BlazingPizza.Shared/Address.cs +++ b/src/BlazingPizza.Shared/Address.cs @@ -5,20 +5,20 @@ public class Address public int Id { get; set; } [Required, MaxLength(100)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required, MaxLength(100)] - public string Line1 { get; set; } + public string Line1 { get; set; } = string.Empty; [MaxLength(100)] - public string Line2 { get; set; } + public string Line2 { get; set; } = string.Empty; [Required, MaxLength(50)] - public string City { get; set; } + public string City { get; set; } = string.Empty; [Required, MaxLength(20)] - public string Region { get; set; } + public string Region { get; set; } = string.Empty; [Required, MaxLength(20)] - public string PostalCode { get; set; } + public string PostalCode { get; set; } = string.Empty; } diff --git a/src/BlazingPizza.Shared/BlazingPizza.Shared.csproj b/src/BlazingPizza.Shared/BlazingPizza.Shared.csproj index fb17e53b..785c204e 100644 --- a/src/BlazingPizza.Shared/BlazingPizza.Shared.csproj +++ b/src/BlazingPizza.Shared/BlazingPizza.Shared.csproj @@ -1,17 +1,18 @@  - - $(TargetFrameworkVersion) - BlazingPizza + + $(TargetFrameworkVersion) + BlazingPizza true - + enable + - - - - + + + + diff --git a/src/BlazingPizza.Shared/NotificationSubscription.cs b/src/BlazingPizza.Shared/NotificationSubscription.cs index 5c99e3f4..16711e31 100644 --- a/src/BlazingPizza.Shared/NotificationSubscription.cs +++ b/src/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public int NotificationSubscriptionId { get; set; } + public required int NotificationSubscriptionId { get; set; } - public string UserId { get; set; } + public required string UserId { get; set; } - public string Url { get; set; } + public required string Url { get; set; } - public string P256dh { get; set; } + public required string P256dh { get; set; } - public string Auth { get; set; } + public required string Auth { get; set; } } diff --git a/src/BlazingPizza.Shared/Order.cs b/src/BlazingPizza.Shared/Order.cs index 180ec55b..f1211588 100644 --- a/src/BlazingPizza.Shared/Order.cs +++ b/src/BlazingPizza.Shared/Order.cs @@ -6,13 +6,15 @@ public class Order { public int OrderId { get; set; } - public string UserId { get; set; } + // Set by the server during POST + public string? UserId { get; set; } public DateTime CreatedTime { get; set; } public Address DeliveryAddress { get; set; } = new Address(); - public LatLong DeliveryLocation { get; set; } + // Set by server during POST + public LatLong? DeliveryLocation { get; set; } public List Pizzas { get; set; } = new List(); diff --git a/src/BlazingPizza.Shared/OrderWithStatus.cs b/src/BlazingPizza.Shared/OrderWithStatus.cs index fbfac7af..c0e3495a 100644 --- a/src/BlazingPizza.Shared/OrderWithStatus.cs +++ b/src/BlazingPizza.Shared/OrderWithStatus.cs @@ -9,16 +9,19 @@ public class OrderWithStatus public readonly static TimeSpan PreparationDuration = TimeSpan.FromSeconds(10); public readonly static TimeSpan DeliveryDuration = TimeSpan.FromMinutes(1); // Unrealistic, but more interesting to watch - public Order Order { get; set; } + // Set from DB + public Order Order { get; set; } = null!; - public string StatusText { get; set; } + // Set from Order + public string StatusText { get; set; } = null!; public bool IsDelivered => StatusText == "Delivered"; - public List MapMarkers { get; set; } + public List MapMarkers { get; set; } = null!; public static OrderWithStatus FromOrder(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // To simulate a real backend process, we fake status updates based on the amount // of time since the order was placed string statusText; @@ -65,6 +68,7 @@ public static OrderWithStatus FromOrder(Order order) private static LatLong ComputeStartPosition(Order order) { + ArgumentNullException.ThrowIfNull(order.DeliveryLocation); // Random but deterministic based on order ID var rng = new Random(order.OrderId); var distance = 0.01 + rng.NextDouble() * 0.02; diff --git a/src/BlazingPizza.Shared/Pizza.cs b/src/BlazingPizza.Shared/Pizza.cs index 2a7a1c29..150de954 100644 --- a/src/BlazingPizza.Shared/Pizza.cs +++ b/src/BlazingPizza.Shared/Pizza.cs @@ -15,22 +15,24 @@ public class Pizza public int OrderId { get; set; } - public PizzaSpecial Special { get; set; } + public PizzaSpecial? Special { get; set; } public int SpecialId { get; set; } public int Size { get; set; } - public List Toppings { get; set; } + public List Toppings { get; set; } = new(); public decimal GetBasePrice() { + if(Special == null) throw new NullReferenceException($"{nameof(Special)} was null when calculating Base Price."); return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice; } public decimal GetTotalPrice() { - return GetBasePrice() + Toppings.Sum(t => t.Topping.Price); + if (Toppings.Any(t => t.Topping is null)) throw new NullReferenceException($"{nameof(Toppings)} contained null when calculating the Total Price."); + return GetBasePrice() + Toppings.Sum(t => t.Topping!.Price); } public string GetFormattedTotalPrice() diff --git a/src/BlazingPizza.Shared/PizzaSpecial.cs b/src/BlazingPizza.Shared/PizzaSpecial.cs index 1fd9006a..d53ab286 100644 --- a/src/BlazingPizza.Shared/PizzaSpecial.cs +++ b/src/BlazingPizza.Shared/PizzaSpecial.cs @@ -7,13 +7,13 @@ public class PizzaSpecial { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal BasePrice { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string ImageUrl { get; set; } + public string ImageUrl { get; set; } = "img/pizzas/cheese.jpg"; public string GetFormattedBasePrice() => BasePrice.ToString("0.00"); } \ No newline at end of file diff --git a/src/BlazingPizza.Shared/PizzaTopping.cs b/src/BlazingPizza.Shared/PizzaTopping.cs index 1c062732..724f1c06 100644 --- a/src/BlazingPizza.Shared/PizzaTopping.cs +++ b/src/BlazingPizza.Shared/PizzaTopping.cs @@ -2,7 +2,7 @@ public class PizzaTopping { - public Topping Topping { get; set; } + public Topping? Topping { get; set; } public int ToppingId { get; set; } diff --git a/src/BlazingPizza.Shared/Topping.cs b/src/BlazingPizza.Shared/Topping.cs index 4eb67ae9..29b9e790 100644 --- a/src/BlazingPizza.Shared/Topping.cs +++ b/src/BlazingPizza.Shared/Topping.cs @@ -4,7 +4,7 @@ public class Topping { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } diff --git a/src/BlazingPizza.Shared/UserInfo.cs b/src/BlazingPizza.Shared/UserInfo.cs index babb4315..fb0045bf 100644 --- a/src/BlazingPizza.Shared/UserInfo.cs +++ b/src/BlazingPizza.Shared/UserInfo.cs @@ -4,6 +4,6 @@ public class UserInfo { public bool IsAuthenticated { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file From def0f14d86b2188aa1146122c801a9c73bd89c14 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Wed, 20 Sep 2023 17:34:46 -0400 Subject: [PATCH 35/53] Updated docs --- docs/04-refactor-state-management.md | 19 +++--- docs/05-checkout-with-validation.md | 36 +++++------ docs/06-authentication-and-authorization.md | 2 +- docs/07-javascript-interop.md | 2 +- docs/08-templated-components.md | 69 +++++++++++++-------- 5 files changed, 73 insertions(+), 55 deletions(-) diff --git a/docs/04-refactor-state-management.md b/docs/04-refactor-state-management.md index cb69d939..7fdf3f04 100644 --- a/docs/04-refactor-state-management.md +++ b/docs/04-refactor-state-management.md @@ -55,7 +55,7 @@ public class OrderState { public bool ShowingConfigureDialog { get; private set; } - public Pizza ConfiguringPizza { get; private set; } + public Pizza? ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); } @@ -84,13 +84,16 @@ public void CancelConfigurePizzaDialog() ShowingConfigureDialog = false; } -public void ConfirmConfigurePizzaDialog() -{ - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; - - ShowingConfigureDialog = false; -} + public void ConfirmConfigurePizzaDialog() + { + if (ConfiguringPizza is not null) + { + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; + } + + ShowingConfigureDialog = false; + } public void ResetOrder() { diff --git a/docs/05-checkout-with-validation.md b/docs/05-checkout-with-validation.md index fcc21439..da0aafad 100644 --- a/docs/05-checkout-with-validation.md +++ b/docs/05-checkout-with-validation.md @@ -113,7 +113,7 @@ The markup here is going to be a bit tedious, so you probably want to copy and p
      @code { - [Parameter] public Address Address { get; set; } + [Parameter, EditorRequired] public Address Address { get; set; } = new(); } ``` @@ -174,23 +174,23 @@ namespace BlazingPizza { public int Id { get; set; } - [Required, MaxLength(100)] - public string Name { get; set; } - - [Required, MaxLength(100)] - public string Line1 { get; set; } - - [MaxLength(100)] - public string Line2 { get; set; } - - [Required, MaxLength(50)] - public string City { get; set; } - - [Required, MaxLength(20)] - public string Region { get; set; } - - [Required, MaxLength(20)] - public string PostalCode { get; set; } + [Required, MaxLength(100)] + public string Name { get; set; } = string.Empty; + + [Required, MaxLength(100)] + public string Line1 { get; set; } = string.Empty; + + [MaxLength(100)] + public string Line2 { get; set; } = string.Empty; + + [Required, MaxLength(50)] + public string City { get; set; } = string.Empty; + + [Required, MaxLength(20)] + public string Region { get; set; } = string.Empty; + + [Required, MaxLength(20)] + public string PostalCode { get; set; } = string.Empty; } } ``` diff --git a/docs/06-authentication-and-authorization.md b/docs/06-authentication-and-authorization.md index 1a443c39..e5731eb8 100644 --- a/docs/06-authentication-and-authorization.md +++ b/docs/06-authentication-and-authorization.md @@ -123,7 +123,7 @@ To orchestrate the authentication flow, add an `Authentication` component to the @code{ [Parameter] - public string Action { get; set; } + public string Action { get; set; } = string.Empty; } ``` diff --git a/docs/07-javascript-interop.md b/docs/07-javascript-interop.md index af2e9b12..f435faf3 100644 --- a/docs/07-javascript-interop.md +++ b/docs/07-javascript-interop.md @@ -103,7 +103,7 @@ Add an async `RemovePizza` method to the `Index` component that calls the `Confi ```csharp async Task RemovePizza(Pizza configuredPizza) { - if (await JS.Confirm($"Remove {configuredPizza.Special.Name} pizza from the order?")) + if (await JS.Confirm($"Remove {configuredPizza.Special?.Name} pizza from the order?")) { OrderState.RemoveConfiguredPizza(configuredPizza); } diff --git a/docs/08-templated-components.md b/docs/08-templated-components.md index c48b3e55..7082f934 100644 --- a/docs/08-templated-components.md +++ b/docs/08-templated-components.md @@ -82,7 +82,7 @@ First, add a parameter called `ChildContent` of type `RenderFragment`. The name ```razor @code { - [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter, EditorRequired] public RenderFragment? ChildContent { get; set; } } ``` @@ -111,7 +111,7 @@ Next, to give this dialog some conditional behavior, let's add a parameter of ty } @code { - [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter, EditorRequired] public RenderFragment? ChildContent { get; set; } [Parameter] public bool Show { get; set; } } ``` @@ -219,13 +219,16 @@ Now that we've defined a generic type parameter we can use it in a parameter dec ```html @code { - IEnumerable items; + IEnumerable? items; - [Parameter] public Func>> Loader { get; set; } + [Parameter, EditorRequired] public Func>>? Loader { get; set; } protected override async Task OnParametersSetAsync() { - items = await Loader(); + if (Loader is not null) + { + items = await Loader(); + } } } ``` @@ -233,7 +236,7 @@ Now that we've defined a generic type parameter we can use it in a parameter dec Since we have the data, we can now add the structure of each of the states we need to handle. Add the following markup to `TemplatedList.razor`: ```html -@if (items == null) +@if (items is null) { } @@ -258,15 +261,15 @@ Now, these are our three states of the dialog, and we'd like to accept a content Here's an example of the three parameters to add: ```C# - [Parameter] public RenderFragment Loading{ get; set; } - [Parameter] public RenderFragment Empty { get; set; } - [Parameter] public RenderFragment Item { get; set; } + [Parameter] public RenderFragment? Loading { get; set; } + [Parameter] public RenderFragment? Empty { get; set; } + [Parameter, EditorRequired] public RenderFragment? Item { get; set; } ``` Now that we have some `RenderFragment` parameters, we can start using them. Update the markup we created earlier to plug in the correct parameter in each place. ```html -@if (items == null) +@if (items is null) { @Loading } @@ -280,7 +283,10 @@ else @foreach (var item in items) {
      - @Item(item) + @if (Item is not null) + { + @Item(item) + }
      }
      @@ -295,17 +301,20 @@ Let's add another `string` parameter, and finally the functions block of `Templa ```html @code { - IEnumerable items; + IEnumerable? items; - [Parameter] public Func>> Loader { get; set; } - [Parameter] public RenderFragment Loading { get; set; } - [Parameter] public RenderFragment Empty { get; set; } - [Parameter] public RenderFragment Item { get; set; } - [Parameter] public string ListGroupClass { get; set; } + [Parameter, EditorRequired] public Func>>? Loader { get; set; } + [Parameter] public RenderFragment? Loading { get; set; } + [Parameter] public RenderFragment? Empty { get; set; } + [Parameter, EditorRequired] public RenderFragment? Item { get; set; } + [Parameter] public string? ListGroupClass { get; set; } protected override async Task OnParametersSetAsync() { - items = await Loader(); + if (Loader is not null) + { + items = await Loader(); + } } } ``` @@ -315,7 +324,7 @@ Lastly update the `
      ` to contain `
      - @Item(item) + @if (Item is not null) + { + @Item(item) + }
      }
      } @code { - IEnumerable items; + IEnumerable? items; - [Parameter] public Func>> Loader { get; set; } - [Parameter] public RenderFragment Loading { get; set; } - [Parameter] public RenderFragment Empty { get; set; } - [Parameter] public RenderFragment Item { get; set; } - [Parameter] public string ListGroupClass { get; set; } + [Parameter, EditorRequired] public Func>>? Loader { get; set; } + [Parameter] public RenderFragment? Loading { get; set; } + [Parameter] public RenderFragment? Empty { get; set; } + [Parameter, EditorRequired] public RenderFragment? Item { get; set; } + [Parameter] public string? ListGroupClass { get; set; } protected override async Task OnParametersSetAsync() { - items = await Loader(); + if (Loader is not null) + { + items = await Loader(); + } } } ``` From 7747b65a6b8e283abd822c884f7a6b92cbef4e36 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Wed, 20 Sep 2023 17:53:16 -0400 Subject: [PATCH 36/53] Notification interop doesn't work with required. --- .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ .../BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ src/BlazingPizza.Shared/NotificationSubscription.cs | 12 ++++++------ 11 files changed, 66 insertions(+), 66 deletions(-) diff --git a/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/00-get-started/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/03-show-order-status/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/04-refactor-state-management/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/04-refactor-state-management/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/04-refactor-state-management/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/07-javascript-interop/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/08-templated-components/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/08-templated-components/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/08-templated-components/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/08-templated-components/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs b/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs +++ b/save-points/09-progressive-web-app/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file diff --git a/src/BlazingPizza.Shared/NotificationSubscription.cs b/src/BlazingPizza.Shared/NotificationSubscription.cs index 16711e31..7f1ea4d9 100644 --- a/src/BlazingPizza.Shared/NotificationSubscription.cs +++ b/src/BlazingPizza.Shared/NotificationSubscription.cs @@ -2,13 +2,13 @@ public class NotificationSubscription { - public required int NotificationSubscriptionId { get; set; } + public int? NotificationSubscriptionId { get; set; } - public required string UserId { get; set; } + public string? UserId { get; set; } - public required string Url { get; set; } + public string? Url { get; set; } - public required string P256dh { get; set; } + public string? P256dh { get; set; } - public required string Auth { get; set; } -} + public string? Auth { get; set; } +} \ No newline at end of file From 7195eb27c2e6f63c8fd45cdc680f4022fb12350b Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Wed, 20 Sep 2023 18:00:33 -0400 Subject: [PATCH 37/53] Extra sln file --- .../BlazingPizza.Client.sln | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.sln diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.sln b/save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.sln deleted file mode 100644 index 44cb21ca..00000000 --- a/save-points/07-javascript-interop/BlazingPizza.Client/BlazingPizza.Client.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazingPizza.Client", "BlazingPizza.Client.csproj", "{0E9CDAD8-E27C-4B5A-A189-D8D5E3D2C283}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0E9CDAD8-E27C-4B5A-A189-D8D5E3D2C283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0E9CDAD8-E27C-4B5A-A189-D8D5E3D2C283}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0E9CDAD8-E27C-4B5A-A189-D8D5E3D2C283}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0E9CDAD8-E27C-4B5A-A189-D8D5E3D2C283}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {083D1F98-DE33-460B-B0AF-C71ADD95944F} - EndGlobalSection -EndGlobal From a2e5a478eab5c600e35d2c2081453fc9d8acc659 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 12:44:12 -0400 Subject: [PATCH 38/53] Fixed link that moved for getting started --- docs/00-get-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/00-get-started.md b/docs/00-get-started.md index 40a4616c..20e6b57c 100644 --- a/docs/00-get-started.md +++ b/docs/00-get-started.md @@ -4,7 +4,7 @@ In this session, you'll setup your machine for Blazor development and build your ## Setup -To get started with Blazor, follow the getting instructions on [blazor.net](https://blazor.net). +To get started with Blazor, follow the getting instructions on [blazor.net](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/intro). ## Build your first app From 175f4383c81df5bb9becfb50ee6d317db7fd38ad Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 13:20:43 -0400 Subject: [PATCH 39/53] Removed extra comments. --- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- .../BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor | 2 -- 9 files changed, 18 deletions(-) diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index d1788226..01662935 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/02-customize-a-pizza/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -64,10 +64,8 @@
      @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index fa52ca09..e643d7b3 100644 --- a/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/03-show-order-status/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -64,10 +64,8 @@
      @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index fa52ca09..e643d7b3 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/04-refactor-state-management/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -64,10 +64,8 @@
      @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index fa52ca09..e643d7b3 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -64,10 +64,8 @@
      @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index fa52ca09..e643d7b3 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -64,10 +64,8 @@
      @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index fa52ca09..e643d7b3 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -64,10 +64,8 @@
      @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 87eb782b..47b708c6 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -62,10 +62,8 @@ @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 87eb782b..47b708c6 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -62,10 +62,8 @@ @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } diff --git a/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor b/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor index 87eb782b..47b708c6 100644 --- a/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor +++ b/src/BlazingPizza.Client/Shared/ConfigurePizzaDialog.razor @@ -62,10 +62,8 @@ @code { - // toppings is only null while loading List? toppings; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnCancel { get; set; } [Parameter, EditorRequired] public EventCallback OnConfirm { get; set; } From c26f1afb8aad9d0d29525b704cea496e273ca13c Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 13:20:46 -0400 Subject: [PATCH 40/53] Clairified the message about EditorRequired. --- docs/02-customize-a-pizza.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/02-customize-a-pizza.md b/docs/02-customize-a-pizza.md index 527e0ea6..ef4a674a 100644 --- a/docs/02-customize-a-pizza.md +++ b/docs/02-customize-a-pizza.md @@ -67,12 +67,13 @@ Add a *ConfigurePizzaDialog.razor* file under the *Shared* directory. Since this > Note: In Visual Studio, you can right-click the *Shared* directory in Solution Explorer, then choose *Add* -> *New Item* to use the *Razor Component* item template to add a new Razor component. -The `ConfigurePizzaDialog` should have a `Pizza` parameter that specifies the pizza being configured. Component parameters are defined by adding a writable property to the component decorated with the `[Parameter]` attribute. The `[EditorRequired]` attribute is also added to the `Pizza` parameter because the component requires parameter to be set in order to function. Add a `@code` block to the `ConfigurePizzaDialog` with the following `Pizza` parameter: +The `ConfigurePizzaDialog` should have a `Pizza` parameter that specifies the pizza being configured. Component parameters are defined by adding a writable property to the component decorated with the `[Parameter]` attribute. Because the `Pizza` parameter requires a value for the component to function, the `[EditorRequired]` attribute is also added. By adding the `[EditorRequired]` attribute, if a parameter value isn't provided, editors or build tools may display warnings to the user. + +Add a `@code` block to the `ConfigurePizzaDialog` with the following `Pizza` parameter: ```csharp @code { - // A new Pizza instance is created by ShowConfigurePizzaDialog before the component is shown. - [Parameter, EditorRequired] public Pizza Pizza { get; set; } = default!; + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); } ``` From e7fb7678171bc4a151d310f90e7779d69d4406ef Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 13:28:26 -0400 Subject: [PATCH 41/53] Trimmed whitespace --- docs/04-refactor-state-management.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/04-refactor-state-management.md b/docs/04-refactor-state-management.md index 7fdf3f04..77b2020b 100644 --- a/docs/04-refactor-state-management.md +++ b/docs/04-refactor-state-management.md @@ -84,17 +84,17 @@ public void CancelConfigurePizzaDialog() ShowingConfigureDialog = false; } - public void ConfirmConfigurePizzaDialog() +public void ConfirmConfigurePizzaDialog() +{ + if (ConfiguringPizza is not null) { - if (ConfiguringPizza is not null) - { - Order.Pizzas.Add(ConfiguringPizza); - ConfiguringPizza = null; - } - - ShowingConfigureDialog = false; + Order.Pizzas.Add(ConfiguringPizza); + ConfiguringPizza = null; } + ShowingConfigureDialog = false; +} + public void ResetOrder() { Order = new Order(); From 59125da5ea34de26de34c2678207ab6fe1333721 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 13:35:04 -0400 Subject: [PATCH 42/53] Supressed nullable warning on change event --- docs/02-customize-a-pizza.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/02-customize-a-pizza.md b/docs/02-customize-a-pizza.md index ef4a674a..d2e69c9c 100644 --- a/docs/02-customize-a-pizza.md +++ b/docs/02-customize-a-pizza.md @@ -147,7 +147,7 @@ If you wanted to implement two-way binding manually, you could do so by combinin max="@Pizza.MaximumSize" step="1" value="@Pizza.Size" - @onchange="@((ChangeEventArgs e) => Pizza.Size = int.Parse((string) e.Value))" /> + @onchange="@((ChangeEventArgs e) => Pizza.Size = int.Parse((string?) e.Value))" /> ``` In Blazor you can use the `@bind` directive attribute to specify a two-way binding with this same behavior. The equivalent markup using `@bind` looks like this: @@ -185,7 +185,6 @@ The user should also be able to select additional toppings on `ConfigurePizzaDia // toppings is only null while loading List toppings = null!; - // A new Pizza is constructed by ShowConfigurePizzaDialog before the component is shown. [Parameter, EditorRequired] public Pizza Pizza { get; set; } = default!; protected async override Task OnInitializedAsync() @@ -250,6 +249,7 @@ void ToppingSelected(ChangeEventArgs e) void AddTopping(Topping topping) { + if (toppings is null) return; if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null) { Pizza.Toppings.Add(new PizzaTopping() { Topping = topping }); @@ -333,9 +333,12 @@ In the `Index` component add an event handler for the `OnConfirm` event that add ```csharp void ConfirmConfigurePizzaDialog() { - order.Pizzas.Add(configuringPizza); - configuringPizza = null; - + if (configuringPizza is not null) + { + order.Pizzas.Add(configuringPizza); + configuringPizza = null; + } + showingConfigureDialog = false; } ``` @@ -364,8 +367,7 @@ Create a new `ConfiguredPizzaItem` component for displaying a configured pizza.
      @code { - // Set by Order.Pizzas which is never null - [Parameter, EditorRequired] public Pizza Pizza { get; set; } = null!; + [Parameter, EditorRequired] public Pizza Pizza { get; set; } = new(); [Parameter, EditorRequired] public EventCallback OnRemoved { get; set; } } ``` From 5c1c94a80d75b8ed176feeee06c50876f399333a Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 13:35:47 -0400 Subject: [PATCH 43/53] Added EditorRequired to step --- docs/05-checkout-with-validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/05-checkout-with-validation.md b/docs/05-checkout-with-validation.md index da0aafad..7c5b54d6 100644 --- a/docs/05-checkout-with-validation.md +++ b/docs/05-checkout-with-validation.md @@ -63,7 +63,7 @@ Create a new component in the `BlazingPizza.Client` project's `Shared` folder ca ```razor @code { - [Parameter] public Address Address { get; set; } + [Parameter, EditorRequired] public Address Address { get; set; } = new(); } ``` From 6fe4eef05cabc086c13ea93c0db68ada99640698 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 16:36:40 -0400 Subject: [PATCH 44/53] Refactored GetUserId. This code was duplicated in multiple places. --- docs/06-authentication-and-authorization.md | 68 ++++++++++--------- .../BlazingPizza.Server/OrdersController.cs | 6 +- .../BlazingPizza.Server/OrdersController.cs | 6 +- .../BlazingPizza.Server/OrdersController.cs | 6 +- .../BlazingPizza.Server/OrdersController.cs | 6 +- .../BlazingPizza.Server/OrdersController.cs | 6 +- .../BlazingPizza.Server/OrdersController.cs | 6 +- .../BlazingPizza.Server/OrdersController.cs | 2 +- 8 files changed, 56 insertions(+), 50 deletions(-) diff --git a/docs/06-authentication-and-authorization.md b/docs/06-authentication-and-authorization.md index e5731eb8..4bde186b 100644 --- a/docs/06-authentication-and-authorization.md +++ b/docs/06-authentication-and-authorization.md @@ -232,40 +232,35 @@ To create the strongly typed client, add a new `OrdersClient` class to the clien *BlazingPizza.Client/OrdersClient.cs* ```csharp -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Net.Http.Json; -using System.Threading.Tasks; -namespace BlazingPizza.Client +namespace BlazingPizza.Client; + +public class OrdersClient { - public class OrdersClient - { - private readonly HttpClient httpClient; + private readonly HttpClient httpClient; - public OrdersClient(HttpClient httpClient) - { - this.httpClient = httpClient; - } + public OrdersClient(HttpClient httpClient) + { + this.httpClient = httpClient; + } - public async Task> GetOrders() => - await httpClient.GetFromJsonAsync>("orders"); + public async Task> GetOrders() => + await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus) ?? new(); - public async Task GetOrder(int orderId) => - await httpClient.GetFromJsonAsync($"orders/{orderId}"); + public async Task GetOrder(int orderId) => + await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus) ?? new(); - public async Task PlaceOrder(Order order) - { - var response = await httpClient.PostAsJsonAsync("orders", order); - response.EnsureSuccessStatusCode(); - var orderId = await response.Content.ReadFromJsonAsync(); - return orderId; - } + public async Task PlaceOrder(Order order) + { + var response = await httpClient.PostAsJsonAsync("orders", order, OrderContext.Default.Order); + response.EnsureSuccessStatusCode(); + var orderId = await response.Content.ReadFromJsonAsync(); + return orderId; } + } ``` @@ -295,6 +290,7 @@ We have already written this context for you and it is located in the `BlazingPi [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Topping))] +[JsonSerializable(typeof(Dictionary))] public partial class OrderContext : JsonSerializerContext {} ``` @@ -302,17 +298,19 @@ You can now optimize the calls to the HttpClient in the `OrdersClient` class by ```csharp public async Task> GetOrders() => - await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus); + await httpClient.GetFromJsonAsync("orders", OrderContext.Default.ListOrderWithStatus) ?? new(); + public async Task GetOrder(int orderId) => - await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus); + await httpClient.GetFromJsonAsync($"orders/{orderId}", OrderContext.Default.OrderWithStatus) ?? new(); + public async Task PlaceOrder(Order order) { - var response = await httpClient.PostAsJsonAsync("orders", order, OrderContext.Default.Order); - response.EnsureSuccessStatusCode(); - var orderId = await response.Content.ReadFromJsonAsync(); - return orderId; + var response = await httpClient.PostAsJsonAsync("orders", order, OrderContext.Default.Order); + response.EnsureSuccessStatusCode(); + var orderId = await response.Content.ReadFromJsonAsync(); + return orderId; } ``` @@ -369,7 +367,15 @@ private async void PollForUpdates() { orderWithStatus = await OrdersClient.GetOrder(OrderId); StateHasChanged(); - await Task.Delay(4000); + + if (orderWithStatus.IsDelivered) + { + pollingCancellationToken.Cancel(); + } + else + { + await Task.Delay(4000); + } } catch (AccessTokenNotAvailableException ex) { diff --git a/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs b/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs index a0a99b09..9e78e746 100644 --- a/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs +++ b/save-points/00-get-started/BlazingPizza.Server/OrdersController.cs @@ -20,7 +20,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -35,7 +35,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -54,7 +54,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - // order.UserId = GetUserId(); + // order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up diff --git a/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs b/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs index a0a99b09..9e78e746 100644 --- a/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs +++ b/save-points/01-Components-and-layout/BlazingPizza.Server/OrdersController.cs @@ -20,7 +20,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -35,7 +35,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -54,7 +54,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - // order.UserId = GetUserId(); + // order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up diff --git a/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs b/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs index a0a99b09..9e78e746 100644 --- a/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs +++ b/save-points/02-customize-a-pizza/BlazingPizza.Server/OrdersController.cs @@ -20,7 +20,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -35,7 +35,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -54,7 +54,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - // order.UserId = GetUserId(); + // order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up diff --git a/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs b/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs index a0a99b09..9e78e746 100644 --- a/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs +++ b/save-points/03-show-order-status/BlazingPizza.Server/OrdersController.cs @@ -20,7 +20,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -35,7 +35,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -54,7 +54,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - // order.UserId = GetUserId(); + // order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up diff --git a/save-points/04-refactor-state-management/BlazingPizza.Server/OrdersController.cs b/save-points/04-refactor-state-management/BlazingPizza.Server/OrdersController.cs index a0a99b09..9e78e746 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Server/OrdersController.cs +++ b/save-points/04-refactor-state-management/BlazingPizza.Server/OrdersController.cs @@ -20,7 +20,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -35,7 +35,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -54,7 +54,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - // order.UserId = GetUserId(); + // order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs b/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs index a0a99b09..9e78e746 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs +++ b/save-points/05-checkout-with-validation/BlazingPizza.Server/OrdersController.cs @@ -20,7 +20,7 @@ public OrdersController(PizzaStoreContext db) public async Task>> GetOrders() { var orders = await _db.Orders - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -35,7 +35,7 @@ public async Task> GetOrderWithStatus(int orderId) { var order = await _db.Orders .Where(o => o.OrderId == orderId) - // .Where(o => o.UserId == GetUserId()) + // .Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) .Include(o => o.DeliveryLocation) .Include(o => o.Pizzas).ThenInclude(p => p.Special) .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping) @@ -54,7 +54,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - // order.UserId = GetUserId(); + // order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs index c80b1c34..c98f4aee 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Server/OrdersController.cs @@ -59,7 +59,7 @@ public async Task> PlaceOrder(Order order) { order.CreatedTime = DateTime.Now; order.DeliveryLocation = new LatLong(51.5001, -0.1239); - order.UserId = PizzaApiExtensions.GetUserId(HttpContext); + order.UserId = PizzaApiExtensions.GetUserId(HttpContext); // Enforce existence of Pizza.SpecialId and Topping.ToppingId // in the database - prevent the submitter from making up From 51ea18c23419ffbbe7a258b1bb23a578013b01bc Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 16:40:52 -0400 Subject: [PATCH 45/53] Updated `!=` to `is not` null --- docs/06-authentication-and-authorization.md | 10 +++++----- .../BlazingPizza.Client/Pages/Authentication.razor | 2 +- .../BlazingPizza.Client/Pages/Authentication.razor | 2 +- .../BlazingPizza.Client/Pages/Authentication.razor | 2 +- .../BlazingPizza.Client/Pages/Authentication.razor | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/06-authentication-and-authorization.md b/docs/06-authentication-and-authorization.md index 4bde186b..c83adb45 100644 --- a/docs/06-authentication-and-authorization.md +++ b/docs/06-authentication-and-authorization.md @@ -402,7 +402,7 @@ To verify this, place an order while signed in with one account. Then sign out a This is easily fixed. Back in the `OrdersController` code, look for the commented-out line in `PlaceOrder`, and uncomment it: ```cs -order.UserId = GetUserId(); +order.UserId = PizzaApiExtensions.GetUserId(HttpContext); ``` Now each order will be stamped with the ID of the user who owns it. @@ -410,7 +410,7 @@ Now each order will be stamped with the ID of the user who owns it. Next look for the commented-out `.Where` lines in `GetOrders` and `GetOrderWithStatus`, and uncomment both. These lines ensure that users can only retrieve details of their own orders: ```csharp -.Where(o => o.UserId == GetUserId()) +.Where(o => o.UserId == PizzaApiExtensions.GetUserId(HttpContext)) ``` Now if you run the app again, you'll no longer be able to see the existing order details, because they aren't associated with your user ID. If you place a new order with one account, you won't be able to see it from a different account. That makes the application much more useful. @@ -532,7 +532,7 @@ To define the state that we want persisted, add a `PizzaAuthenticationState` cla ```csharp public class PizzaAuthenticationState : RemoteAuthenticationState { - public Order Order { get; set; } + public Order? Order { get; set; } } ``` @@ -559,7 +559,7 @@ Now we need to add logic to persist the current order, and then reestablish the Action="@Action" /> @code{ - [Parameter] public string Action { get; set; } + [Parameter] public string Action { get; set; } = string.Empty; public PizzaAuthenticationState RemoteAuthenticationState { get; set; } = new PizzaAuthenticationState(); @@ -574,7 +574,7 @@ Now we need to add logic to persist the current order, and then reestablish the private void RestorePizza(PizzaAuthenticationState pizzaState) { - if (pizzaState.Order != null) + if (pizzaState.Order is not null) { OrderState.ReplaceOrder(pizzaState.Order); } diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor index c2f633e1..7fbca1da 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor @@ -24,7 +24,7 @@ private void RestorePizza(PizzaAuthenticationState pizzaState) { - if (pizzaState.Order != null) + if (pizzaState.Order is not null) { OrderState.ReplaceOrder(pizzaState.Order); } diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor index c2f633e1..7fbca1da 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor @@ -24,7 +24,7 @@ private void RestorePizza(PizzaAuthenticationState pizzaState) { - if (pizzaState.Order != null) + if (pizzaState.Order is not null) { OrderState.ReplaceOrder(pizzaState.Order); } diff --git a/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor b/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor index c2f633e1..7fbca1da 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor @@ -24,7 +24,7 @@ private void RestorePizza(PizzaAuthenticationState pizzaState) { - if (pizzaState.Order != null) + if (pizzaState.Order is not null) { OrderState.ReplaceOrder(pizzaState.Order); } diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor index c2f633e1..7fbca1da 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor @@ -24,7 +24,7 @@ private void RestorePizza(PizzaAuthenticationState pizzaState) { - if (pizzaState.Order != null) + if (pizzaState.Order is not null) { OrderState.ReplaceOrder(pizzaState.Order); } From 63efdf49b9946e573724ee03286509ee9bed6669 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 17:05:44 -0400 Subject: [PATCH 46/53] Fixes: SignoutSessionStateManager obsolete warning. See: https://learn.microsoft.com/en-us/dotnet/core/compatibility/aspnet-core/7.0/wasm-app-authentication --- docs/06-authentication-and-authorization.md | 10 ++++------ .../BlazingPizza.Client/Shared/LoginDisplay.razor | 6 ++---- .../BlazingPizza.Client/Shared/RedirectToLogin.razor | 2 +- .../BlazingPizza.Client/Shared/LoginDisplay.razor | 8 +++----- .../BlazingPizza.Client/Shared/RedirectToLogin.razor | 2 +- .../BlazingPizza.Client/Shared/LoginDisplay.razor | 8 +++----- .../BlazingPizza.Client/Shared/RedirectToLogin.razor | 2 +- .../BlazingPizza.Client/Shared/LoginDisplay.razor | 8 +++----- .../BlazingPizza.Client/Shared/RedirectToLogin.razor | 2 +- 9 files changed, 19 insertions(+), 29 deletions(-) diff --git a/docs/06-authentication-and-authorization.md b/docs/06-authentication-and-authorization.md index c83adb45..dee991f9 100644 --- a/docs/06-authentication-and-authorization.md +++ b/docs/06-authentication-and-authorization.md @@ -149,7 +149,6 @@ Create a new component called `LoginDisplay` in the client project's `Shared` fo ```html @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager @code{ - async Task BeginSignOut() + void BeginSignOut() { - await SignOutManager.SetSignOutState(); - Navigation.NavigateTo("authentication/logout"); + Navigation.NavigateToLogout("authentication/logout"); } } ``` @@ -451,7 +449,7 @@ Next, to make the router respect such attributes, update *App.razor* to render a ``` -The `AuthorizeRouteView` will route navigations to the correct component, but only if the user is authorized. If the user is not authorized, the `NotAuthorized` content is displayed. You can also specify content to display while the `AuthorizeRouteView` is determining if the user is authorized. +The `AuthorizeRouteView` will route navigation to the correct component, but only if the user is authorized. If the user is not authorized, the `NotAuthorized` content is displayed. You can also specify content to display while the `AuthorizeRouteView` is determining if the user is authorized. Now when you try to navigate to the checkout page while signed out, you see the `NotAuthorized` content we setup in *App.razor*. @@ -466,7 +464,7 @@ Instead of telling the user they are unauthorized it would be better if we redir @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}"); + Navigation.NavigateToLogin($"authentication/login?returnUrl={Navigation.Uri}"); } } ``` diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor index e1d9367f..e442a897 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -1,5 +1,4 @@ @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager @code{ - async Task BeginSignOut() + void BeginSignOut() { - await SignOutManager.SetSignOutState(); - Navigation.NavigateTo("authentication/logout"); + Navigation.NavigateToLogout("authentication/logout"); } } \ No newline at end of file diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/RedirectToLogin.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/RedirectToLogin.razor index 16c4bea2..696c387d 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/RedirectToLogin.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Shared/RedirectToLogin.razor @@ -2,6 +2,6 @@ @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}"); + Navigation.NavigateToLogin($"authentication/login"); } } \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/LoginDisplay.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/LoginDisplay.razor index e1d9367f..b5c5bfa7 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -1,5 +1,4 @@ @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager -@code{ - async Task BeginSignOut() +@code { + void BeginSignOut() { - await SignOutManager.SetSignOutState(); - Navigation.NavigateTo("authentication/logout"); + Navigation.NavigateToLogout("authentication/logout"); } } \ No newline at end of file diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/RedirectToLogin.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/RedirectToLogin.razor index 16c4bea2..696c387d 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Shared/RedirectToLogin.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Shared/RedirectToLogin.razor @@ -2,6 +2,6 @@ @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}"); + Navigation.NavigateToLogin($"authentication/login"); } } \ No newline at end of file diff --git a/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor b/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor index e1d9367f..b5c5bfa7 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -1,5 +1,4 @@ @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager -@code{ - async Task BeginSignOut() +@code { + void BeginSignOut() { - await SignOutManager.SetSignOutState(); - Navigation.NavigateTo("authentication/logout"); + Navigation.NavigateToLogout("authentication/logout"); } } \ No newline at end of file diff --git a/save-points/08-templated-components/BlazingPizza.Client/Shared/RedirectToLogin.razor b/save-points/08-templated-components/BlazingPizza.Client/Shared/RedirectToLogin.razor index 16c4bea2..696c387d 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Shared/RedirectToLogin.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Shared/RedirectToLogin.razor @@ -2,6 +2,6 @@ @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}"); + Navigation.NavigateToLogin($"authentication/login"); } } \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor index e1d9367f..b5c5bfa7 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -1,5 +1,4 @@ @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager -@code{ - async Task BeginSignOut() +@code { + void BeginSignOut() { - await SignOutManager.SetSignOutState(); - Navigation.NavigateTo("authentication/logout"); + Navigation.NavigateToLogout("authentication/logout"); } } \ No newline at end of file diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/RedirectToLogin.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/RedirectToLogin.razor index 16c4bea2..696c387d 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/RedirectToLogin.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Shared/RedirectToLogin.razor @@ -2,6 +2,6 @@ @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}"); + Navigation.NavigateToLogin($"authentication/login"); } } \ No newline at end of file From b11af38dd94194f34868feabcd178595367bb12d Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 17:08:18 -0400 Subject: [PATCH 47/53] Added Nullable to orderWithStatus --- docs/03-show-order-status.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/03-show-order-status.md b/docs/03-show-order-status.md index d4dbba41..e8b1bc00 100644 --- a/docs/03-show-order-status.md +++ b/docs/03-show-order-status.md @@ -105,7 +105,7 @@ Then add a `@code` block that makes an asynchronous request for the data we need ```csharp @code { - IEnumerable ordersWithStatus; + IEnumerable? ordersWithStatus; protected override async Task OnParametersSetAsync() { From f0bfc8731ef892d8cef295c5bd12c2415827238a Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 17:20:15 -0400 Subject: [PATCH 48/53] Updated Razor Class Library verbage, it was reminicent of .NET Core 3.0/3.1 --- docs/08-templated-components.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/08-templated-components.md b/docs/08-templated-components.md index 7082f934..06aab3a9 100644 --- a/docs/08-templated-components.md +++ b/docs/08-templated-components.md @@ -35,23 +35,28 @@ It looks like: - netstandard2.0 - 3.0 + net{versionNumber} + enable + enable - - + + + + + + ``` There are a few things here worth understanding. -Firstly, the package targets `netstandard2.0`. Blazor Server uses `netcoreapp3.1` and Blazor WebAssembly uses `netstandard2.1` - so targeting `netstandard2.0` means that it will work for either scenario. +Firstly, the package targets a version of .NET, ex: `net7.0`. -Additional, the `3.0` sets the Razor language version. Version 3 is needed to support components and the `.razor` file extension. +Additional, the `3.0` identifies the supported platforms. This value is understood by the compatibility analyzer. Client-side apps target the full .NET API surface area, but not all .NET APIs are supported on WebAssembly due to browser sandbox constraints. Unsupported APIs throw PlatformNotSupportedException when running on WebAssembly. A platform compatibility analyzer warns the developer when the app uses APIs that aren't supported by the app's target platforms. Lastly the `` element adds a package references to the Blazor component model. From d551aedf7476698a3cf5604c713434e05e67f2ac Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 17:23:08 -0400 Subject: [PATCH 49/53] Changed `!=` to `is not null` --- docs/09-progressive-web-app.md | 2 +- .../BlazingPizza.Client/Pages/Index.razor | 2 +- .../BlazingPizza.Client/Pages/Index.razor | 2 +- .../BlazingPizza.Client/Pages/Index.razor | 2 +- .../BlazingPizza.Client/Pages/Index.razor | 2 +- .../BlazingPizza.Client/Pages/Index.razor | 2 +- .../BlazingPizza.Client/Pages/Checkout.razor | 8 ++++---- .../BlazingPizza.Client/Pages/Index.razor | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/09-progressive-web-app.md b/docs/09-progressive-web-app.md index fdb27990..44ba41c7 100644 --- a/docs/09-progressive-web-app.md +++ b/docs/09-progressive-web-app.md @@ -126,7 +126,7 @@ You'll then need to define `RequestNotificationSubscriptionAsync`. Add this else async Task RequestNotificationSubscriptionAsync() { var subscription = await JSRuntime.InvokeAsync("blazorPushNotifications.requestSubscription"); - if (subscription != null) + if (subscription is not null) { try { diff --git a/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor b/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor index fead86ef..8d333900 100644 --- a/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/04-refactor-state-management/BlazingPizza.Client/Pages/Index.razor @@ -5,7 +5,7 @@
        - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor index efded2d6..1c72c094 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor @@ -5,7 +5,7 @@
          - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Index.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Index.razor index efded2d6..1c72c094 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Index.razor @@ -5,7 +5,7 @@
            - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor index 76549da9..58c8e82c 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Index.razor @@ -6,7 +6,7 @@
              - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { diff --git a/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor b/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor index 3c1c882c..43dde123 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Pages/Index.razor @@ -6,7 +6,7 @@
                - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor index f3c3a0fe..53e45fe9 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Checkout.razor @@ -41,7 +41,7 @@ async Task RequestNotificationSubscriptionAsync() { var subscription = await JSRuntime.InvokeAsync("blazorPushNotifications.requestSubscription"); - if (subscription != null) + if (subscription is not null) { try { @@ -61,9 +61,9 @@ try { var newOrderId = await OrdersClient.PlaceOrder(OrderState.Order); - OrderState.ResetOrder(); - NavigationManager.NavigateTo($"myorders/{newOrderId}"); - } + OrderState.ResetOrder(); + NavigationManager.NavigateTo($"myorders/{newOrderId}"); + } catch (AccessTokenNotAvailableException ex) { ex.Redirect(); diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor index 3c1c882c..43dde123 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor @@ -6,7 +6,7 @@
                  - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { From 474c164a51cf83897a025bb52ba441138e31874c Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 17:31:33 -0400 Subject: [PATCH 50/53] The app was updated to include a Minimal API implementation of NotificationsController. However the code in save-point/09 and instructions were not updated. This fixes the issue. --- docs/09-progressive-web-app.md | 2 +- .../NotificationsController.cs | 43 ------------------- 2 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs diff --git a/docs/09-progressive-web-app.md b/docs/09-progressive-web-app.md index 44ba41c7..714a407f 100644 --- a/docs/09-progressive-web-app.md +++ b/docs/09-progressive-web-app.md @@ -164,7 +164,7 @@ To try this out, start placing an order and go to the checkout screen. You shoul ![image](https://user-images.githubusercontent.com/1101362/66354176-eed8eb80-e95b-11e9-9799-b4eba6410971.png) -Choose *Allow* and check in the browser dev console that it didn't cause any errors. If you want, set a breakpoint on the server in `NotificationsController`'s `Subscribe` action method, and run with debugging. You should be able to see the incoming data from the browser, which includes an endpoint URL as well as some cryptographic tokens. +Choose *Allow* and check in the browser dev console that it didn't cause any errors. If you want, set a breakpoint on the server in `PizzaApiExtensions`'s `MapPut("/notifications/subscribe"...)` API method, and run with debugging. You should be able to see the incoming data from the browser, which includes an endpoint URL as well as some cryptographic tokens. Once you've either allowed or blocked notifications for a given site, your browser won't ask you again. If you need to reset things for further testing, and you're using either Chrome or Edge beta, you can click the "information" icon to the left of the address bar, and change *Notifications* back to *Ask (default)* as in this screenshot: diff --git a/save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs b/save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs deleted file mode 100644 index 43633d88..00000000 --- a/save-points/09-progressive-web-app/BlazingPizza.Server/NotificationsController.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace BlazingPizza.Server -{ - [Route("notifications")] - [ApiController] - [Authorize] - public class NotificationsController : Controller - { - private readonly PizzaStoreContext _db; - - public NotificationsController(PizzaStoreContext db) - { - _db = db; - } - - [HttpPut("subscribe")] - public async Task Subscribe(NotificationSubscription subscription) - { - // We're storing at most one subscription per user, so delete old ones. - // Alternatively, you could let the user register multiple subscriptions from different browsers/devices. - var userId = PizzaApiExtensions.GetUserId(HttpContext); - if (userId is null) - { - throw new UnauthorizedAccessException(); - } - var oldSubscriptions = _db.NotificationSubscriptions.Where(e => e.UserId == userId); - _db.NotificationSubscriptions.RemoveRange(oldSubscriptions); - - // Store new subscription - subscription.UserId = userId; - _db.NotificationSubscriptions.Attach(subscription); - - await _db.SaveChangesAsync(); - return subscription; - } - - } -} From 6fbd4cd136fc32ad13b6bb06ac49b035d397526c Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 17:39:06 -0400 Subject: [PATCH 51/53] Pulling up some missed changes from the final exercise --- src/BlazingPizza.Client/Pages/Authentication.razor | 2 +- src/BlazingPizza.Client/Pages/Checkout.razor | 8 ++++---- src/BlazingPizza.Client/Pages/Index.razor | 2 +- src/BlazingPizza.Client/Shared/LoginDisplay.razor | 8 +++----- src/BlazingPizza.Client/Shared/RedirectToLogin.razor | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/BlazingPizza.Client/Pages/Authentication.razor b/src/BlazingPizza.Client/Pages/Authentication.razor index c2f633e1..7fbca1da 100644 --- a/src/BlazingPizza.Client/Pages/Authentication.razor +++ b/src/BlazingPizza.Client/Pages/Authentication.razor @@ -24,7 +24,7 @@ private void RestorePizza(PizzaAuthenticationState pizzaState) { - if (pizzaState.Order != null) + if (pizzaState.Order is not null) { OrderState.ReplaceOrder(pizzaState.Order); } diff --git a/src/BlazingPizza.Client/Pages/Checkout.razor b/src/BlazingPizza.Client/Pages/Checkout.razor index f3c3a0fe..53e45fe9 100644 --- a/src/BlazingPizza.Client/Pages/Checkout.razor +++ b/src/BlazingPizza.Client/Pages/Checkout.razor @@ -41,7 +41,7 @@ async Task RequestNotificationSubscriptionAsync() { var subscription = await JSRuntime.InvokeAsync("blazorPushNotifications.requestSubscription"); - if (subscription != null) + if (subscription is not null) { try { @@ -61,9 +61,9 @@ try { var newOrderId = await OrdersClient.PlaceOrder(OrderState.Order); - OrderState.ResetOrder(); - NavigationManager.NavigateTo($"myorders/{newOrderId}"); - } + OrderState.ResetOrder(); + NavigationManager.NavigateTo($"myorders/{newOrderId}"); + } catch (AccessTokenNotAvailableException ex) { ex.Redirect(); diff --git a/src/BlazingPizza.Client/Pages/Index.razor b/src/BlazingPizza.Client/Pages/Index.razor index 3c1c882c..43dde123 100644 --- a/src/BlazingPizza.Client/Pages/Index.razor +++ b/src/BlazingPizza.Client/Pages/Index.razor @@ -6,7 +6,7 @@
                    - @if (specials != null) + @if (specials is not null) { @foreach (var special in specials) { diff --git a/src/BlazingPizza.Client/Shared/LoginDisplay.razor b/src/BlazingPizza.Client/Shared/LoginDisplay.razor index e1d9367f..b5c5bfa7 100644 --- a/src/BlazingPizza.Client/Shared/LoginDisplay.razor +++ b/src/BlazingPizza.Client/Shared/LoginDisplay.razor @@ -1,5 +1,4 @@ @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager -@code{ - async Task BeginSignOut() +@code { + void BeginSignOut() { - await SignOutManager.SetSignOutState(); - Navigation.NavigateTo("authentication/logout"); + Navigation.NavigateToLogout("authentication/logout"); } } \ No newline at end of file diff --git a/src/BlazingPizza.Client/Shared/RedirectToLogin.razor b/src/BlazingPizza.Client/Shared/RedirectToLogin.razor index 16c4bea2..696c387d 100644 --- a/src/BlazingPizza.Client/Shared/RedirectToLogin.razor +++ b/src/BlazingPizza.Client/Shared/RedirectToLogin.razor @@ -2,6 +2,6 @@ @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}"); + Navigation.NavigateToLogin($"authentication/login"); } } \ No newline at end of file From d6a3d9e8c0922d7f8dd36a5440497ff5532bc5c6 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 17:45:02 -0400 Subject: [PATCH 52/53] Removed unused NavigationManager references --- .../BlazingPizza.Client/Pages/Index.razor | 1 - .../BlazingPizza.Client/Pages/Authentication.razor | 1 - .../BlazingPizza.Client/Pages/Index.razor | 1 - .../BlazingPizza.Client/Pages/MyOrders.razor | 1 - .../BlazingPizza.Client/Pages/OrderDetails.razor | 1 - .../BlazingPizza.Client/Pages/Authentication.razor | 1 - .../07-javascript-interop/BlazingPizza.Client/Pages/Index.razor | 1 - .../BlazingPizza.Client/Pages/MyOrders.razor | 1 - .../BlazingPizza.Client/Pages/OrderDetails.razor | 1 - .../BlazingPizza.Client/Pages/Authentication.razor | 1 - .../BlazingPizza.Client/Pages/Index.razor | 1 - .../BlazingPizza.Client/Pages/MyOrders.razor | 1 - .../BlazingPizza.Client/Pages/OrderDetails.razor | 1 - .../BlazingPizza.Client/Pages/Authentication.razor | 1 - .../09-progressive-web-app/BlazingPizza.Client/Pages/Index.razor | 1 - .../BlazingPizza.Client/Pages/MyOrders.razor | 1 - .../BlazingPizza.Client/Pages/OrderDetails.razor | 1 - 17 files changed, 17 deletions(-) diff --git a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor index 1c72c094..613db159 100644 --- a/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor +++ b/save-points/05-checkout-with-validation/BlazingPizza.Client/Pages/Index.razor @@ -1,7 +1,6 @@ @page "/" @inject HttpClient HttpClient @inject OrderState OrderState -@inject NavigationManager NavigationManager
                      diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor index 7fbca1da..c7266152 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/Authentication.razor @@ -1,6 +1,5 @@ @page "/authentication/{action}" @inject OrderState OrderState -@inject NavigationManager NavigationManager
                        diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/MyOrders.razor index e739c243..da479989 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/MyOrders.razor @@ -1,7 +1,6 @@ @page "/myorders" @attribute [Authorize] @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager Blazing Pizza - My Orders diff --git a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/OrderDetails.razor b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/OrderDetails.razor index 019a0af1..f940f60f 100644 --- a/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/save-points/06-authentication-and-authorization/BlazingPizza.Client/Pages/OrderDetails.razor @@ -2,7 +2,6 @@ @attribute [Authorize] @using System.Threading @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager @implements IDisposable Blazing Pizza - Order Details diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor index 7fbca1da..c7266152 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/Authentication.razor @@ -1,6 +1,5 @@ @page "/authentication/{action}" @inject OrderState OrderState -@inject NavigationManager NavigationManager diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor index e739c243..da479989 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/MyOrders.razor @@ -1,7 +1,6 @@ @page "/myorders" @attribute [Authorize] @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager Blazing Pizza - My Orders diff --git a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor index 4393f07a..79c18741 100644 --- a/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/save-points/07-javascript-interop/BlazingPizza.Client/Pages/OrderDetails.razor @@ -2,7 +2,6 @@ @attribute [Authorize] @using System.Threading @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager @implements IDisposable Blazing Pizza - Order Details diff --git a/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor b/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor index 7fbca1da..c7266152 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Pages/Authentication.razor @@ -1,6 +1,5 @@ @page "/authentication/{action}" @inject OrderState OrderState -@inject NavigationManager NavigationManager diff --git a/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor index 300b84fd..1efeb194 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Pages/MyOrders.razor @@ -1,7 +1,6 @@ @page "/myorders" @attribute [Authorize] @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager Blazing Pizza - My Orders diff --git a/save-points/08-templated-components/BlazingPizza.Client/Pages/OrderDetails.razor b/save-points/08-templated-components/BlazingPizza.Client/Pages/OrderDetails.razor index 4393f07a..79c18741 100644 --- a/save-points/08-templated-components/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/save-points/08-templated-components/BlazingPizza.Client/Pages/OrderDetails.razor @@ -2,7 +2,6 @@ @attribute [Authorize] @using System.Threading @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager @implements IDisposable Blazing Pizza - Order Details diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor index 7fbca1da..c7266152 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/Authentication.razor @@ -1,6 +1,5 @@ @page "/authentication/{action}" @inject OrderState OrderState -@inject NavigationManager NavigationManager diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor index 300b84fd..1efeb194 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/MyOrders.razor @@ -1,7 +1,6 @@ @page "/myorders" @attribute [Authorize] @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager Blazing Pizza - My Orders diff --git a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor index 4393f07a..79c18741 100644 --- a/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor +++ b/save-points/09-progressive-web-app/BlazingPizza.Client/Pages/OrderDetails.razor @@ -2,7 +2,6 @@ @attribute [Authorize] @using System.Threading @inject OrdersClient OrdersClient -@inject NavigationManager NavigationManager @implements IDisposable Blazing Pizza - Order Details From 4a7346a23c8e0ba45d48ec3be8888683433eef68 Mon Sep 17 00:00:00 2001 From: "ed.charbeneau@gmail.com" Date: Thu, 21 Sep 2023 18:02:32 -0400 Subject: [PATCH 53/53] Updated code on Map component --- docs/07-javascript-interop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/07-javascript-interop.md b/docs/07-javascript-interop.md index f435faf3..41c4c748 100644 --- a/docs/07-javascript-interop.md +++ b/docs/07-javascript-interop.md @@ -18,7 +18,7 @@ Open *Map.razor* and take a look at the code: string elementId = $"map-{Guid.NewGuid().ToString("D")}"; [Parameter] double Zoom { get; set; } - [Parameter] List Markers { get; set; } + [Parameter, EditorRequired] public List Markers { get; set; } = new(); protected async override Task OnAfterRenderAsync(bool firstRender) {