From f4958b445ab8f81d66b589ab3d9930e14652e6b1 Mon Sep 17 00:00:00 2001 From: Thomas Anderson Date: Tue, 5 Nov 2024 13:41:05 +0000 Subject: [PATCH 1/7] initial commit for relationships --- Cdms.Consumers/GmrConsumer.cs | 2 +- Cdms.Model/Gvms/Gmr.cs | 5 ++ Cdms.Model/Gvms/Gmr.g.cs | 8 -- Cdms.Model/Ipaffs/ImportNotification.cs | 2 +- Cdms.Model/Relationships/GmrRelationships.cs | 15 ++++ Cdms.Model/Relationships/ITdmRelationships.cs | 2 +- .../Relationships/MovementTdmRelationships.cs | 4 +- .../NotificationTdmRelationships.cs | 4 +- Cdms.Types.Gvms.Mapping.V1/Class1.cs | 7 -- Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs | 56 +++++++++++- .../JsonApi/CdmsResponseModelAdapter.cs | 86 +++++++++++++++++++ CdmsBackend/Program.cs | 3 + .../JsonApiClient/JsonApiClient.cs | 5 +- CdmsBackent.IntegrationTests/SmokeTests.cs | 2 +- 14 files changed, 176 insertions(+), 25 deletions(-) create mode 100644 Cdms.Model/Relationships/GmrRelationships.cs delete mode 100644 Cdms.Types.Gvms.Mapping.V1/Class1.cs create mode 100644 CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs diff --git a/Cdms.Consumers/GmrConsumer.cs b/Cdms.Consumers/GmrConsumer.cs index 1c2d1b8..741c67a 100644 --- a/Cdms.Consumers/GmrConsumer.cs +++ b/Cdms.Consumers/GmrConsumer.cs @@ -14,7 +14,7 @@ public async Task OnHandle(SearchGmrsForDeclarationIdsResponse message) { foreach (var gmr in message.Gmrs) { - var internalGmr = GmrMapper.Map(gmr); + var internalGmr = GrmWithTransformMapper.MapWithTransform(gmr); var existingGmr = await dbContext.Gmrs.Find(internalGmr.Id); if (existingGmr is null) diff --git a/Cdms.Model/Gvms/Gmr.cs b/Cdms.Model/Gvms/Gmr.cs index 7715757..457201b 100644 --- a/Cdms.Model/Gvms/Gmr.cs +++ b/Cdms.Model/Gvms/Gmr.cs @@ -2,6 +2,7 @@ using System.Text.Json.Serialization; using Cdms.Model.Auditing; using Cdms.Model.Data; +using Cdms.Model.Relationships; using JsonApiDotNetCore.MongoDb.Resources; using JsonApiDotNetCore.Resources.Annotations; using MongoDB.Bson.Serialization.Attributes; @@ -37,4 +38,8 @@ public string? StringId public string? LocalId { get; set; } [Attr] public List AuditEntries { get; set; } = new List(); + + [Attr] + [JsonPropertyName("relationships")] + public GmrRelationships Relationships { get; set; } = new GmrRelationships(); } \ No newline at end of file diff --git a/Cdms.Model/Gvms/Gmr.g.cs b/Cdms.Model/Gvms/Gmr.g.cs index b850572..75b8300 100644 --- a/Cdms.Model/Gvms/Gmr.g.cs +++ b/Cdms.Model/Gvms/Gmr.g.cs @@ -128,14 +128,6 @@ public partial class Gmr // [System.ComponentModel.Description("")] public ActualCrossing? ActualCrossing { get; set; } - - /// - /// - /// - [Attr] - [System.ComponentModel.Description("")] - public Declarations? Declarations { get; set; } - } diff --git a/Cdms.Model/Ipaffs/ImportNotification.cs b/Cdms.Model/Ipaffs/ImportNotification.cs index 26ed229..d8286a4 100644 --- a/Cdms.Model/Ipaffs/ImportNotification.cs +++ b/Cdms.Model/Ipaffs/ImportNotification.cs @@ -15,7 +15,7 @@ public partial class ImportNotification : IMongoIdentifiable, IDataEntity private int? matchReference; //// This field is used by the jsonapi-consumer to control the correct casing in the type field - [JsonIgnore] public string Type { get; set; } = "importnotifications"; + [JsonIgnore] public string Type { get; set; } = "import-notifications"; //[BsonId(IdGenerator = typeof(StringObjectIdGenerator))] [JsonIgnore] diff --git a/Cdms.Model/Relationships/GmrRelationships.cs b/Cdms.Model/Relationships/GmrRelationships.cs new file mode 100644 index 0000000..a0d5fbe --- /dev/null +++ b/Cdms.Model/Relationships/GmrRelationships.cs @@ -0,0 +1,15 @@ +using JsonApiDotNetCore.Resources.Annotations; + +namespace Cdms.Model.Relationships; + +public class GmrRelationships : ITdmRelationships +{ + [Attr] public TdmRelationshipObject Transits { get; set; } = TdmRelationshipObject.CreateDefault(); + + [Attr] public TdmRelationshipObject Customs { get; set; } = TdmRelationshipObject.CreateDefault(); + + public List<(string, TdmRelationshipObject)> GetRelationshipObjects() + { + return [("transits", Transits), ("customs", Customs)]; + } +} \ No newline at end of file diff --git a/Cdms.Model/Relationships/ITdmRelationships.cs b/Cdms.Model/Relationships/ITdmRelationships.cs index 47bba5d..814141b 100644 --- a/Cdms.Model/Relationships/ITdmRelationships.cs +++ b/Cdms.Model/Relationships/ITdmRelationships.cs @@ -2,5 +2,5 @@ namespace Cdms.Model.Relationships; public interface ITdmRelationships { - public (string, TdmRelationshipObject) GetRelationshipObject(); + public List<(string, TdmRelationshipObject)> GetRelationshipObjects(); } \ No newline at end of file diff --git a/Cdms.Model/Relationships/MovementTdmRelationships.cs b/Cdms.Model/Relationships/MovementTdmRelationships.cs index aa7f130..900b966 100644 --- a/Cdms.Model/Relationships/MovementTdmRelationships.cs +++ b/Cdms.Model/Relationships/MovementTdmRelationships.cs @@ -6,8 +6,8 @@ public class MovementTdmRelationships : ITdmRelationships { [Attr] public TdmRelationshipObject Notifications { get; set; } = TdmRelationshipObject.CreateDefault(); - public (string, TdmRelationshipObject) GetRelationshipObject() + public List<(string, TdmRelationshipObject)> GetRelationshipObjects() { - return ("notifications", Notifications); + return [("notifications", Notifications)]; } } \ No newline at end of file diff --git a/Cdms.Model/Relationships/NotificationTdmRelationships.cs b/Cdms.Model/Relationships/NotificationTdmRelationships.cs index b634e4b..44ce54f 100644 --- a/Cdms.Model/Relationships/NotificationTdmRelationships.cs +++ b/Cdms.Model/Relationships/NotificationTdmRelationships.cs @@ -6,8 +6,8 @@ public class NotificationTdmRelationships : ITdmRelationships { [Attr] public TdmRelationshipObject Movements { get; set; } = TdmRelationshipObject.CreateDefault(); - public (string, TdmRelationshipObject) GetRelationshipObject() + public List<(string, TdmRelationshipObject)> GetRelationshipObjects() { - return ("movements", Movements); + return [("movements", Movements)]; } } \ No newline at end of file diff --git a/Cdms.Types.Gvms.Mapping.V1/Class1.cs b/Cdms.Types.Gvms.Mapping.V1/Class1.cs deleted file mode 100644 index aa44752..0000000 --- a/Cdms.Types.Gvms.Mapping.V1/Class1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Cdms.Types.Gmr.Mapping.V1 -{ - public class Class1 - { - - } -} diff --git a/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs b/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs index 53de81c..a326e68 100644 --- a/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs +++ b/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs @@ -10,8 +10,62 @@ #nullable enable +using Cdms.Model.Extensions; +using Cdms.Model.Relationships; + namespace Cdms.Types.Gvms.Mapping; +public static class GrmWithTransformMapper +{ + public static Cdms.Model.Gvms.Gmr MapWithTransform(Cdms.Types.Gvms.Gmr from) + { + if (from is null) + { + return default!; + } + + var gmr = GmrMapper.Map(from); + Map(from, gmr); + return gmr; + } + + private static void Map(Gmr from, Model.Gvms.Gmr to) + { + if (from.Declarations.Customs is not null) + { + to.Relationships.Customs = new TdmRelationshipObject() + { + Links = new RelationshipLinks() + { + Self = "http://example.com/articles/1/relationships/author", + Related = "http://example.com/articles/1/author", + }, + Data = from.Declarations.Customs.Select(x => new RelationshipDataItem() + { + Id = x.Id, Type = "importnotification" + }).ToList() + }; + } + + if (from.Declarations.Transits is not null) + { + to.Relationships.Transits = new TdmRelationshipObject() + { + Links = new RelationshipLinks() + { + Self = "http://example.com/articles/1/relationships/author", + Related = "http://example.com/articles/1/author", + }, + Data = from.Declarations.Transits.Select(x => new RelationshipDataItem() + { + Id = x.Id, + Type = "movement" + }).ToList() + }; + } + } +} + public static class GmrMapper { public static Cdms.Model.Gvms.Gmr Map(Cdms.Types.Gvms.Gmr from) @@ -34,7 +88,7 @@ public static Cdms.Model.Gvms.Gmr Map(Cdms.Types.Gvms.Gmr from) to.PlannedCrossing = PlannedCrossingMapper.Map(from?.PlannedCrossing); to.CheckedInCrossing = CheckedInCrossingMapper.Map(from?.CheckedInCrossing); to.ActualCrossing = ActualCrossingMapper.Map(from?.ActualCrossing); - to.Declarations = DeclarationsMapper.Map(from?.Declarations); + return to; } } diff --git a/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs new file mode 100644 index 0000000..56170b6 --- /dev/null +++ b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs @@ -0,0 +1,86 @@ +using Cdms.Model.Relationships; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Queries; +using JsonApiDotNetCore.QueryStrings; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCore.Serialization.Response; +using RelationshipLinks = JsonApiDotNetCore.Serialization.Objects.RelationshipLinks; + +namespace CdmsBackend.JsonApi; + +public class CdmsResponseModelAdapter( + IJsonApiRequest request, + IJsonApiOptions options, + ILinkBuilder linkBuilder, + IMetaBuilder metaBuilder, + IResourceDefinitionAccessor resourceDefinitionAccessor, + IEvaluatedIncludeCache evaluatedIncludeCache, + ISparseFieldSetCache sparseFieldSetCache, + IRequestQueryStringAccessor requestQueryStringAccessor) + : IResponseModelAdapter +{ + private readonly IResponseModelAdapter inner = new ResponseModelAdapter(request, options, linkBuilder, metaBuilder, + resourceDefinitionAccessor, + evaluatedIncludeCache, sparseFieldSetCache, requestQueryStringAccessor); + + public Document Convert(object? model) + { + var document = inner.Convert(model); + if (document.Data.Value is null) + { + return document; + } + + var listOfResourceObjects = document.Data.ManyValue is not null + ? document.Data.ManyValue.ToList() + : new List() { document.Data.SingleValue }; + + + foreach (var resourceObject in listOfResourceObjects) + { + if (resourceObject.Attributes.TryGetValue("relationships", out var value)) + { + var relationships = (value as ITdmRelationships).GetRelationshipObjects(); + resourceObject.Relationships = new Dictionary(); + + foreach (var relationship in relationships) + { + var list = relationship.Item2.Data.Select(item => new ResourceIdentifierObject() + { + Type = item.Type, + Id = item.Id, + //Lid = item.Id, + Meta = new Dictionary() + { + { "matched", item.Matched }, + { "sourceItem", item.SourceItem }, + { "destinationItem", item.DestinationItem }, + { "matchingLevel", item.MatchingLevel }, + { "self", item.Links?.Self } + }, + }) + .ToList(); + + resourceObject.Relationships.Add(relationship.Item1, + new RelationshipObject() + { + Meta = new Dictionary() { { "matched", relationship.Item2.Matched } }, + Links = new RelationshipLinks() + { + Self = relationship.Item2.Links?.Self, + Related = relationship.Item2?.Links?.Related + }, + Data = new SingleOrManyData(list) + }); + } + + resourceObject.Attributes.Remove("relationships"); + } + } + + + return document; + } +} \ No newline at end of file diff --git a/CdmsBackend/Program.cs b/CdmsBackend/Program.cs index 94a3c0c..e97a85b 100644 --- a/CdmsBackend/Program.cs +++ b/CdmsBackend/Program.cs @@ -23,6 +23,8 @@ using OpenTelemetry.Trace; using JsonApiDotNetCore.MongoDb.Repositories; using JsonApiDotNetCore.Repositories; +using CdmsBackend.JsonApi; +using JsonApiDotNetCore.Serialization.Response; //-------- Configure the WebApplication builder------------------// @@ -122,6 +124,7 @@ static void ConfigureJsonApiOptions(JsonApiOptions options) discovery => discovery.AddAssembly(Assembly.Load("Cdms.Model"))); builder.Services.AddJsonApiMongoDb(); + builder.Services.AddScoped(); builder.Services.AddScoped(typeof(IResourceReadRepository<,>), typeof(MongoRepository<,>)); builder.Services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(MongoRepository<,>)); builder.Services.AddScoped(typeof(IResourceRepository<,>), typeof(MongoRepository<,>)); diff --git a/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs b/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs index 9c4f84c..a80a3eb 100644 --- a/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs +++ b/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs @@ -1,9 +1,10 @@ -using System.Net.Http.Headers; +using System.Net.Http.Headers; using JsonApiSerializer; using JsonApiSerializer.JsonApi; using Microsoft.AspNetCore.WebUtilities; using Newtonsoft.Json; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace CdmsBackend.IntegrationTests.JsonApiClient; @@ -66,6 +67,8 @@ public Response Get( if (responseMessage.IsSuccessStatusCode) { + var r = JsonSerializer.Deserialize(s); + response.DocumentRoot = JsonConvert.DeserializeObject>( responseMessage.Content.ReadAsStringAsync().Result, settings); diff --git a/CdmsBackent.IntegrationTests/SmokeTests.cs b/CdmsBackent.IntegrationTests/SmokeTests.cs index 04f6537..9b6a781 100644 --- a/CdmsBackent.IntegrationTests/SmokeTests.cs +++ b/CdmsBackent.IntegrationTests/SmokeTests.cs @@ -53,7 +53,7 @@ public async Task SyncNotifications() factory.GetDbContext().Inbox.Count().Should().Be(5); // Check Api - var jsonClientResponse = client.AsJsonApiClient().Get("api/importnotifications"); + var jsonClientResponse = client.AsJsonApiClient().Get("api/import-notifications"); jsonClientResponse.IsSuccess.Should().BeTrue(); jsonClientResponse.DocumentRoot.Data.Length.Should().Be(5); } From edf1ad22e7b36ac7c502a1c48d4719e9273a75ee Mon Sep 17 00:00:00 2001 From: Thomas Anderson Date: Wed, 6 Nov 2024 08:12:34 +0000 Subject: [PATCH 2/7] Added Integration test for fetching a Gmr and verifying the relationship --- Cdms.Model/Extensions/LinksBuilder.cs | 34 +++++++- Cdms.Model/Ipaffs/ImportNotification.cs | 4 +- Cdms.Model/Movement.cs | 3 +- .../Relationships/RelationshipDataItem.cs | 2 +- .../Relationships/TdmRelationshipObject.cs | 4 +- Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs | 52 ------------ .../GrmWithTransformMapper.cs | 54 ++++++++++++ .../JsonApi/CdmsResponseModelAdapter.cs | 48 ++++++++--- CdmsBackent.IntegrationTests/GmrTests.cs | 82 +++++++++++++++++++ .../IntegrationTestsApplicationFactory.cs | 3 +- .../{ => Helpers}/LocalBlobItem.cs | 2 +- .../{ => Helpers}/LocalBlobService.cs | 4 +- .../{ => Helpers}/MongoRunnerProvider.cs | 6 +- .../JsonApiClient/JsonApiClient.cs | 73 ++++------------- .../JsonApiClient/JsonApiDocument.cs | 26 ++++++ .../JsonApiClient/ManyItemsJsonApiDocument.cs | 14 ++++ .../SingleItemJsonApiDocument.cs | 13 +++ CdmsBackent.IntegrationTests/SmokeTests.cs | 26 +++--- 18 files changed, 300 insertions(+), 150 deletions(-) create mode 100644 Cdms.Types.Gvms.Mapping.V1/GrmWithTransformMapper.cs create mode 100644 CdmsBackent.IntegrationTests/GmrTests.cs rename CdmsBackent.IntegrationTests/{ => Helpers}/IntegrationTestsApplicationFactory.cs (97%) rename CdmsBackent.IntegrationTests/{ => Helpers}/LocalBlobItem.cs (91%) rename CdmsBackent.IntegrationTests/{ => Helpers}/LocalBlobService.cs (91%) rename CdmsBackent.IntegrationTests/{ => Helpers}/MongoRunnerProvider.cs (94%) create mode 100644 CdmsBackent.IntegrationTests/JsonApiClient/JsonApiDocument.cs create mode 100644 CdmsBackent.IntegrationTests/JsonApiClient/ManyItemsJsonApiDocument.cs create mode 100644 CdmsBackent.IntegrationTests/JsonApiClient/SingleItemJsonApiDocument.cs diff --git a/Cdms.Model/Extensions/LinksBuilder.cs b/Cdms.Model/Extensions/LinksBuilder.cs index 8cd3901..d9ac2bb 100644 --- a/Cdms.Model/Extensions/LinksBuilder.cs +++ b/Cdms.Model/Extensions/LinksBuilder.cs @@ -6,12 +6,12 @@ public static class Notification { public static string BuildSelfNotificationLink(string id) { - return LinksBuilder.BuildSelfLink("notification", id); + return LinksBuilder.BuildSelfLink("import-notifications", id); } public static string BuildRelatedMovementLink(string id) { - return LinksBuilder.BuildRelatedLink("notification", id, "movements"); + return LinksBuilder.BuildRelatedLink("import-notifications", id, "movements"); } } @@ -24,7 +24,30 @@ public static string BuildSelfMovementLink(string id) public static string BuildRelatedMovementLink(string id) { - return LinksBuilder.BuildRelatedLink("movements", id, "notifications"); + return LinksBuilder.BuildRelatedLink("movements", id, "import-notifications"); + } + } + + public static class Gmr + { + public static string BuildSelfRelationshipCustomsLink(string id) + { + return LinksBuilder.BuildSelfRelationshipLink("gmr", id, "import-notifications"); + } + + public static string BuildSelfRelationshipTransitsLink(string id) + { + return LinksBuilder.BuildSelfRelationshipLink("gmr", id, "movement"); + } + + public static string BuildRelatedTransitLink(string id) + { + return LinksBuilder.BuildSelfLink("movements", id); + } + + public static string BuildRelatedCustomsLink(string id) + { + return LinksBuilder.BuildSelfLink("import-notifications", id); } } @@ -37,4 +60,9 @@ public static string BuildRelatedLink(string type, string id, string related) { return $"/api/{type}/{id}/{related}"; } + + public static string BuildSelfRelationshipLink(string type, string id, string relationship) + { + return $"/api/{type}/{id}/relationships/{relationship}"; + } } \ No newline at end of file diff --git a/Cdms.Model/Ipaffs/ImportNotification.cs b/Cdms.Model/Ipaffs/ImportNotification.cs index 1e171ce..420ae30 100644 --- a/Cdms.Model/Ipaffs/ImportNotification.cs +++ b/Cdms.Model/Ipaffs/ImportNotification.cs @@ -15,7 +15,7 @@ public partial class ImportNotification : IMongoIdentifiable, IDataEntity private int? matchReference; //// This field is used by the jsonapi-consumer to control the correct casing in the type field - [JsonIgnore] public string Type { get; set; } = "import-notifications"; + [JsonIgnore] public string Type { get; set; } = "import-notification"; //[BsonId(IdGenerator = typeof(StringObjectIdGenerator))] [JsonIgnore] @@ -125,7 +125,7 @@ public void AddRelationship(string type, TdmRelationshipObject relationship) } } - Relationships.Movements.Matched = Relationships.Movements.Data.Any(x => x.Matched); + Relationships.Movements.Matched = Relationships.Movements.Data.Any(x => x.Matched.GetValueOrDefault()); } public void Changed(AuditEntry auditEntry) diff --git a/Cdms.Model/Movement.cs b/Cdms.Model/Movement.cs index 440491c..9a21be9 100644 --- a/Cdms.Model/Movement.cs +++ b/Cdms.Model/Movement.cs @@ -108,7 +108,8 @@ public void AddRelationship(string type, TdmRelationshipObject relationship) Relationships.Notifications.Matched = Items .Select(x => x.ItemNumber) - .All(itemNumber => Relationships.Notifications.Data.Any(x => x.Matched && x.SourceItem == itemNumber)); + .All(itemNumber => + Relationships.Notifications.Data.Any(x => x.Matched.GetValueOrDefault() && x.SourceItem == itemNumber)); } public void Update(AuditEntry auditEntry) diff --git a/Cdms.Model/Relationships/RelationshipDataItem.cs b/Cdms.Model/Relationships/RelationshipDataItem.cs index 53eecf9..b5f9377 100644 --- a/Cdms.Model/Relationships/RelationshipDataItem.cs +++ b/Cdms.Model/Relationships/RelationshipDataItem.cs @@ -6,7 +6,7 @@ namespace Cdms.Model.Relationships; public sealed class RelationshipDataItem { - [Attr] public bool Matched { get; set; } = default!; + [Attr] public bool? Matched { get; set; } = default!; [Attr] public string Type { get; set; } diff --git a/Cdms.Model/Relationships/TdmRelationshipObject.cs b/Cdms.Model/Relationships/TdmRelationshipObject.cs index 09c035b..68fc034 100644 --- a/Cdms.Model/Relationships/TdmRelationshipObject.cs +++ b/Cdms.Model/Relationships/TdmRelationshipObject.cs @@ -4,7 +4,7 @@ namespace Cdms.Model.Relationships; public sealed class TdmRelationshipObject { - [Attr] public bool Matched { get; set; } = default!; + [Attr] public bool? Matched { get; set; } = default!; [Attr] public RelationshipLinks Links { get; set; } @@ -12,6 +12,6 @@ public sealed class TdmRelationshipObject public static TdmRelationshipObject CreateDefault() { - return new TdmRelationshipObject() { Matched = false }; + return new TdmRelationshipObject(); } } \ No newline at end of file diff --git a/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs b/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs index a326e68..dff80f4 100644 --- a/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs +++ b/Cdms.Types.Gvms.Mapping.V1/GmrMapper.g.cs @@ -11,61 +11,9 @@ using Cdms.Model.Extensions; -using Cdms.Model.Relationships; namespace Cdms.Types.Gvms.Mapping; -public static class GrmWithTransformMapper -{ - public static Cdms.Model.Gvms.Gmr MapWithTransform(Cdms.Types.Gvms.Gmr from) - { - if (from is null) - { - return default!; - } - - var gmr = GmrMapper.Map(from); - Map(from, gmr); - return gmr; - } - - private static void Map(Gmr from, Model.Gvms.Gmr to) - { - if (from.Declarations.Customs is not null) - { - to.Relationships.Customs = new TdmRelationshipObject() - { - Links = new RelationshipLinks() - { - Self = "http://example.com/articles/1/relationships/author", - Related = "http://example.com/articles/1/author", - }, - Data = from.Declarations.Customs.Select(x => new RelationshipDataItem() - { - Id = x.Id, Type = "importnotification" - }).ToList() - }; - } - - if (from.Declarations.Transits is not null) - { - to.Relationships.Transits = new TdmRelationshipObject() - { - Links = new RelationshipLinks() - { - Self = "http://example.com/articles/1/relationships/author", - Related = "http://example.com/articles/1/author", - }, - Data = from.Declarations.Transits.Select(x => new RelationshipDataItem() - { - Id = x.Id, - Type = "movement" - }).ToList() - }; - } - } -} - public static class GmrMapper { public static Cdms.Model.Gvms.Gmr Map(Cdms.Types.Gvms.Gmr from) diff --git a/Cdms.Types.Gvms.Mapping.V1/GrmWithTransformMapper.cs b/Cdms.Types.Gvms.Mapping.V1/GrmWithTransformMapper.cs new file mode 100644 index 0000000..893a210 --- /dev/null +++ b/Cdms.Types.Gvms.Mapping.V1/GrmWithTransformMapper.cs @@ -0,0 +1,54 @@ +using Cdms.Model.Extensions; +using Cdms.Model.Relationships; + +namespace Cdms.Types.Gvms.Mapping; + +public static class GrmWithTransformMapper +{ + public static Cdms.Model.Gvms.Gmr MapWithTransform(Cdms.Types.Gvms.Gmr from) + { + if (from is null) + { + return default!; + } + + var gmr = GmrMapper.Map(from); + Map(from, gmr); + return gmr; + } + + private static void Map(Gmr from, Model.Gvms.Gmr to) + { + if (from.Declarations.Customs is not null) + { + to.Relationships.Customs = new TdmRelationshipObject() + { + Links = new RelationshipLinks() + { + Self = LinksBuilder.Gmr.BuildSelfRelationshipCustomsLink(":id"), + Related = LinksBuilder.Gmr.BuildRelatedCustomsLink(":id"), + }, + Data = from.Declarations.Customs.Select(x => new RelationshipDataItem() + { + Id = x.Id, Type = "import-notifications" + }).ToList() + }; + } + + if (from.Declarations.Transits is not null) + { + to.Relationships.Transits = new TdmRelationshipObject() + { + Links = new RelationshipLinks() + { + Self = LinksBuilder.Gmr.BuildSelfRelationshipTransitsLink(":id"), + Related = LinksBuilder.Gmr.BuildRelatedTransitLink(":id"), + }, + Data = from.Declarations.Transits.Select(x => new RelationshipDataItem() + { + Id = x.Id, Type = "movement" + }).ToList() + }; + } + } +} \ No newline at end of file diff --git a/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs index 56170b6..cf8dec5 100644 --- a/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs +++ b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs @@ -47,26 +47,50 @@ public Document Convert(object? model) foreach (var relationship in relationships) { - var list = relationship.Item2.Data.Select(item => new ResourceIdentifierObject() + var list = relationship.Item2.Data.Select(item => { - Type = item.Type, - Id = item.Id, - //Lid = item.Id, - Meta = new Dictionary() + var meta = new Dictionary(); + if (item.Matched.HasValue) { - { "matched", item.Matched }, - { "sourceItem", item.SourceItem }, - { "destinationItem", item.DestinationItem }, - { "matchingLevel", item.MatchingLevel }, - { "self", item.Links?.Self } - }, + meta.Add("matched", item.Matched); + } + + if (item.SourceItem.HasValue) + { + meta.Add("sourceItem", item.SourceItem); + } + + if (item.DestinationItem.HasValue) + { + meta.Add("destinationItem", item.DestinationItem); + } + + if (item.MatchingLevel.HasValue) + { + meta.Add("matchingLevel", item.MatchingLevel); + } + + if (!string.IsNullOrEmpty(item.Links?.Self)) + { + meta.Add("self", item.Links?.Self); + } + + return new ResourceIdentifierObject() { Type = item.Type, Id = item.Id, Meta = meta, }; }) .ToList(); + + var meta = new Dictionary(); + + if (relationship.Item2.Matched.HasValue) + { + meta.Add("matched", relationship.Item2.Matched); + } + resourceObject.Relationships.Add(relationship.Item1, new RelationshipObject() { - Meta = new Dictionary() { { "matched", relationship.Item2.Matched } }, + Meta = meta, Links = new RelationshipLinks() { Self = relationship.Item2.Links?.Self, diff --git a/CdmsBackent.IntegrationTests/GmrTests.cs b/CdmsBackent.IntegrationTests/GmrTests.cs new file mode 100644 index 0000000..2932677 --- /dev/null +++ b/CdmsBackent.IntegrationTests/GmrTests.cs @@ -0,0 +1,82 @@ +using System.Net; +using System.Text; +using System.Text.Json; +using Cdms.Business.Commands; +using Cdms.Model; +using CdmsBackend.IntegrationTests.Helpers; +using CdmsBackend.IntegrationTests.JsonApiClient; +using FluentAssertions; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; +using Xunit.Abstractions; + +namespace CdmsBackend.IntegrationTests; + +[Trait("Category", "Integration")] +public class GmrTests : + IClassFixture, IAsyncLifetime +{ + private readonly HttpClient client; + private readonly IntegrationTestsApplicationFactory factory; + + public GmrTests(IntegrationTestsApplicationFactory factory, ITestOutputHelper testOutputHelper) + { + this.factory = factory; + this.factory.testOutputHelper = testOutputHelper; + client = + this.factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); + } + + public async Task InitializeAsync() + { + await factory.ClearDb(client); + + await MakeSyncGmrsRequest(new SyncGmrsCommand() + { + SyncPeriod = SyncPeriod.All, + RootFolder = "SmokeTest" + }); + + // Assert + await Task.Delay(TimeSpan.FromSeconds(1)); + } + + [Fact] + public async Task FetchSingleGmrTest() + { + + //Act + var jsonClientResponse = client.AsJsonApiClient().GetById("GMRAPOQSPDUG","api/gmrs"); + + // Assert + jsonClientResponse.Data.Relationships["customs"].Links.Self.Should().Be("/api/gmr/:id/relationships/import-notifications"); + jsonClientResponse.Data.Relationships["customs"].Links.Related.Should().Be("/api/import-notifications/:id"); + + jsonClientResponse.Data.Relationships["customs"].Data.ManyValue[0].Id.Should().Be("56GB123456789AB043"); + jsonClientResponse.Data.Relationships["customs"].Data.ManyValue[0].Type.Should().Be("import-notifications"); + } + + + + + private Task MakeSyncGmrsRequest(SyncGmrsCommand command) + { + return PostCommand(command, "/sync/gmrs"); + } + + + private Task PostCommand(T command, string uri) + { + var jsonData = JsonSerializer.Serialize(command); + HttpContent content = new StringContent(jsonData, Encoding.UTF8, "application/json"); + + return client.PostAsync(uri, content); + } + + + + public Task DisposeAsync() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/CdmsBackent.IntegrationTests/IntegrationTestsApplicationFactory.cs b/CdmsBackent.IntegrationTests/Helpers/IntegrationTestsApplicationFactory.cs similarity index 97% rename from CdmsBackent.IntegrationTests/IntegrationTestsApplicationFactory.cs rename to CdmsBackent.IntegrationTests/Helpers/IntegrationTestsApplicationFactory.cs index 9ccf422..47e2b32 100644 --- a/CdmsBackent.IntegrationTests/IntegrationTestsApplicationFactory.cs +++ b/CdmsBackent.IntegrationTests/Helpers/IntegrationTestsApplicationFactory.cs @@ -1,7 +1,6 @@ using Azure.Storage.Blobs; using Cdms.Backend.Data.Extensions; using Cdms.BlobService; -using Cdms.Consumers.Tests; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; @@ -13,7 +12,7 @@ using Microsoft.Extensions.Logging; using Xunit.Abstractions; -namespace CdmsBackend.IntegrationTests; +namespace CdmsBackend.IntegrationTests.Helpers; public class IntegrationTestsApplicationFactory : WebApplicationFactory { diff --git a/CdmsBackent.IntegrationTests/LocalBlobItem.cs b/CdmsBackent.IntegrationTests/Helpers/LocalBlobItem.cs similarity index 91% rename from CdmsBackent.IntegrationTests/LocalBlobItem.cs rename to CdmsBackent.IntegrationTests/Helpers/LocalBlobItem.cs index a739570..b674a1f 100644 --- a/CdmsBackent.IntegrationTests/LocalBlobItem.cs +++ b/CdmsBackent.IntegrationTests/Helpers/LocalBlobItem.cs @@ -1,6 +1,6 @@ using Cdms.BlobService; -namespace CdmsBackend.IntegrationTests; +namespace CdmsBackend.IntegrationTests.Helpers; public class LocalBlobItem(string name) : IBlobItem { diff --git a/CdmsBackent.IntegrationTests/LocalBlobService.cs b/CdmsBackent.IntegrationTests/Helpers/LocalBlobService.cs similarity index 91% rename from CdmsBackent.IntegrationTests/LocalBlobService.cs rename to CdmsBackent.IntegrationTests/Helpers/LocalBlobService.cs index 96ccd80..7f4471a 100644 --- a/CdmsBackent.IntegrationTests/LocalBlobService.cs +++ b/CdmsBackent.IntegrationTests/Helpers/LocalBlobService.cs @@ -1,6 +1,6 @@ -using Cdms.BlobService; +using Cdms.BlobService; -namespace CdmsBackend.IntegrationTests; +namespace CdmsBackend.IntegrationTests.Helpers; public class LocalBlobService(string root) : IBlobService { diff --git a/CdmsBackent.IntegrationTests/MongoRunnerProvider.cs b/CdmsBackent.IntegrationTests/Helpers/MongoRunnerProvider.cs similarity index 94% rename from CdmsBackent.IntegrationTests/MongoRunnerProvider.cs rename to CdmsBackent.IntegrationTests/Helpers/MongoRunnerProvider.cs index 6e84bad..c4cc921 100644 --- a/CdmsBackent.IntegrationTests/MongoRunnerProvider.cs +++ b/CdmsBackent.IntegrationTests/Helpers/MongoRunnerProvider.cs @@ -1,6 +1,6 @@ using EphemeralMongo; -namespace Cdms.Consumers.Tests; +namespace CdmsBackend.IntegrationTests.Helpers; internal sealed class MongoRunnerProvider { @@ -24,7 +24,9 @@ public IMongoRunner Get() { // Single-node replica set mode is required for transaction support in MongoDB. //UseSingleNodeReplicaSet = true, - KillMongoProcessesWhenCurrentProcessExits = true, AdditionalArguments = "--quiet", MongoPort = 57020 + KillMongoProcessesWhenCurrentProcessExits = true, + AdditionalArguments = "--quiet", + MongoPort = 57020 }; _runner = MongoRunner.Run(runnerOptions); diff --git a/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs b/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs index a80a3eb..24ff9f5 100644 --- a/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs +++ b/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiClient.cs @@ -1,8 +1,8 @@ using System.Net.Http.Headers; +using System.Text.Json; +using JsonApiDotNetCore.Serialization.JsonConverters; using JsonApiSerializer; -using JsonApiSerializer.JsonApi; using Microsoft.AspNetCore.WebUtilities; -using Newtonsoft.Json; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; using JsonSerializer = System.Text.Json.JsonSerializer; @@ -14,24 +14,13 @@ public class JsonApiClient(HttpClient client) static MediaTypeWithQualityHeaderValue contentType = new MediaTypeWithQualityHeaderValue(strContentType); - public static void HandleDeserializationError(object sender, ErrorEventArgs errorArgs) - { - errorArgs.ErrorContext.Handled = true; - } - - /// - /// Jsonapi serializer settings. - /// - static JsonApiSerializerSettings settings = new JsonApiSerializerSettings() { Error = HandleDeserializationError, }; - public Response Get( + public ManyItemsJsonApiDocument Get( string path, Dictionary query = null, Dictionary headers = null, - IList relations = null) where TRequest : class, new() + IList relations = null) { - var response = new Response(); - client.DefaultRequestHeaders.Accept.Add(contentType); if (headers != null) @@ -59,38 +48,21 @@ public Response Get( HttpResponseMessage responseMessage = client.GetAsync(uri).Result; - response.HttpStatusCode = responseMessage.StatusCode; - - response.IsSuccess = responseMessage.IsSuccessStatusCode; - var s = responseMessage.Content.ReadAsStringAsync().Result; - if (responseMessage.IsSuccessStatusCode) - { - var r = JsonSerializer.Deserialize(s); - - response.DocumentRoot = - JsonConvert.DeserializeObject>( - responseMessage.Content.ReadAsStringAsync().Result, settings); - } - else - { - response.Error = - JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result, settings); - } - - return response; + return JsonSerializer.Deserialize(s, + new JsonSerializerOptions() + { + Converters = { new SingleOrManyDataConverterFactory() }, PropertyNameCaseInsensitive = true + }); } - public Response GetById(string id, + public SingleItemJsonApiDocument GetById(string id, string path, Dictionary query = null, Dictionary headers = null, - IList relations = null) where TRequest : class, new() + IList relations = null) { - var response = new Response(); - - client.DefaultRequestHeaders.Accept.Add(contentType); if (headers != null) @@ -118,23 +90,12 @@ public Response GetById(string id, HttpResponseMessage responseMessage = client.GetAsync(uri).Result; - response.HttpStatusCode = responseMessage.StatusCode; - - response.IsSuccess = responseMessage.IsSuccessStatusCode; - - if (responseMessage.IsSuccessStatusCode) - { - response.DocumentRoot = - JsonConvert.DeserializeObject>( - responseMessage.Content.ReadAsStringAsync().Result, settings); - } - else - { - response.Error = - JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result, settings); - } - + var s = responseMessage.Content.ReadAsStringAsync().Result; - return response; + return JsonSerializer.Deserialize(s, + new JsonSerializerOptions() + { + Converters = { new SingleOrManyDataConverterFactory() }, PropertyNameCaseInsensitive = true + }); } } \ No newline at end of file diff --git a/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiDocument.cs b/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiDocument.cs new file mode 100644 index 0000000..d7b3299 --- /dev/null +++ b/CdmsBackent.IntegrationTests/JsonApiClient/JsonApiDocument.cs @@ -0,0 +1,26 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using JsonApiDotNetCore.Serialization.JsonConverters; +using JsonApiDotNetCore.Serialization.Objects; + +namespace CdmsBackend.IntegrationTests.JsonApiClient; + +public abstract class JsonApiDocument +{ + protected JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions() + { + Converters = { new SingleOrManyDataConverterFactory() }, + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + [JsonPropertyName("links")] public TopLevelLinks? Links { get; set; } + + [JsonPropertyName("data")] public T Data { get; set; } + + [JsonPropertyName("errors")] public IList? Errors { get; set; } + + [JsonPropertyName("included")] public IList? Included { get; set; } + + [JsonPropertyName("meta")] public IDictionary? Meta { get; set; } +} \ No newline at end of file diff --git a/CdmsBackent.IntegrationTests/JsonApiClient/ManyItemsJsonApiDocument.cs b/CdmsBackent.IntegrationTests/JsonApiClient/ManyItemsJsonApiDocument.cs new file mode 100644 index 0000000..a61e064 --- /dev/null +++ b/CdmsBackent.IntegrationTests/JsonApiClient/ManyItemsJsonApiDocument.cs @@ -0,0 +1,14 @@ +using System.Text.Json; +using JsonApiDotNetCore.Serialization.Objects; + +namespace CdmsBackend.IntegrationTests.JsonApiClient; + +public class ManyItemsJsonApiDocument : JsonApiDocument> +{ + public List GetResourceObjects() + { + return Data.Select(x => + JsonSerializer.Deserialize(JsonSerializer.Serialize(x.Attributes, jsonSerializerOptions), + jsonSerializerOptions)).ToList(); + } +} \ No newline at end of file diff --git a/CdmsBackent.IntegrationTests/JsonApiClient/SingleItemJsonApiDocument.cs b/CdmsBackent.IntegrationTests/JsonApiClient/SingleItemJsonApiDocument.cs new file mode 100644 index 0000000..ce83bcc --- /dev/null +++ b/CdmsBackent.IntegrationTests/JsonApiClient/SingleItemJsonApiDocument.cs @@ -0,0 +1,13 @@ +using System.Text.Json; +using JsonApiDotNetCore.Serialization.Objects; + +namespace CdmsBackend.IntegrationTests.JsonApiClient; + +public class SingleItemJsonApiDocument : JsonApiDocument +{ + public T GetResourceObject() + { + return JsonSerializer.Deserialize(JsonSerializer.Serialize(this.Data.Attributes, jsonSerializerOptions), + jsonSerializerOptions); + } +} \ No newline at end of file diff --git a/CdmsBackent.IntegrationTests/SmokeTests.cs b/CdmsBackent.IntegrationTests/SmokeTests.cs index 467265e..3f2b5ba 100644 --- a/CdmsBackent.IntegrationTests/SmokeTests.cs +++ b/CdmsBackent.IntegrationTests/SmokeTests.cs @@ -7,6 +7,7 @@ using Cdms.Model; using Cdms.Model.Gvms; using Cdms.Model.Ipaffs; +using CdmsBackend.IntegrationTests.Helpers; using CdmsBackend.IntegrationTests.JsonApiClient; using FluentAssertions; using Microsoft.AspNetCore.Hosting; @@ -52,9 +53,8 @@ public async Task SyncNotifications() factory.GetDbContext().Notifications.Count().Should().Be(5); // Check Api - var jsonClientResponse = client.AsJsonApiClient().Get("api/import-notifications"); - jsonClientResponse.IsSuccess.Should().BeTrue(); - jsonClientResponse.DocumentRoot.Data.Length.Should().Be(5); + var jsonClientResponse = client.AsJsonApiClient().Get("api/import-notifications"); + jsonClientResponse.Data.Count.Should().Be(5); } [Fact] @@ -81,11 +81,11 @@ public async Task SyncDecisions() // Check Api var jsonClientResponse = - client.AsJsonApiClient().GetById("CHEDPGB20241039875A5", "api/movements"); - jsonClientResponse.IsSuccess.Should().BeTrue(); - jsonClientResponse.DocumentRoot.Data.Items[0].Checks.Length.Should().Be(1); - jsonClientResponse.DocumentRoot.Data.Items[0].Checks[0].CheckCode.Should().Be("H234"); - jsonClientResponse.DocumentRoot.Data.Items[0].Checks[0].DepartmentCode.Should().Be("PHA"); + client.AsJsonApiClient().GetById("CHEDPGB20241039875A5", "api/movements"); + var movement = jsonClientResponse.GetResourceObject(); + movement.Items[0].Checks.Length.Should().Be(1); + movement.Items[0].Checks[0].CheckCode.Should().Be("H234"); + movement.Items[0].Checks[0].DepartmentCode.Should().Be("PHA"); } [Fact] @@ -106,9 +106,8 @@ public async Task SyncClearanceRequests() factory.GetDbContext().Movements.Count().Should().Be(5); // Check Api - var jsonClientResponse = client.AsJsonApiClient().Get("api/movements"); - jsonClientResponse.IsSuccess.Should().BeTrue(); - jsonClientResponse.DocumentRoot.Data.Length.Should().Be(5); + var jsonClientResponse = client.AsJsonApiClient().Get("api/movements"); + jsonClientResponse.Data.Count.Should().Be(5); } [Fact] @@ -129,9 +128,8 @@ public async Task SyncGmrs() factory.GetDbContext().Gmrs.Count().Should().Be(3); // Check Api - var jsonClientResponse = client.AsJsonApiClient().Get("api/gmrs"); - jsonClientResponse.IsSuccess.Should().BeTrue(); - jsonClientResponse.DocumentRoot.Data.Length.Should().Be(3); + var jsonClientResponse = client.AsJsonApiClient().Get("api/gmrs"); + jsonClientResponse.Data.Count.Should().Be(3); } From d4e8dd30c15cafcfd70e3122f44ee7961f02f323 Mon Sep 17 00:00:00 2001 From: Thomas Anderson Date: Wed, 6 Nov 2024 11:32:04 +0000 Subject: [PATCH 3/7] updated audit entry on Gmr --- Cdms.Consumers/GmrConsumer.cs | 7 ++++--- .../Features/GenerateModels/ClassMaps/Bootstrap.cs | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cdms.Consumers/GmrConsumer.cs b/Cdms.Consumers/GmrConsumer.cs index 741c67a..935057c 100644 --- a/Cdms.Consumers/GmrConsumer.cs +++ b/Cdms.Consumers/GmrConsumer.cs @@ -16,11 +16,12 @@ public async Task OnHandle(SearchGmrsForDeclarationIdsResponse message) { var internalGmr = GrmWithTransformMapper.MapWithTransform(gmr); var existingGmr = await dbContext.Gmrs.Find(internalGmr.Id); - + var auditId = Context.Headers["messageId"].ToString(); if (existingGmr is null) { + var auditEntry = - AuditEntry.CreateCreatedEntry(internalGmr, internalGmr.Id, 1, gmr.LastUpdated); + AuditEntry.CreateCreatedEntry(internalGmr, auditId, 1, gmr.LastUpdated); internalGmr.AuditEntries.Add(auditEntry); await dbContext.Gmrs.Insert(internalGmr); } @@ -32,7 +33,7 @@ public async Task OnHandle(SearchGmrsForDeclarationIdsResponse message) var auditEntry = AuditEntry.CreateUpdated( previous: existingGmr, current: internalGmr, - id: internalGmr.Id, + id: auditId, version: internalGmr.AuditEntries.Count + 1, lastUpdated: gmr.LastUpdated); internalGmr.AuditEntries.Add(auditEntry); diff --git a/CdmsBackend.Cli/Features/GenerateModels/ClassMaps/Bootstrap.cs b/CdmsBackend.Cli/Features/GenerateModels/ClassMaps/Bootstrap.cs index 0c37c5f..dbdcea8 100644 --- a/CdmsBackend.Cli/Features/GenerateModels/ClassMaps/Bootstrap.cs +++ b/CdmsBackend.Cli/Features/GenerateModels/ClassMaps/Bootstrap.cs @@ -247,6 +247,7 @@ public static void RegisterVehicleMovementsClassMaps() map.MapProperty("vehicleRegNum").SetName("vehicleRegistrationNumber"); map.MapProperty("updatedDateTime").SetName("lastUpdated").IsDateTime(); map.MapProperty("localDateTimeOfDeparture").SetName("departsAt").IsDateTime(); + map.MapProperty("declarations").ExcludeFromInternal(); }); GeneratorClassMap.RegisterClassMap("SearchGmrsForDeclarationIdsResponse", From 3afa0e27b7b364dbda2585194bb258820871985d Mon Sep 17 00:00:00 2001 From: Thomas Anderson Date: Wed, 6 Nov 2024 13:16:17 +0000 Subject: [PATCH 4/7] attempt to fix sonar issue --- .../JsonApi/CdmsResponseModelAdapter.cs | 88 +++++++------------ 1 file changed, 34 insertions(+), 54 deletions(-) diff --git a/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs index cf8dec5..7ac6f2d 100644 --- a/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs +++ b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs @@ -35,76 +35,56 @@ public Document Convert(object? model) var listOfResourceObjects = document.Data.ManyValue is not null ? document.Data.ManyValue.ToList() - : new List() { document.Data.SingleValue }; + : [document.Data.SingleValue]; foreach (var resourceObject in listOfResourceObjects) { if (resourceObject.Attributes.TryGetValue("relationships", out var value)) { - var relationships = (value as ITdmRelationships).GetRelationshipObjects(); - resourceObject.Relationships = new Dictionary(); - - foreach (var relationship in relationships) - { - var list = relationship.Item2.Data.Select(item => - { - var meta = new Dictionary(); - if (item.Matched.HasValue) - { - meta.Add("matched", item.Matched); - } + ProcessRelationships(value, resourceObject); + } + } - if (item.SourceItem.HasValue) - { - meta.Add("sourceItem", item.SourceItem); - } - if (item.DestinationItem.HasValue) - { - meta.Add("destinationItem", item.DestinationItem); - } + return document; + } - if (item.MatchingLevel.HasValue) - { - meta.Add("matchingLevel", item.MatchingLevel); - } + private static void ProcessRelationships(object? value, ResourceObject resourceObject) + { + var relationships = (value as ITdmRelationships).GetRelationshipObjects(); + resourceObject.Relationships = new Dictionary(); - if (!string.IsNullOrEmpty(item.Links?.Self)) - { - meta.Add("self", item.Links?.Self); - } + foreach (var relationship in relationships) + { + var list = relationship.Item2.Data.Select(item => + new ResourceIdentifierObject() + { + Type = item.Type, Id = item.Id, Meta = item.ToDictionary(), + }) + .ToList(); - return new ResourceIdentifierObject() { Type = item.Type, Id = item.Id, Meta = meta, }; - }) - .ToList(); + var meta = new Dictionary(); - var meta = new Dictionary(); + if (relationship.Item2.Matched.HasValue) + { + meta.Add("matched", relationship.Item2.Matched); + } - if (relationship.Item2.Matched.HasValue) + resourceObject.Relationships.Add(relationship.Item1, + new RelationshipObject() + { + Meta = meta, + Links = new RelationshipLinks() { - meta.Add("matched", relationship.Item2.Matched); - } - - resourceObject.Relationships.Add(relationship.Item1, - new RelationshipObject() - { - Meta = meta, - Links = new RelationshipLinks() - { - Self = relationship.Item2.Links?.Self, - Related = relationship.Item2?.Links?.Related - }, - Data = new SingleOrManyData(list) - }); - } - - resourceObject.Attributes.Remove("relationships"); - } + Self = relationship.Item2.Links?.Self, + Related = relationship.Item2?.Links?.Related + }, + Data = new SingleOrManyData(list) + }); } - - return document; + resourceObject.Attributes.Remove("relationships"); } } \ No newline at end of file From 332e0a8cb0b182e4c3f6a087562d9e5d1ce8f259 Mon Sep 17 00:00:00 2001 From: Thomas Anderson Date: Wed, 6 Nov 2024 13:21:45 +0000 Subject: [PATCH 5/7] suppressed sonar error --- CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs index 7ac6f2d..83b6af4 100644 --- a/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs +++ b/CdmsBackend/JsonApi/CdmsResponseModelAdapter.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Cdms.Model.Relationships; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; @@ -10,6 +11,9 @@ namespace CdmsBackend.JsonApi; +[SuppressMessage("SonarLint", "S107", + Justification = + "This is to override a class which is part of JsonApi so this requires the 8 parameters")] public class CdmsResponseModelAdapter( IJsonApiRequest request, IJsonApiOptions options, From 2679f76291b7acf706fde8956abe2aebc9c3af45 Mon Sep 17 00:00:00 2001 From: Thomas Anderson Date: Wed, 6 Nov 2024 13:44:44 +0000 Subject: [PATCH 6/7] fixed a few sonar issues --- Cdms.Model/Extensions/LinksBuilder.cs | 19 +++++++----- Cdms.Model/Movement.cs | 2 +- .../Relationships/RelationshipDataItem.cs | 31 +++++++++++++++++++ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/Cdms.Model/Extensions/LinksBuilder.cs b/Cdms.Model/Extensions/LinksBuilder.cs index d9ac2bb..80d33e0 100644 --- a/Cdms.Model/Extensions/LinksBuilder.cs +++ b/Cdms.Model/Extensions/LinksBuilder.cs @@ -4,50 +4,53 @@ public static class LinksBuilder { public static class Notification { + public const string ResourceName = "import-notifications"; public static string BuildSelfNotificationLink(string id) { - return LinksBuilder.BuildSelfLink("import-notifications", id); + return LinksBuilder.BuildSelfLink(ResourceName, id); } public static string BuildRelatedMovementLink(string id) { - return LinksBuilder.BuildRelatedLink("import-notifications", id, "movements"); + return LinksBuilder.BuildRelatedLink(ResourceName, id, "movements"); } } public static class Movement { + public const string ResourceName = "movements"; public static string BuildSelfMovementLink(string id) { - return LinksBuilder.BuildSelfLink("movements", id); + return LinksBuilder.BuildSelfLink(ResourceName, id); } public static string BuildRelatedMovementLink(string id) { - return LinksBuilder.BuildRelatedLink("movements", id, "import-notifications"); + return LinksBuilder.BuildRelatedLink(ResourceName, id, Notification.ResourceName); } } public static class Gmr { + public const string ResourceName = "gmr"; public static string BuildSelfRelationshipCustomsLink(string id) { - return LinksBuilder.BuildSelfRelationshipLink("gmr", id, "import-notifications"); + return LinksBuilder.BuildSelfRelationshipLink(ResourceName, id, Notification.ResourceName); } public static string BuildSelfRelationshipTransitsLink(string id) { - return LinksBuilder.BuildSelfRelationshipLink("gmr", id, "movement"); + return LinksBuilder.BuildSelfRelationshipLink(ResourceName, id, Movement.ResourceName); } public static string BuildRelatedTransitLink(string id) { - return LinksBuilder.BuildSelfLink("movements", id); + return LinksBuilder.BuildSelfLink(Movement.ResourceName, id); } public static string BuildRelatedCustomsLink(string id) { - return LinksBuilder.BuildSelfLink("import-notifications", id); + return LinksBuilder.BuildSelfLink(Notification.ResourceName, id); } } diff --git a/Cdms.Model/Movement.cs b/Cdms.Model/Movement.cs index 9a21be9..ee4060f 100644 --- a/Cdms.Model/Movement.cs +++ b/Cdms.Model/Movement.cs @@ -109,7 +109,7 @@ public void AddRelationship(string type, TdmRelationshipObject relationship) Relationships.Notifications.Matched = Items .Select(x => x.ItemNumber) .All(itemNumber => - Relationships.Notifications.Data.Any(x => x.Matched.GetValueOrDefault() && x.SourceItem == itemNumber)); + Relationships.Notifications.Data.Exists(x => x.Matched.GetValueOrDefault() && x.SourceItem == itemNumber)); } public void Update(AuditEntry auditEntry) diff --git a/Cdms.Model/Relationships/RelationshipDataItem.cs b/Cdms.Model/Relationships/RelationshipDataItem.cs index b5f9377..3b0367a 100644 --- a/Cdms.Model/Relationships/RelationshipDataItem.cs +++ b/Cdms.Model/Relationships/RelationshipDataItem.cs @@ -23,6 +23,37 @@ public sealed class RelationshipDataItem public int? MatchingLevel { get; set; } + public Dictionary ToDictionary() + { + var meta = new Dictionary(); + if (Matched.HasValue) + { + meta.Add("matched", Matched); + } + + if (SourceItem.HasValue) + { + meta.Add("sourceItem", SourceItem); + } + + if (DestinationItem.HasValue) + { + meta.Add("destinationItem", DestinationItem); + } + + if (MatchingLevel.HasValue) + { + meta.Add("matchingLevel", MatchingLevel); + } + + if (!string.IsNullOrEmpty(Links?.Self)) + { + meta.Add("self", Links?.Self); + } + + return meta; + } + public static RelationshipDataItem CreateFromNotification(ImportNotification notification, Movement movement, string matchReference, bool matched = true, string reason = null) { From 366c16b1732b59d96987601e77a64a37bfe8db1f Mon Sep 17 00:00:00 2001 From: Thomas Anderson Date: Wed, 6 Nov 2024 13:47:55 +0000 Subject: [PATCH 7/7] removed null check --- Cdms.Model/Relationships/RelationshipDataItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cdms.Model/Relationships/RelationshipDataItem.cs b/Cdms.Model/Relationships/RelationshipDataItem.cs index 3b0367a..e7bc5ed 100644 --- a/Cdms.Model/Relationships/RelationshipDataItem.cs +++ b/Cdms.Model/Relationships/RelationshipDataItem.cs @@ -48,7 +48,7 @@ public sealed class RelationshipDataItem if (!string.IsNullOrEmpty(Links?.Self)) { - meta.Add("self", Links?.Self); + meta.Add("self", Links.Self); } return meta;