From 61beb55a6b287e540c3459deab3a17ff3d0866ec Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 17 Feb 2022 19:41:07 +0000 Subject: [PATCH 01/19] Update to ASP.NET Core 7 preview 1 Update to preview 1 of ASP.NET Core 7. --- .vscode/launch.json | 2 +- .vsconfig | 2 +- NuGet.config | 1 + README.md | 2 +- global.json | 2 +- src/TodoApp/AuthenticationEndpoints.cs | 4 ++-- src/TodoApp/TodoApp.csproj | 6 +++--- tests/TodoApp.Tests/TodoApp.Tests.csproj | 4 ++-- tests/TodoApp.Tests/TodoAppFixture.cs | 8 ++++---- 9 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 7e776beb..3a8c5b58 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/src/TodoApp/bin/Debug/net6.0/TodoApp.dll", + "program": "${workspaceFolder}/src/TodoApp/bin/Debug/net7.0/TodoApp.dll", "args": [], "cwd": "${workspaceFolder}/src/TodoApp", "stopAtEntry": false, diff --git a/.vsconfig b/.vsconfig index 2641f274..89c6a67e 100644 --- a/.vsconfig +++ b/.vsconfig @@ -3,7 +3,7 @@ "components": [ "Microsoft.VisualStudio.Component.CoreEditor", "Microsoft.VisualStudio.Workload.CoreEditor", - "Microsoft.NetCore.Component.Runtime.6.0", + "Microsoft.NetCore.Component.Runtime.7.0", "Microsoft.NetCore.Component.SDK", "Microsoft.VisualStudio.Component.Roslyn.Compiler", "Microsoft.VisualStudio.Component.Roslyn.LanguageServices", diff --git a/NuGet.config b/NuGet.config index 38ac8e75..15b0564c 100644 --- a/NuGet.config +++ b/NuGet.config @@ -2,6 +2,7 @@ + diff --git a/README.md b/README.md index e32c35fd..00e0fc90 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ with [User Secrets] instead. Compiling the application yourself requires Git and the [.NET SDK](https://www.microsoft.com/net/download/core "Download the .NET SDK") -to be installed (version `6.0.100` or later). +to be installed (version `7.0.100` or later). To build and test the application locally from a terminal/command-line, run the following set of commands: diff --git a/global.json b/global.json index 177f6101..7fd929c4 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.402", + "version": "7.0.100-preview.1.22110.4", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/AuthenticationEndpoints.cs b/src/TodoApp/AuthenticationEndpoints.cs index 3010dd05..a6347e78 100644 --- a/src/TodoApp/AuthenticationEndpoints.cs +++ b/src/TodoApp/AuthenticationEndpoints.cs @@ -44,8 +44,8 @@ public static IServiceCollection AddGitHubAuthentication(this IServiceCollection { options.AccessDeniedPath = DeniedPath; options.CallbackPath = SignInPath + "-github"; - options.ClientId = configuration["GitHub:ClientId"]; - options.ClientSecret = configuration["GitHub:ClientSecret"]; + options.ClientId = configuration["GitHub:ClientId"] ?? string.Empty; + options.ClientSecret = configuration["GitHub:ClientSecret"] ?? string.Empty; options.EnterpriseDomain = configuration["GitHub:EnterpriseDomain"]; options.Scope.Add("user:email"); diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index d9455f81..5ec1b0d4 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -3,14 +3,14 @@ false $(NoWarn);CA1050 TodoApp - net6.0 + net7.0 true latest TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index ba38277f..29896132 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -2,7 +2,7 @@ false TodoApp - net6.0 + net7.0 @@ -10,7 +10,7 @@ - + diff --git a/tests/TodoApp.Tests/TodoAppFixture.cs b/tests/TodoApp.Tests/TodoAppFixture.cs index 1fb31683..38067eeb 100644 --- a/tests/TodoApp.Tests/TodoAppFixture.cs +++ b/tests/TodoApp.Tests/TodoAppFixture.cs @@ -56,10 +56,10 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) // Also override the default options for the GitHub OAuth provider var config = new[] { - KeyValuePair.Create("DataDirectory", dataDirectory), - KeyValuePair.Create("GitHub:ClientId", "github-id"), - KeyValuePair.Create("GitHub:ClientSecret", "github-secret"), - KeyValuePair.Create("GitHub:EnterpriseDomain", string.Empty) + KeyValuePair.Create("DataDirectory", dataDirectory), + KeyValuePair.Create("GitHub:ClientId", "github-id"), + KeyValuePair.Create("GitHub:ClientSecret", "github-secret"), + KeyValuePair.Create("GitHub:EnterpriseDomain", string.Empty) }; configBuilder.AddInMemoryCollection(config); From acb257eefe47ac14b08af166142436075b4212a8 Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 17 Feb 2022 21:21:18 +0000 Subject: [PATCH 02/19] Add scripts to load IDEs with local .dotnet Add scripts to load Visual Studio and Visual Studio Code with the .dotnet folder for a local install on the PATH. --- startvs.cmd | 24 ++++++++++++++++++++++++ startvscode.cmd | 29 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 startvs.cmd create mode 100644 startvscode.cmd diff --git a/startvs.cmd b/startvs.cmd new file mode 100644 index 00000000..76dff83d --- /dev/null +++ b/startvs.cmd @@ -0,0 +1,24 @@ +@ECHO OFF +SETLOCAL + +:: This command launches a Visual Studio solution with environment variables required to use a local version of the .NET SDK. + +:: This tells .NET to use the same dotnet.exe that the build script uses. +SET DOTNET_ROOT=%~dp0.dotnetcli +SET DOTNET_ROOT(x86)=%~dp0.dotnetcli\x86 + +:: Put our local dotnet.exe on PATH first so Visual Studio knows which one to use. +SET PATH=%DOTNET_ROOT%;%PATH% + +SET sln=%~dp0TodoApp.sln + +IF NOT EXIST "%DOTNET_ROOT%\dotnet.exe" ( + echo The .NET SDK has not yet been installed. Run `%~dp0build.ps1` to install it + exit /b 1 +) + +IF "%VSINSTALLDIR%" == "" ( + start "" "%sln%" +) else ( + "%VSINSTALLDIR%\Common7\IDE\devenv.com" "%sln%" +) diff --git a/startvscode.cmd b/startvscode.cmd new file mode 100644 index 00000000..b34b6d9d --- /dev/null +++ b/startvscode.cmd @@ -0,0 +1,29 @@ +@ECHO OFF +SETLOCAL + +:: This command launches Visual Studio Code with environment variables required to use a local version of the .NET SDK. + +:: This tells .NET to use the same dotnet.exe that the build script uses. +SET DOTNET_ROOT=%~dp0.dotnetcli +SET DOTNET_ROOT(x86)=%~dp0.dotnetcli\x86 + +:: Put our local dotnet.exe on PATH first so Visual Studio Code knows which one to use. +SET PATH=%DOTNET_ROOT%;%PATH% + +:: Sets the Target Framework for Visual Studio Code. +SET TARGET=net7.0 + +SET FOLDER=%~1 + +IF NOT EXIST "%DOTNET_ROOT%\dotnet.exe" ( + echo The .NET SDK has not yet been installed. Run `%~dp0build.ps1` to install it + exit /b 1 +) + +IF "%FOLDER%"=="" ( + code . +) else ( + code "%FOLDER%" +) + +exit /b 1 From d99b554e197fb062384b10e9f78e2af9a93b8f9a Mon Sep 17 00:00:00 2001 From: martincostello Date: Sat, 19 Feb 2022 14:33:58 +0000 Subject: [PATCH 03/19] Disable HTTP/2 Disable HTTP/2 to work around bug with DELETE being treated as POST. --- src/TodoApp/Program.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/TodoApp/Program.cs b/src/TodoApp/Program.cs index 8f3515e7..dba1fdd6 100644 --- a/src/TodoApp/Program.cs +++ b/src/TodoApp/Program.cs @@ -30,6 +30,11 @@ options => options.ForwardedHeaders |= ForwardedHeaders.XForwardedHost); } +// HACK Workaround https://github.com/dotnet/aspnetcore/issues/40301 +builder.WebHost.ConfigureKestrel( + options => options.ConfigureEndpointDefaults( + defaults => defaults.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1)); + // Create the app var app = builder.Build(); From f8c1bd3ef458122fa2f43a1a583e06c682fed98c Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 15 Mar 2022 19:03:41 +0000 Subject: [PATCH 04/19] Update to ASP.NET Core 7 preview 2 Update to preview 2 of ASP.NET Core 7. --- global.json | 2 +- src/TodoApp/Program.cs | 5 ----- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs | 2 +- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/global.json b/global.json index 7fd929c4..66498419 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.1.22110.4", + "version": "7.0.100-preview.2.22153.17", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/Program.cs b/src/TodoApp/Program.cs index dba1fdd6..8f3515e7 100644 --- a/src/TodoApp/Program.cs +++ b/src/TodoApp/Program.cs @@ -30,11 +30,6 @@ options => options.ForwardedHeaders |= ForwardedHeaders.XForwardedHost); } -// HACK Workaround https://github.com/dotnet/aspnetcore/issues/40301 -builder.WebHost.ConfigureKestrel( - options => options.ConfigureEndpointDefaults( - defaults => defaults.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1)); - // Create the app var app = builder.Build(); diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index 5ec1b0d4..dd1a9976 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs b/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs index d63a5e19..8ce6c23e 100644 --- a/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs +++ b/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs @@ -15,7 +15,7 @@ public RemoteAuthorizationEventsFilter(IHttpClientFactory httpClientFactory) private IHttpClientFactory HttpClientFactory { get; } - public void PostConfigure(string name, GitHubAuthenticationOptions options) + public void PostConfigure(string? name, GitHubAuthenticationOptions options) { // Use HttpClientFactory for HTTP requests so that the tests // can intercept the request and return canned responses. diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index 29896132..df05e4c7 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From 2dfb2fa3a973c8bfcdeec454988b4bac66713376 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 13 Apr 2022 18:08:14 +0100 Subject: [PATCH 05/19] Update to ASP.NET Core 7 preview 3 Update to preview 3 of ASP.NET Core 7. --- global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 66498419..d467822b 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.2.22153.17", + "version": "7.0.100-preview.3.22179.4", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index dd1a9976..2f17f5f0 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index df05e4c7..105533ec 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From 4713f37bdd75e3fb53c81059b43b4a7e2b04f5e2 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 10 May 2022 19:39:07 +0100 Subject: [PATCH 06/19] Update to ASP.NET Core 7 preview 4 Update to preview 4 of ASP.NET Core 7. --- NuGet.config | 3 +++ global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs | 2 +- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/NuGet.config b/NuGet.config index 15b0564c..f326f0cd 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,6 +6,9 @@ + + + diff --git a/global.json b/global.json index d467822b..4377249b 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.3.22179.4", + "version": "7.0.100-preview.4.22252.9", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index 2f17f5f0..19938b80 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs b/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs index 8ce6c23e..929bba54 100644 --- a/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs +++ b/tests/TodoApp.Tests/RemoteAuthorizationEventsFilter.cs @@ -19,7 +19,7 @@ public void PostConfigure(string? name, GitHubAuthenticationOptions options) { // Use HttpClientFactory for HTTP requests so that the tests // can intercept the request and return canned responses. - options.Backchannel = HttpClientFactory.CreateClient(name); + options.Backchannel = HttpClientFactory.CreateClient(name ?? string.Empty); // Configure the GitHub provider to redirect back to the // test application, rather than GitHub's own login pages. diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index 105533ec..9bb2e18d 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From e7d1447473c4653235d2b32662b1c88d77d441aa Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 11 May 2022 11:29:45 +0100 Subject: [PATCH 07/19] Use TypedResults Use the new TypedResults methods and remove some of the explicit Produces() method calls. --- src/TodoApp/ApiEndpoints.cs | 27 ++++++++++------------ src/TodoApp/Models/CreatedTodoItemModel.cs | 9 ++++++++ 2 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 src/TodoApp/Models/CreatedTodoItemModel.cs diff --git a/src/TodoApp/ApiEndpoints.cs b/src/TodoApp/ApiEndpoints.cs index 17b10a20..68589053 100644 --- a/src/TodoApp/ApiEndpoints.cs +++ b/src/TodoApp/ApiEndpoints.cs @@ -2,6 +2,7 @@ // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. using System.Security.Claims; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.EntityFrameworkCore; using NodaTime; using TodoApp.Data; @@ -73,21 +74,20 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder .RequireAuthorization(); // Get a specific Todo item - builder.MapGet("/api/items/{id}", async ( + builder.MapGet("/api/items/{id}", async Task, ProblemHttpResult>> ( Guid id, ClaimsPrincipal user, ITodoService service, CancellationToken cancellationToken) => { var model = await service.GetAsync(user.GetUserId(), id, cancellationToken); - return model is null ? Results.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound) : Results.Json(model); + return model is null ? TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound) : TypedResults.Ok(model); }) - .Produces(StatusCodes.Status200OK) .ProducesProblem(StatusCodes.Status404NotFound) .RequireAuthorization(); // Create a new Todo item - builder.MapPost("/api/items", async ( + builder.MapPost("/api/items", async Task, ProblemHttpResult>> ( CreateTodoItemModel model, ClaimsPrincipal user, ITodoService service, @@ -95,19 +95,18 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder { if (string.IsNullOrWhiteSpace(model.Text)) { - return Results.Problem("No item text specified.", statusCode: StatusCodes.Status400BadRequest); + return TypedResults.Problem("No item text specified.", statusCode: StatusCodes.Status400BadRequest); } var id = await service.AddItemAsync(user.GetUserId(), model.Text, cancellationToken); - return Results.Created($"/api/items/{id}", new { id }); + return TypedResults.Created($"/api/items/{id}", new CreatedTodoItemModel() { Id = id }); }) - .Produces(StatusCodes.Status201Created) .ProducesProblem(StatusCodes.Status400BadRequest) .RequireAuthorization(); // Mark a Todo item as completed - builder.MapPost("/api/items/{id}/complete", async ( + builder.MapPost("/api/items/{id}/complete", async Task> ( Guid id, ClaimsPrincipal user, ITodoService service, @@ -117,27 +116,25 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder return wasCompleted switch { - true => Results.NoContent(), - false => Results.Problem("Item already completed.", statusCode: StatusCodes.Status400BadRequest), - _ => Results.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), + true => TypedResults.NoContent(), + false => TypedResults.Problem("Item already completed.", statusCode: StatusCodes.Status400BadRequest), + _ => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), }; }) - .Produces(StatusCodes.Status204NoContent) .ProducesProblem(StatusCodes.Status400BadRequest) .ProducesProblem(StatusCodes.Status404NotFound) .RequireAuthorization(); // Delete a Todo item - builder.MapDelete("/api/items/{id}", async ( + builder.MapDelete("/api/items/{id}", async Task> ( Guid id, ClaimsPrincipal user, ITodoService service, CancellationToken cancellationToken) => { var wasDeleted = await service.DeleteItemAsync(user.GetUserId(), id, cancellationToken); - return wasDeleted ? Results.NoContent() : Results.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound); + return wasDeleted ? TypedResults.NoContent() : TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound); }) - .Produces(StatusCodes.Status204NoContent) .ProducesProblem(StatusCodes.Status404NotFound) .RequireAuthorization(); diff --git a/src/TodoApp/Models/CreatedTodoItemModel.cs b/src/TodoApp/Models/CreatedTodoItemModel.cs new file mode 100644 index 00000000..a678d048 --- /dev/null +++ b/src/TodoApp/Models/CreatedTodoItemModel.cs @@ -0,0 +1,9 @@ +// Copyright (c) Martin Costello, 2021. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace TodoApp.Models; + +public class CreatedTodoItemModel +{ + public string Id { get; set; } = string.Empty; +} From ae460bfa66e4b70fd3507e294b26b7b217aac27f Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 11 May 2022 11:39:31 +0100 Subject: [PATCH 08/19] Use MapGroup() Use MapGroup() for the API endpoints. --- src/TodoApp/ApiEndpoints.cs | 143 ++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/src/TodoApp/ApiEndpoints.cs b/src/TodoApp/ApiEndpoints.cs index 68589053..d1d1e314 100644 --- a/src/TodoApp/ApiEndpoints.cs +++ b/src/TodoApp/ApiEndpoints.cs @@ -63,80 +63,79 @@ public static IServiceCollection AddTodoApi(this IServiceCollection services) /// public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder builder) { - // Get all Todo items - builder.MapGet("/api/items", async ( - ITodoService service, - ClaimsPrincipal user, - CancellationToken cancellationToken) => - { - return await service.GetListAsync(user.GetUserId(), cancellationToken); - }) - .RequireAuthorization(); - - // Get a specific Todo item - builder.MapGet("/api/items/{id}", async Task, ProblemHttpResult>> ( - Guid id, - ClaimsPrincipal user, - ITodoService service, - CancellationToken cancellationToken) => - { - var model = await service.GetAsync(user.GetUserId(), id, cancellationToken); - return model is null ? TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound) : TypedResults.Ok(model); - }) - .ProducesProblem(StatusCodes.Status404NotFound) - .RequireAuthorization(); - - // Create a new Todo item - builder.MapPost("/api/items", async Task, ProblemHttpResult>> ( - CreateTodoItemModel model, - ClaimsPrincipal user, - ITodoService service, - CancellationToken cancellationToken) => - { - if (string.IsNullOrWhiteSpace(model.Text)) + var group = builder.MapGroup("/api/items") + .RequireAuthorization(); + { + // Get all Todo items + group.MapGet("/", async ( + ITodoService service, + ClaimsPrincipal user, + CancellationToken cancellationToken) => { - return TypedResults.Problem("No item text specified.", statusCode: StatusCodes.Status400BadRequest); - } - - var id = await service.AddItemAsync(user.GetUserId(), model.Text, cancellationToken); - - return TypedResults.Created($"/api/items/{id}", new CreatedTodoItemModel() { Id = id }); - }) - .ProducesProblem(StatusCodes.Status400BadRequest) - .RequireAuthorization(); - - // Mark a Todo item as completed - builder.MapPost("/api/items/{id}/complete", async Task> ( - Guid id, - ClaimsPrincipal user, - ITodoService service, - CancellationToken cancellationToken) => - { - var wasCompleted = await service.CompleteItemAsync(user.GetUserId(), id, cancellationToken); - - return wasCompleted switch + return await service.GetListAsync(user.GetUserId(), cancellationToken); + }); + + // Get a specific Todo item + group.MapGet("/{id}", async Task, ProblemHttpResult>> ( + Guid id, + ClaimsPrincipal user, + ITodoService service, + CancellationToken cancellationToken) => { - true => TypedResults.NoContent(), - false => TypedResults.Problem("Item already completed.", statusCode: StatusCodes.Status400BadRequest), - _ => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), - }; - }) - .ProducesProblem(StatusCodes.Status400BadRequest) - .ProducesProblem(StatusCodes.Status404NotFound) - .RequireAuthorization(); - - // Delete a Todo item - builder.MapDelete("/api/items/{id}", async Task> ( - Guid id, - ClaimsPrincipal user, - ITodoService service, - CancellationToken cancellationToken) => - { - var wasDeleted = await service.DeleteItemAsync(user.GetUserId(), id, cancellationToken); - return wasDeleted ? TypedResults.NoContent() : TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound); - }) - .ProducesProblem(StatusCodes.Status404NotFound) - .RequireAuthorization(); + var model = await service.GetAsync(user.GetUserId(), id, cancellationToken); + return model is null ? TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound) : TypedResults.Ok(model); + }) + .ProducesProblem(StatusCodes.Status404NotFound); + + // Create a new Todo item + group.MapPost("/", async Task, ProblemHttpResult>> ( + CreateTodoItemModel model, + ClaimsPrincipal user, + ITodoService service, + CancellationToken cancellationToken) => + { + if (string.IsNullOrWhiteSpace(model.Text)) + { + return TypedResults.Problem("No item text specified.", statusCode: StatusCodes.Status400BadRequest); + } + + var id = await service.AddItemAsync(user.GetUserId(), model.Text, cancellationToken); + + return TypedResults.Created($"/api/items/{id}", new CreatedTodoItemModel() { Id = id }); + }) + .ProducesProblem(StatusCodes.Status400BadRequest); + + // Mark a Todo item as completed + group.MapPost("/{id}/complete", async Task> ( + Guid id, + ClaimsPrincipal user, + ITodoService service, + CancellationToken cancellationToken) => + { + var wasCompleted = await service.CompleteItemAsync(user.GetUserId(), id, cancellationToken); + + return wasCompleted switch + { + true => TypedResults.NoContent(), + false => TypedResults.Problem("Item already completed.", statusCode: StatusCodes.Status400BadRequest), + _ => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), + }; + }) + .ProducesProblem(StatusCodes.Status400BadRequest) + .ProducesProblem(StatusCodes.Status404NotFound); + + // Delete a Todo item + group.MapDelete("/{id}", async Task> ( + Guid id, + ClaimsPrincipal user, + ITodoService service, + CancellationToken cancellationToken) => + { + var wasDeleted = await service.DeleteItemAsync(user.GetUserId(), id, cancellationToken); + return wasDeleted ? TypedResults.NoContent() : TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound); + }) + .ProducesProblem(StatusCodes.Status404NotFound); + }; // Redirect to Open API/Swagger documentation builder.MapGet("/api", () => Results.Redirect("/swagger-ui/index.html")) From f01c51c1ed626ee2985b26b54d174cf41fa9ac72 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 11 May 2022 11:42:39 +0100 Subject: [PATCH 09/19] Use switch expressions User switch expressions to make some lines of code terser. --- src/TodoApp/ApiEndpoints.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/TodoApp/ApiEndpoints.cs b/src/TodoApp/ApiEndpoints.cs index d1d1e314..f91417a1 100644 --- a/src/TodoApp/ApiEndpoints.cs +++ b/src/TodoApp/ApiEndpoints.cs @@ -83,7 +83,11 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder CancellationToken cancellationToken) => { var model = await service.GetAsync(user.GetUserId(), id, cancellationToken); - return model is null ? TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound) : TypedResults.Ok(model); + return model switch + { + null => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), + _ => TypedResults.Ok(model), + }; }) .ProducesProblem(StatusCodes.Status404NotFound); @@ -132,7 +136,11 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder CancellationToken cancellationToken) => { var wasDeleted = await service.DeleteItemAsync(user.GetUserId(), id, cancellationToken); - return wasDeleted ? TypedResults.NoContent() : TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound); + return wasDeleted switch + { + true => TypedResults.NoContent(), + false => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), + }; }) .ProducesProblem(StatusCodes.Status404NotFound); }; From 01990a8d8c5debdcf853ec8656b491c53ef5cc66 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 11 May 2022 11:45:44 +0100 Subject: [PATCH 10/19] Bind user Id to custom type Bind the user's Id to a custom type, rather than extract it out each time it is needed. --- src/TodoApp/ApiEndpoints.cs | 38 ++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/TodoApp/ApiEndpoints.cs b/src/TodoApp/ApiEndpoints.cs index f91417a1..671070e7 100644 --- a/src/TodoApp/ApiEndpoints.cs +++ b/src/TodoApp/ApiEndpoints.cs @@ -1,7 +1,6 @@ // Copyright (c) Martin Costello, 2021. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. -using System.Security.Claims; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.EntityFrameworkCore; using NodaTime; @@ -69,20 +68,20 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder // Get all Todo items group.MapGet("/", async ( ITodoService service, - ClaimsPrincipal user, + TodoUser user, CancellationToken cancellationToken) => { - return await service.GetListAsync(user.GetUserId(), cancellationToken); + return await service.GetListAsync(user, cancellationToken); }); // Get a specific Todo item group.MapGet("/{id}", async Task, ProblemHttpResult>> ( Guid id, - ClaimsPrincipal user, + TodoUser user, ITodoService service, CancellationToken cancellationToken) => { - var model = await service.GetAsync(user.GetUserId(), id, cancellationToken); + var model = await service.GetAsync(user, id, cancellationToken); return model switch { null => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), @@ -94,7 +93,7 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder // Create a new Todo item group.MapPost("/", async Task, ProblemHttpResult>> ( CreateTodoItemModel model, - ClaimsPrincipal user, + TodoUser user, ITodoService service, CancellationToken cancellationToken) => { @@ -103,7 +102,7 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder return TypedResults.Problem("No item text specified.", statusCode: StatusCodes.Status400BadRequest); } - var id = await service.AddItemAsync(user.GetUserId(), model.Text, cancellationToken); + var id = await service.AddItemAsync(user, model.Text, cancellationToken); return TypedResults.Created($"/api/items/{id}", new CreatedTodoItemModel() { Id = id }); }) @@ -112,11 +111,11 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder // Mark a Todo item as completed group.MapPost("/{id}/complete", async Task> ( Guid id, - ClaimsPrincipal user, + TodoUser user, ITodoService service, CancellationToken cancellationToken) => { - var wasCompleted = await service.CompleteItemAsync(user.GetUserId(), id, cancellationToken); + var wasCompleted = await service.CompleteItemAsync(user, id, cancellationToken); return wasCompleted switch { @@ -131,11 +130,11 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder // Delete a Todo item group.MapDelete("/{id}", async Task> ( Guid id, - ClaimsPrincipal user, + TodoUser user, ITodoService service, CancellationToken cancellationToken) => { - var wasDeleted = await service.DeleteItemAsync(user.GetUserId(), id, cancellationToken); + var wasDeleted = await service.DeleteItemAsync(user, id, cancellationToken); return wasDeleted switch { true => TypedResults.NoContent(), @@ -152,4 +151,21 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder return builder; } + + private readonly struct TodoUser + { + private TodoUser(string id) + { + Id = id; + } + + public string Id { get; } + + public static implicit operator string(TodoUser value) => value.Id; + + public static ValueTask BindAsync(HttpContext context) + { + return ValueTask.FromResult(new TodoUser(context.User.GetUserId())); + } + } } From 06103bd81d9676f1e71316eb065eafcca8d9073b Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 11 May 2022 11:59:24 +0100 Subject: [PATCH 11/19] Add summaries and descriptions Add summaries and descriptions to all of the API endpoints. --- src/TodoApp/ApiEndpoints.cs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/TodoApp/ApiEndpoints.cs b/src/TodoApp/ApiEndpoints.cs index 671070e7..3e0eeb91 100644 --- a/src/TodoApp/ApiEndpoints.cs +++ b/src/TodoApp/ApiEndpoints.cs @@ -65,16 +65,16 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder var group = builder.MapGroup("/api/items") .RequireAuthorization(); { - // Get all Todo items group.MapGet("/", async ( ITodoService service, TodoUser user, CancellationToken cancellationToken) => { return await service.GetListAsync(user, cancellationToken); - }); + }) + .WithSummary("Get all Todo items") + .WithDescription("Gets all of the current user's todo items."); - // Get a specific Todo item group.MapGet("/{id}", async Task, ProblemHttpResult>> ( Guid id, TodoUser user, @@ -88,9 +88,10 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder _ => TypedResults.Ok(model), }; }) - .ProducesProblem(StatusCodes.Status404NotFound); + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Get a specific Todo item") + .WithDescription("Gets the todo item with the specified ID."); - // Create a new Todo item group.MapPost("/", async Task, ProblemHttpResult>> ( CreateTodoItemModel model, TodoUser user, @@ -106,9 +107,10 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder return TypedResults.Created($"/api/items/{id}", new CreatedTodoItemModel() { Id = id }); }) - .ProducesProblem(StatusCodes.Status400BadRequest); + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Create a new Todo item") + .WithDescription("Creates a new todo item for the current user and returns its ID."); - // Mark a Todo item as completed group.MapPost("/{id}/complete", async Task> ( Guid id, TodoUser user, @@ -125,9 +127,10 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder }; }) .ProducesProblem(StatusCodes.Status400BadRequest) - .ProducesProblem(StatusCodes.Status404NotFound); + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Mark a Todo item as completed") + .WithDescription("Marks the todo item with the specified ID as complete."); - // Delete a Todo item group.MapDelete("/{id}", async Task> ( Guid id, TodoUser user, @@ -141,7 +144,9 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder false => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), }; }) - .ProducesProblem(StatusCodes.Status404NotFound); + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Delete a Todo item") + .WithDescription("Deletes the todo item with the specified ID."); }; // Redirect to Open API/Swagger documentation From da379e9038b4023e4cf9f2c9739c38d13ca3cf27 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 14 Jun 2022 18:16:16 +0100 Subject: [PATCH 12/19] Update to ASP.NET Core 7 preview 5 Update to preview 5 of ASP.NET Core 7. --- global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 4377249b..65d83b71 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.4.22252.9", + "version": "7.0.100-preview.5.22307.18", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index 19938b80..62638051 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index 9bb2e18d..88317168 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From 76537374b6e190ddd02f9ae8cfe002b0b3d23ca8 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 15 Jun 2022 12:39:57 +0100 Subject: [PATCH 13/19] Use [AsParameters] Use [AsParameters] to collapse two parameters down into one. --- src/TodoApp/ApiEndpoints.cs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/TodoApp/ApiEndpoints.cs b/src/TodoApp/ApiEndpoints.cs index 3e0eeb91..9ada47eb 100644 --- a/src/TodoApp/ApiEndpoints.cs +++ b/src/TodoApp/ApiEndpoints.cs @@ -66,22 +66,20 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder .RequireAuthorization(); { group.MapGet("/", async ( - ITodoService service, - TodoUser user, + [AsParameters] TodoRequestContext context, CancellationToken cancellationToken) => { - return await service.GetListAsync(user, cancellationToken); + return await context.Service.GetListAsync(context.User, cancellationToken); }) .WithSummary("Get all Todo items") .WithDescription("Gets all of the current user's todo items."); group.MapGet("/{id}", async Task, ProblemHttpResult>> ( Guid id, - TodoUser user, - ITodoService service, + [AsParameters] TodoRequestContext context, CancellationToken cancellationToken) => { - var model = await service.GetAsync(user, id, cancellationToken); + var model = await context.Service.GetAsync(context.User, id, cancellationToken); return model switch { null => TypedResults.Problem("Item not found.", statusCode: StatusCodes.Status404NotFound), @@ -94,8 +92,7 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder group.MapPost("/", async Task, ProblemHttpResult>> ( CreateTodoItemModel model, - TodoUser user, - ITodoService service, + [AsParameters] TodoRequestContext context, CancellationToken cancellationToken) => { if (string.IsNullOrWhiteSpace(model.Text)) @@ -103,7 +100,7 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder return TypedResults.Problem("No item text specified.", statusCode: StatusCodes.Status400BadRequest); } - var id = await service.AddItemAsync(user, model.Text, cancellationToken); + var id = await context.Service.AddItemAsync(context.User, model.Text, cancellationToken); return TypedResults.Created($"/api/items/{id}", new CreatedTodoItemModel() { Id = id }); }) @@ -113,11 +110,10 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder group.MapPost("/{id}/complete", async Task> ( Guid id, - TodoUser user, - ITodoService service, + [AsParameters] TodoRequestContext context, CancellationToken cancellationToken) => { - var wasCompleted = await service.CompleteItemAsync(user, id, cancellationToken); + var wasCompleted = await context.Service.CompleteItemAsync(context.User, id, cancellationToken); return wasCompleted switch { @@ -133,11 +129,10 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder group.MapDelete("/{id}", async Task> ( Guid id, - TodoUser user, - ITodoService service, + [AsParameters] TodoRequestContext context, CancellationToken cancellationToken) => { - var wasDeleted = await service.DeleteItemAsync(user, id, cancellationToken); + var wasDeleted = await context.Service.DeleteItemAsync(context.User, id, cancellationToken); return wasDeleted switch { true => TypedResults.NoContent(), @@ -157,6 +152,8 @@ public static IEndpointRouteBuilder MapTodoApiRoutes(this IEndpointRouteBuilder return builder; } + private record struct TodoRequestContext(TodoUser User, ITodoService Service); + private readonly struct TodoUser { private TodoUser(string id) From 583c58a0079318ce4be7b26b5cb7cace708a8ac1 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 12 Jul 2022 18:14:41 +0100 Subject: [PATCH 14/19] Update to ASP.NET Core 7 preview 6 Update to preview 6 of ASP.NET Core 7. --- global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 65d83b71..6298aa41 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.5.22307.18", + "version": "7.0.100-preview.6.22352.1", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index 62638051..6d271edc 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index 88317168..97849231 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From 2ccbf4115e8f1484f55c80c383d10d30c511e579 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 9 Aug 2022 17:04:10 +0100 Subject: [PATCH 15/19] Update to ASP.NET Core 7 preview 7 Update to preview 7 of ASP.NET Core 7. --- global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 6298aa41..fdfda88b 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.6.22352.1", + "version": "7.0.100-preview.7.22377.5", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index 6d271edc..ecf1d6f1 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index 97849231..041eab0e 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From 4876e199a0084a2125b267e3de56602d3b24cf49 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 10 Aug 2022 17:32:20 +0100 Subject: [PATCH 16/19] Simplify AddAuthentication Use the string overload rather than the options overload. --- src/TodoApp/AuthenticationEndpoints.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TodoApp/AuthenticationEndpoints.cs b/src/TodoApp/AuthenticationEndpoints.cs index a6347e78..7875313d 100644 --- a/src/TodoApp/AuthenticationEndpoints.cs +++ b/src/TodoApp/AuthenticationEndpoints.cs @@ -31,7 +31,7 @@ public static class AuthenticationEndpoints public static IServiceCollection AddGitHubAuthentication(this IServiceCollection services) { return services - .AddAuthentication(options => options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme) + .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = SignInPath; From a013737346b2758f6765742187a07e3c18b9e00a Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 14 Sep 2022 15:56:46 +0100 Subject: [PATCH 17/19] Update to ASP.NET Core 7 RC1 Update to release candidate 1 of ASP.NET Core 7. --- global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index fdfda88b..a3f52cf7 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.7.22377.5", + "version": "7.0.100-rc.1.22431.12", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index ecf1d6f1..f5eb885b 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index 041eab0e..bf50ee15 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From 83a7cc25b9806b25bd215414055e32860ef05c2c Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 11 Oct 2022 18:35:24 +0100 Subject: [PATCH 18/19] Update to ASP.NET Core 7 RC2 Update to release candidate 2 of ASP.NET Core 7. --- global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index a3f52cf7..54125015 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-rc.1.22431.12", + "version": "7.0.100-rc.2.22477.23", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index f5eb885b..4130460a 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index bf50ee15..c1c1901a 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - + From 3abcfd1fd91894cc8edd296eaba0e02c52c575cd Mon Sep 17 00:00:00 2001 From: martincostello Date: Mon, 7 Nov 2022 09:31:16 +0000 Subject: [PATCH 19/19] Update to ASP.NET Core 7 Update to the stable release of .NET 7. --- NuGet.config | 4 ---- global.json | 2 +- src/TodoApp/TodoApp.csproj | 4 ++-- tests/TodoApp.Tests/TodoApp.Tests.csproj | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/NuGet.config b/NuGet.config index f326f0cd..38ac8e75 100644 --- a/NuGet.config +++ b/NuGet.config @@ -2,13 +2,9 @@ - - - - diff --git a/global.json b/global.json index 54125015..e6ab3173 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-rc.2.22477.23", + "version": "7.0.100", "allowPrerelease": false, "rollForward": "latestMajor" } diff --git a/src/TodoApp/TodoApp.csproj b/src/TodoApp/TodoApp.csproj index 4130460a..67d97a20 100644 --- a/src/TodoApp/TodoApp.csproj +++ b/src/TodoApp/TodoApp.csproj @@ -9,8 +9,8 @@ TodoApp - - + + diff --git a/tests/TodoApp.Tests/TodoApp.Tests.csproj b/tests/TodoApp.Tests/TodoApp.Tests.csproj index c1c1901a..99533af0 100644 --- a/tests/TodoApp.Tests/TodoApp.Tests.csproj +++ b/tests/TodoApp.Tests/TodoApp.Tests.csproj @@ -10,7 +10,7 @@ - +