From 8922c38b5a026c0c0ea80b34d2e1d1f4838a06da Mon Sep 17 00:00:00 2001 From: Christian de Jonge Date: Tue, 26 Sep 2023 11:56:16 +0200 Subject: [PATCH] Update to .net version 7 --- .github/workflows/backend_lint_and_test.yml | 20 ++++---- backend/api.test/EndpointTest.cs | 2 +- .../EventHandlers/TestMissionEventHandler.cs | 7 ++- .../Mocks/CustomMissionServiceMock.cs | 7 ++- backend/api.test/Services/MissionService.cs | 12 ++--- backend/api.test/Services/RobotService.cs | 6 ++- backend/api.test/api.test.csproj | 14 +++--- backend/api/MQTT/MqttService.cs | 26 +++++----- backend/api/Program.cs | 14 +++--- backend/api/Services/CustomMissionService.cs | 3 +- backend/api/Services/EchoService.cs | 48 +++++-------------- backend/api/Services/IsarService.cs | 8 ++-- backend/api/Services/SortingService.cs | 6 +-- backend/api/Services/StidService.cs | 12 ++--- backend/api/Utilities/AsyncExtensions.cs | 8 ++-- backend/api/api.csproj | 21 ++++---- backend/api/appsettings.Test.json | 5 ++ 17 files changed, 95 insertions(+), 124 deletions(-) diff --git a/.github/workflows/backend_lint_and_test.yml b/.github/workflows/backend_lint_and_test.yml index bc9f6d5e2..4fb726691 100644 --- a/.github/workflows/backend_lint_and_test.yml +++ b/.github/workflows/backend_lint_and_test.yml @@ -17,12 +17,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: "6.0.x" + dotnet-version: "7.0.x" - name: Build project and dependencies run: dotnet build @@ -30,12 +30,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2.1.0 + uses: actions/checkout@v3 - name: Set up .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: "6.0.x" + dotnet-version: "7.0.x" - name: Build project and dependencies run: dotnet build -warnaserror @@ -46,14 +46,14 @@ jobs: check_formatting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: "6.0.x" + dotnet-version: "7.0.x" - # Dotnet format is included in the .NET6 SDK + # Dotnet format is included in the .NET7 SDK # By default, the task ensures the exit code is 0 # If a file needs to be edited by dotnet format, the exit code will be a non-zero value # We are using severity level 'info' here. diff --git a/backend/api.test/EndpointTest.cs b/backend/api.test/EndpointTest.cs index 480a516cb..61b51b55a 100644 --- a/backend/api.test/EndpointTest.cs +++ b/backend/api.test/EndpointTest.cs @@ -515,7 +515,7 @@ public async Task GetMissionsInAreaTest() Assert.True(areaMissionsResponse.IsSuccessStatusCode); var missions = await areaMissionsResponse.Content.ReadFromJsonAsync>(_serializerOptions); Assert.NotNull(missions); - Assert.Equal(1, missions.Count); + Assert.Single(missions); Assert.Equal(missions[0].Id, mission.MissionId); } diff --git a/backend/api.test/EventHandlers/TestMissionEventHandler.cs b/backend/api.test/EventHandlers/TestMissionEventHandler.cs index a0ef9fe60..22dd1466d 100644 --- a/backend/api.test/EventHandlers/TestMissionEventHandler.cs +++ b/backend/api.test/EventHandlers/TestMissionEventHandler.cs @@ -267,7 +267,7 @@ public async void NoMissionIsStartedIfQueueIsEmptyWhenRobotBecomesAvailable() _mqttService.RaiseEvent(nameof(MqttService.MqttIsarRobotStatusReceived), mqttEventArgs); // Assert - bool ongoingMission = _missionRunService.ReadAll( + var ongoingMission = await _missionRunService.ReadAll( new MissionRunQueryStringParameters { Statuses = new List @@ -276,9 +276,8 @@ public async void NoMissionIsStartedIfQueueIsEmptyWhenRobotBecomesAvailable() }, OrderBy = "DesiredStartTime", PageSize = 100 - }).Result.Any(); - - Assert.False(ongoingMission); + }); + Assert.False(ongoingMission.Any()); } [Fact] diff --git a/backend/api.test/Mocks/CustomMissionServiceMock.cs b/backend/api.test/Mocks/CustomMissionServiceMock.cs index ae7ece030..312fe0ceb 100644 --- a/backend/api.test/Mocks/CustomMissionServiceMock.cs +++ b/backend/api.test/Mocks/CustomMissionServiceMock.cs @@ -21,9 +21,9 @@ public string UploadSource(List tasks) public async Task?> GetMissionTasksFromSourceId(string id) { - if (mockBlobStore.ContainsKey(id)) + if (mockBlobStore.TryGetValue(id, out var value)) { - var content = mockBlobStore[id]; + var content = value; foreach (var task in content) { task.Id = Guid.NewGuid().ToString(); // This is needed as tasks are owned by mission runs @@ -44,8 +44,7 @@ public string CalculateHashFromTasks(IList tasks) } string json = JsonSerializer.Serialize(genericTasks); - var hasher = SHA256.Create(); - byte[] hash = hasher.ComputeHash(Encoding.UTF8.GetBytes(json)); + byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(json)); return BitConverter.ToString(hash).Replace("-", "", StringComparison.CurrentCulture).ToUpperInvariant(); } } diff --git a/backend/api.test/Services/MissionService.cs b/backend/api.test/Services/MissionService.cs index 65fb7d0d1..7c9019c2b 100644 --- a/backend/api.test/Services/MissionService.cs +++ b/backend/api.test/Services/MissionService.cs @@ -43,10 +43,8 @@ public async Task ReadIdDoesNotExist() public async Task Create() { var robot = _context.Robots.First(); - int nReportsBefore = _missionRunService - .ReadAll(new MissionRunQueryStringParameters()) - .Result.Count; - + var reportsBefore = await _missionRunService.ReadAll(new MissionRunQueryStringParameters()); + int nReportsBefore = reportsBefore.Count; var testInstallation = new Installation { InstallationCode = "test", @@ -86,10 +84,8 @@ public async Task Create() }; await _missionRunService.Create(missionRun); - int nReportsAfter = _missionRunService - .ReadAll(new MissionRunQueryStringParameters()) - .Result.Count; - + var reportsAfter = await _missionRunService.ReadAll(new MissionRunQueryStringParameters()); + int nReportsAfter = reportsAfter.Count; Assert.Equal(nReportsBefore + 1, nReportsAfter); } } diff --git a/backend/api.test/Services/RobotService.cs b/backend/api.test/Services/RobotService.cs index 18121cf50..5f9923f53 100644 --- a/backend/api.test/Services/RobotService.cs +++ b/backend/api.test/Services/RobotService.cs @@ -58,7 +58,8 @@ public async Task ReadIdDoesNotExist() public async Task Create() { var robotService = new RobotService(_context); - int nRobotsBefore = robotService.ReadAll().Result.Count(); + var robotsBefore = await robotService.ReadAll(); + int nRobotsBefore = robotsBefore.Count(); var videoStreamQuery = new CreateVideoStreamQuery() { Name = "Front Camera", @@ -84,7 +85,8 @@ public async Task Create() robot.Model = robotModel; await robotService.Create(robot); - int nRobotsAfter = robotService.ReadAll().Result.Count(); + var robotsAfter = await robotService.ReadAll(); + int nRobotsAfter = robotsAfter.Count(); Assert.Equal(nRobotsBefore + 1, nRobotsAfter); } diff --git a/backend/api.test/api.test.csproj b/backend/api.test/api.test.csproj index 3a8807586..1ee3a5021 100644 --- a/backend/api.test/api.test.csproj +++ b/backend/api.test/api.test.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 enable false @@ -10,15 +10,15 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/backend/api/MQTT/MqttService.cs b/backend/api/MQTT/MqttService.cs index 4120806f1..62bb2c14c 100644 --- a/backend/api/MQTT/MqttService.cs +++ b/backend/api/MQTT/MqttService.cs @@ -43,24 +43,24 @@ public MqttService(ILogger logger, IConfiguration config) ).Equals("Production", StringComparison.OrdinalIgnoreCase);*/ var mqttConfig = config.GetSection("Mqtt"); - string password = mqttConfig.GetValue("Password"); - string username = mqttConfig.GetValue("Username"); - _serverHost = mqttConfig.GetValue("Host"); + string password = mqttConfig.GetValue("Password") ?? ""; + string username = mqttConfig.GetValue("Username") ?? ""; + _serverHost = mqttConfig.GetValue("Host") ?? ""; _serverPort = mqttConfig.GetValue("Port"); _maxRetryAttempts = mqttConfig.GetValue("MaxRetryAttempts"); _shouldFailOnMaxRetries = mqttConfig.GetValue("ShouldFailOnMaxRetries"); + + var tlsOptions = new MqttClientTlsOptions + { + UseTls = true, + /* Currently disabled to use self-signed certificate in the internal broker communication */ + //if (_notProduction) + IgnoreCertificateChainErrors = true + }; var builder = new MqttClientOptionsBuilder() .WithTcpServer(_serverHost, _serverPort) - .WithTls( - o => - { - o.UseTls = true; - /* Currently disabled to use self-signed certificate in the internal broker communication */ - //if (_notProduction) - o.IgnoreCertificateChainErrors = true; - } - ) + .WithTlsOptions(tlsOptions) .WithCredentials(username, password); _options = new ManagedMqttClientOptionsBuilder() @@ -70,7 +70,7 @@ public MqttService(ILogger logger, IConfiguration config) RegisterCallbacks(); - var topics = mqttConfig.GetSection("Topics").Get>(); + var topics = mqttConfig.GetSection("Topics").Get>() ?? new List(); SubscribeToTopics(topics); } public static event EventHandler? MqttIsarRobotStatusReceived; diff --git a/backend/api/Program.cs b/backend/api/Program.cs index 359a5c005..681aa283c 100644 --- a/backend/api/Program.cs +++ b/backend/api/Program.cs @@ -92,9 +92,9 @@ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches() - .AddDownstreamWebApi(EchoService.ServiceName, builder.Configuration.GetSection("Echo")) - .AddDownstreamWebApi(StidService.ServiceName, builder.Configuration.GetSection("Stid")) - .AddDownstreamWebApi(IsarService.ServiceName, builder.Configuration.GetSection("Isar")); + .AddDownstreamApi(EchoService.ServiceName, builder.Configuration.GetSection("Echo")) + .AddDownstreamApi(StidService.ServiceName, builder.Configuration.GetSection("Stid")) + .AddDownstreamApi(IsarService.ServiceName, builder.Configuration.GetSection("Isar")); builder.Services.AddAuthorization( options => @@ -106,8 +106,7 @@ ); var app = builder.Build(); - -string basePath = builder.Configuration["BackendBaseRoute"]; +string basePath = builder.Configuration["BackendBaseRoute"] ?? ""; app.UseSwagger( c => { @@ -138,7 +137,7 @@ new Dictionary { { - "Resource", builder.Configuration["AzureAd:ClientId"] + "Resource", builder.Configuration["AzureAd:ClientId"] ?? throw new ArgumentException("No Azure Ad ClientId") } } ); @@ -150,10 +149,11 @@ option.AddRedirect("^$", "swagger"); app.UseRewriter(option); +string[] allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get() ?? Array.Empty(); app.UseCors( corsBuilder => corsBuilder - .WithOrigins(builder.Configuration.GetSection("AllowedOrigins").Get()) + .WithOrigins(allowedOrigins) .SetIsOriginAllowedToAllowWildcardSubdomains() .WithExposedHeaders(QueryStringParameters.PaginationHeader) .AllowAnyHeader() diff --git a/backend/api/Services/CustomMissionService.cs b/backend/api/Services/CustomMissionService.cs index 23ca8b71e..383efaf7a 100644 --- a/backend/api/Services/CustomMissionService.cs +++ b/backend/api/Services/CustomMissionService.cs @@ -66,8 +66,7 @@ public string CalculateHashFromTasks(IList tasks) } string json = JsonSerializer.Serialize(genericTasks); - var hasher = SHA256.Create(); - byte[] hash = hasher.ComputeHash(Encoding.UTF8.GetBytes(json)); + byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(json)); return BitConverter.ToString(hash).Replace("-", "", StringComparison.CurrentCulture).ToUpperInvariant(); } } diff --git a/backend/api/Services/EchoService.cs b/backend/api/Services/EchoService.cs index 05c344467..2198b8c89 100644 --- a/backend/api/Services/EchoService.cs +++ b/backend/api/Services/EchoService.cs @@ -3,7 +3,7 @@ using Api.Database.Models; using Api.Services.Models; using Api.Utilities; -using Microsoft.Identity.Web; +using Microsoft.Identity.Abstractions; using Microsoft.IdentityModel.Tokens; namespace Api.Services { @@ -20,10 +20,10 @@ public interface IEchoService public class EchoService : IEchoService { public const string ServiceName = "EchoApi"; - private readonly IDownstreamWebApi _echoApi; + private readonly IDownstreamApi _echoApi; private readonly ILogger _logger; - public EchoService(IDownstreamWebApi downstreamWebApi, ILogger logger) + public EchoService(IDownstreamApi downstreamWebApi, ILogger logger) { _echoApi = downstreamWebApi; _logger = logger; @@ -35,7 +35,7 @@ public async Task> GetAvailableMissions(string ? "robots/robot-plan?Status=Ready" : $"robots/robot-plan?InstallationCode={installationCode}&&Status=Ready"; - var response = await _echoApi.CallWebApiForUserAsync( + var response = await _echoApi.CallApiForUserAsync( ServiceName, options => { @@ -48,12 +48,7 @@ public async Task> GetAvailableMissions(string var echoMissions = await response.Content.ReadFromJsonAsync< List - >(); - if (echoMissions is null) - { - throw new JsonException("Failed to deserialize missions from Echo"); - } - + >() ?? throw new JsonException("Failed to deserialize missions from Echo"); var availableMissions = ProcessAvailableEchoMission(echoMissions); return availableMissions; @@ -63,7 +58,7 @@ public async Task GetMissionById(int missionId) { string relativePath = $"robots/robot-plan/{missionId}"; - var response = await _echoApi.CallWebApiForUserAsync( + var response = await _echoApi.CallApiForUserAsync( ServiceName, options => { @@ -74,26 +69,15 @@ public async Task GetMissionById(int missionId) response.EnsureSuccessStatusCode(); - var echoMission = await response.Content.ReadFromJsonAsync(); - - if (echoMission is null) - { - throw new JsonException("Failed to deserialize mission from Echo"); - } - - var processedEchoMission = ProcessEchoMission(echoMission); - if (processedEchoMission == null) - { - throw new InvalidDataException($"EchoMission with id: {missionId} is invalid."); - } - + var echoMission = await response.Content.ReadFromJsonAsync() ?? throw new JsonException("Failed to deserialize mission from Echo"); + var processedEchoMission = ProcessEchoMission(echoMission) ?? throw new InvalidDataException($"EchoMission with id: {missionId} is invalid."); return processedEchoMission; } public async Task> GetEchoPlantInfos() { string relativePath = "plantinfo"; - var response = await _echoApi.CallWebApiForUserAsync( + var response = await _echoApi.CallApiForUserAsync( ServiceName, options => { @@ -105,11 +89,7 @@ public async Task> GetEchoPlantInfos() response.EnsureSuccessStatusCode(); var echoPlantInfoResponse = await response.Content.ReadFromJsonAsync< List - >(); - if (echoPlantInfoResponse is null) - { - throw new JsonException("Failed to deserialize plant information from Echo"); - } + >() ?? throw new JsonException("Failed to deserialize plant information from Echo"); var installations = ProcessEchoPlantInfos(echoPlantInfoResponse); return installations; } @@ -117,7 +97,7 @@ public async Task> GetEchoPlantInfos() public async Task GetRobotPoseFromPoseId(int poseId) { string relativePath = $"/robots/pose/{poseId}"; - var response = await _echoApi.CallWebApiForUserAsync( + var response = await _echoApi.CallApiForUserAsync( ServiceName, options => { @@ -126,11 +106,7 @@ public async Task GetRobotPoseFromPoseId(int poseId) } ); response.EnsureSuccessStatusCode(); - var echoPoseResponse = await response.Content.ReadFromJsonAsync(); - if (echoPoseResponse is null) - { - throw new JsonException("Failed to deserialize robot pose from Echo"); - } + var echoPoseResponse = await response.Content.ReadFromJsonAsync() ?? throw new JsonException("Failed to deserialize robot pose from Echo"); return echoPoseResponse; } diff --git a/backend/api/Services/IsarService.cs b/backend/api/Services/IsarService.cs index 6efb457d3..21ea999d8 100644 --- a/backend/api/Services/IsarService.cs +++ b/backend/api/Services/IsarService.cs @@ -2,7 +2,7 @@ using Api.Database.Models; using Api.Services.Models; using Api.Utilities; -using Microsoft.Identity.Web; +using Microsoft.Identity.Abstractions; namespace Api.Services { @@ -24,10 +24,10 @@ public interface IIsarService public class IsarService : IIsarService { public const string ServiceName = "IsarApi"; - private readonly IDownstreamWebApi _isarApi; + private readonly IDownstreamApi _isarApi; private readonly ILogger _logger; - public IsarService(ILogger logger, IDownstreamWebApi downstreamWebApi) + public IsarService(ILogger logger, IDownstreamApi downstreamWebApi) { _logger = logger; _isarApi = downstreamWebApi; @@ -55,7 +55,7 @@ private async Task CallApi( null, "application/json" ); - var response = await _isarApi.CallWebApiForAppAsync( + var response = await _isarApi.CallApiForAppAsync( ServiceName, options => { diff --git a/backend/api/Services/SortingService.cs b/backend/api/Services/SortingService.cs index f2eb2982d..10bb9e5da 100644 --- a/backend/api/Services/SortingService.cs +++ b/backend/api/Services/SortingService.cs @@ -41,13 +41,9 @@ public static void ApplySort(ref IQueryable missions, string orderByQueryS propertyFromQueryName, StringComparison.Ordinal ) - ); - - if (objectProperty == null) - throw new InvalidDataException( + ) ?? throw new InvalidDataException( $"Mission has no property '{propertyFromQueryName}' for ordering" ); - string sortingOrder = param.EndsWith(" desc", StringComparison.OrdinalIgnoreCase) ? "descending" : "ascending"; diff --git a/backend/api/Services/StidService.cs b/backend/api/Services/StidService.cs index 393b32ec7..9bab4d94f 100644 --- a/backend/api/Services/StidService.cs +++ b/backend/api/Services/StidService.cs @@ -1,7 +1,7 @@ using System.Text.Json; using Api.Database.Models; using Api.Services.Models; -using Microsoft.Identity.Web; +using Microsoft.Identity.Abstractions; namespace Api.Services { @@ -13,9 +13,9 @@ public interface IStidService public class StidService : IStidService { public const string ServiceName = "StidApi"; - private readonly IDownstreamWebApi _stidApi; + private readonly IDownstreamApi _stidApi; - public StidService(IDownstreamWebApi downstreamWebApi) + public StidService(IDownstreamApi downstreamWebApi) { _stidApi = downstreamWebApi; } @@ -24,7 +24,7 @@ public async Task GetTagPosition(string tag, string installationCode) { string relativePath = $"{installationCode}/tag?tagNo={tag}"; - var response = await _stidApi.CallWebApiForAppAsync( + var response = await _stidApi.CallApiForAppAsync( ServiceName, options => { @@ -35,9 +35,7 @@ public async Task GetTagPosition(string tag, string installationCode) response.EnsureSuccessStatusCode(); var stidTagPositionResponse = - await response.Content.ReadFromJsonAsync(); - if (stidTagPositionResponse is null) - throw new JsonException("Failed to deserialize tag position from STID"); + await response.Content.ReadFromJsonAsync() ?? throw new JsonException("Failed to deserialize tag position from STID"); // Convert from millimeter to meter return new Position( diff --git a/backend/api/Utilities/AsyncExtensions.cs b/backend/api/Utilities/AsyncExtensions.cs index dbd6c9db5..9dab3efd7 100644 --- a/backend/api/Utilities/AsyncExtensions.cs +++ b/backend/api/Utilities/AsyncExtensions.cs @@ -28,7 +28,7 @@ public CancellationTokenAwaiter(CancellationToken cancellationToken) internal CancellationToken _cancellationToken; - public object GetResult() + public readonly object GetResult() { // this is called by compiler generated methods when the // task has completed. Instead of returning a result, we @@ -43,15 +43,15 @@ public object GetResult() // called by compiler generated/.net internals to check // if the task has completed. - public bool IsCompleted => _cancellationToken.IsCancellationRequested; + public readonly bool IsCompleted => _cancellationToken.IsCancellationRequested; // The compiler will generate stuff that hooks in // here. We hook those methods directly into the // cancellation token. - public void OnCompleted(Action continuation) => + public readonly void OnCompleted(Action continuation) => _cancellationToken.Register(continuation); - public void UnsafeOnCompleted(Action continuation) => + public readonly void UnsafeOnCompleted(Action continuation) => _cancellationToken.Register(continuation); } } diff --git a/backend/api/api.csproj b/backend/api/api.csproj index 7df3efc30..112d6963e 100644 --- a/backend/api/api.csproj +++ b/backend/api/api.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 enable enable f888e68d-d6f9-4dad-adb2-269ae53a9ea1 @@ -12,21 +12,22 @@ - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + + - - - + + + diff --git a/backend/api/appsettings.Test.json b/backend/api/appsettings.Test.json index 9aeca8dfc..efec6b3c0 100644 --- a/backend/api/appsettings.Test.json +++ b/backend/api/appsettings.Test.json @@ -1,4 +1,9 @@ { + "AzureAd": { + "TenantId": "3aa4a235-b6e2-48d5-9195-7fcf05b459b0", + "Instance": "https://login.microsoftonline.com", + "ClientId": "testId" + }, "KeyVault": { "UseKeyVault": false },