diff --git a/docs/api/administration-service.yaml b/docs/api/administration-service.yaml index a4302d81ad..ef3374b702 100644 --- a/docs/api/administration-service.yaml +++ b/docs/api/administration-service.yaml @@ -6030,18 +6030,18 @@ components: ClearinghouseResponseData: type: object properties: - bpn: - type: string - status: - $ref: '#/components/schemas/ClearinghouseResponseStatus' - message: + validationMode: type: string - nullable: true + validationUnits: + type: array + items: + $ref: '#/components/schemas/ValidationUnits' additionalProperties: false ClearinghouseResponseStatus: enum: - - CONFIRM - - DECLINE + - VALID + - INVALID + - INCONCLUSIVE type: string CompanyAddressDetailData: type: object @@ -7621,6 +7621,14 @@ components: type: string nullable: true additionalProperties: false + Reason: + type: object + properties: + message: + type: string + detailMessage: + type: string + additionalProperties: false RegistrationDeclineData: type: object properties: @@ -7990,6 +7998,16 @@ components: items: $ref: '#/components/schemas/ErrorDetails' additionalProperties: false + ValidationUnits: + type: object + properties: + result: + $ref: '#/components/schemas/ClearinghouseResponseStatus' + type: + type: string + reason: + $ref: '#/components/schemas/Reason' + additionalProperties: false securitySchemes: Bearer: type: apiKey diff --git a/src/administration/Administration.Service/BusinessLogic/IRegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/IRegistrationBusinessLogic.cs index 8f9e27414b..c25134ffc3 100644 --- a/src/administration/Administration.Service/BusinessLogic/IRegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/IRegistrationBusinessLogic.cs @@ -53,8 +53,9 @@ public interface IRegistrationBusinessLogic /// Processes the clearinghouse response /// /// the response data + /// BusinessPartnerNumber of the responded data /// cancellation token - Task ProcessClearinghouseResponseAsync(ClearinghouseResponseData data, CancellationToken cancellationToken); + Task ProcessClearinghouseResponseAsync(ClearinghouseResponseData data, string bpn, CancellationToken cancellationToken); /// /// Processes the dim response diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index 1a3172af85..32d3cb283e 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -299,18 +299,18 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn) private ProcessStepTypeId CreateWalletStep() => _settings.UseDimWallet ? ProcessStepTypeId.CREATE_DIM_WALLET : ProcessStepTypeId.CREATE_IDENTITY_WALLET; /// - public async Task ProcessClearinghouseResponseAsync(ClearinghouseResponseData data, CancellationToken cancellationToken) + public async Task ProcessClearinghouseResponseAsync(ClearinghouseResponseData data, string bpn, CancellationToken cancellationToken) { - logger.LogInformation("Process SelfDescription called with the following data {Data}", data.ToString().Replace(Environment.NewLine, string.Empty)); - var result = await portalRepositories.GetInstance().GetSubmittedApplicationIdsByBpn(data.BusinessPartnerNumber.ToUpper()).ToListAsync(cancellationToken).ConfigureAwait(false); + logger.LogInformation("Process SelfDescription called with the following data {Data} and bpn {Bpn}", data.ToString().Replace(Environment.NewLine, string.Empty), bpn.ToString().Replace(Environment.NewLine, string.Empty)); + var result = await portalRepositories.GetInstance().GetSubmittedApplicationIdsByBpn(bpn.ToUpper()).ToListAsync(cancellationToken).ConfigureAwait(false); if (!result.Any()) { - throw NotFoundException.Create(AdministrationRegistrationErrors.REGISTRATION_NOT_COMP_APP_BPN_STATUS_SUBMIT, new ErrorParameter[] { new("businessPartnerNumber", data.BusinessPartnerNumber) }); + throw NotFoundException.Create(AdministrationRegistrationErrors.REGISTRATION_NOT_COMP_APP_BPN_STATUS_SUBMIT, new ErrorParameter[] { new("businessPartnerNumber", bpn) }); } if (result.Count > 1) { - throw ConflictException.Create(AdministrationRegistrationErrors.REGISTRATION_CONFLICT_APP_STATUS_STATUS_SUBMIT_FOUND_BPN, new ErrorParameter[] { new("businessPartnerNumber", data.BusinessPartnerNumber) }); + throw ConflictException.Create(AdministrationRegistrationErrors.REGISTRATION_CONFLICT_APP_STATUS_STATUS_SUBMIT_FOUND_BPN, new ErrorParameter[] { new("businessPartnerNumber", bpn) }); } await clearinghouseBusinessLogic.ProcessEndClearinghouse(result.Single(), data, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/administration/Administration.Service/Controllers/RegistrationController.cs b/src/administration/Administration.Service/Controllers/RegistrationController.cs index b317ec3044..4307e6a88c 100644 --- a/src/administration/Administration.Service/Controllers/RegistrationController.cs +++ b/src/administration/Administration.Service/Controllers/RegistrationController.cs @@ -27,6 +27,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.Framework.Web; using Org.Eclipse.TractusX.Portal.Backend.IssuerComponent.Library.Models; +using Org.Eclipse.TractusX.Portal.Backend.Keycloak.Authentication; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.SdFactory.Library.Models; @@ -183,7 +184,7 @@ public async Task DeclineApplication([FromRoute] Guid applicati [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] public async Task ProcessClearinghouseResponse([FromBody] ClearinghouseResponseData responseData, CancellationToken cancellationToken) { - await logic.ProcessClearinghouseResponseAsync(responseData, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await this.WithBpn(bpn => logic.ProcessClearinghouseResponseAsync(responseData, bpn, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None)); return NoContent(); } diff --git a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs index 1300dbb781..1bd2513152 100644 --- a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs +++ b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs @@ -19,22 +19,23 @@ using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; -using Org.Eclipse.TractusX.Portal.Backend.Custodian.Library.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Framework.Async; using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Linq; using Org.Eclipse.TractusX.Portal.Backend.Framework.Processes.Library.Enums; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Extensions; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.Processes.ApplicationChecklist.Library; +using System.Collections.Immutable; namespace Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.BusinessLogic; public class ClearinghouseBusinessLogic( IPortalRepositories portalRepositories, IClearinghouseService clearinghouseService, - ICustodianBusinessLogic custodianBusinessLogic, IApplicationChecklistService checklistService, IDateTimeProvider dateTimeProvider, IOptions options) @@ -44,37 +45,14 @@ public class ClearinghouseBusinessLogic( public async Task HandleClearinghouse(IApplicationChecklistService.WorkerChecklistProcessStepData context, CancellationToken cancellationToken) { - var overwrite = context.ProcessStepTypeId switch + var validationMode = context.ProcessStepTypeId switch { - ProcessStepTypeId.START_OVERRIDE_CLEARING_HOUSE => true, - ProcessStepTypeId.START_CLEARING_HOUSE => false, + ProcessStepTypeId.START_OVERRIDE_CLEARING_HOUSE => ValidationModes.IDENTIFIER, + ProcessStepTypeId.START_CLEARING_HOUSE => ValidationModes.LEGAL_NAME, _ => throw new UnexpectedConditionException($"HandleClearingHouse called for unexpected processStepTypeId {context.ProcessStepTypeId}. Expected {ProcessStepTypeId.START_CLEARING_HOUSE} or {ProcessStepTypeId.START_OVERRIDE_CLEARING_HOUSE}") }; - string companyDid; - if (_settings.UseDimWallet) - { - var (exists, did) = await portalRepositories.GetInstance() - .GetDidForApplicationId(context.ApplicationId).ConfigureAwait(ConfigureAwaitOptions.None); - if (!exists || string.IsNullOrWhiteSpace(did)) - { - throw new ConflictException($"Did must be set for Application {context.ApplicationId}"); - } - - companyDid = did; - } - else - { - var walletData = await custodianBusinessLogic.GetWalletByBpnAsync(context.ApplicationId, cancellationToken); - if (walletData == null || string.IsNullOrEmpty(walletData.Did)) - { - throw new ConflictException($"Decentralized Identifier for application {context.ApplicationId} is not set"); - } - - companyDid = walletData.Did; - } - - await TriggerCompanyDataPost(context.ApplicationId, companyDid, overwrite, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await TriggerCompanyDataPost(context.ApplicationId, validationMode, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); return new IApplicationChecklistService.WorkerChecklistProcessStepExecutionResult( ProcessStepStatusId.DONE, @@ -85,7 +63,7 @@ public class ClearinghouseBusinessLogic( null); } - private async Task TriggerCompanyDataPost(Guid applicationId, string decentralizedIdentifier, bool overwrite, CancellationToken cancellationToken) + private async Task TriggerCompanyDataPost(Guid applicationId, string validationMode, CancellationToken cancellationToken) { var data = await portalRepositories.GetInstance() .GetClearinghouseDataForApplicationId(applicationId).ConfigureAwait(ConfigureAwaitOptions.None); @@ -99,16 +77,33 @@ private async Task TriggerCompanyDataPost(Guid applicationId, string decentraliz throw new ConflictException($"CompanyApplication {applicationId} is not in status SUBMITTED"); } - if (string.IsNullOrWhiteSpace(data.ParticipantDetails.Bpn)) + if (string.IsNullOrWhiteSpace(data.Bpn)) { throw new ConflictException("BusinessPartnerNumber is null"); } + if (data.Address is null) + { + throw new ConflictException($"Address does not exist."); + } + + var headers = ImmutableDictionary.CreateRange([new("Business-Partner-Number", data.Bpn)]); + var transferData = new ClearinghouseTransferData( - data.ParticipantDetails, - new IdentityDetails(decentralizedIdentifier, data.UniqueIds), - _settings.CallbackUrl, - overwrite); + new LegalEntity( + data.Name, + new LegalAddress( + data.Address.CountryAlpha2Code, + string.Format("{0}-{1}", data.Address.CountryAlpha2Code, data.Address.Region), + data.Address.City, + data.Address.Zipcode, + string.IsNullOrEmpty(data.Address.Streetnumber) + ? data.Address.Streetname + : string.Format("{0} {1}", data.Address.Streetname, data.Address.Streetnumber)), + data.Identifiers.Select(ci => new UniqueIdData(ci.UniqueIdentifierId.GetUniqueIdentifierValue(), ci.Value))), + validationMode, + new CallBack(_settings.CallbackUrl, headers) + ); await clearinghouseService.TriggerCompanyDataPost(transferData, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); } @@ -124,19 +119,24 @@ public async Task ProcessEndClearinghouse(Guid applicationId, ClearinghouseRespo processStepTypeIds: [ProcessStepTypeId.START_SELF_DESCRIPTION_LP]) .ConfigureAwait(ConfigureAwaitOptions.None); - var declined = data.Status == ClearinghouseResponseStatus.DECLINE; - + // Company data is valid if any one of the provided identifiers was responded valid from CH + var validData = data.ValidationUnits.FirstOrDefault(s => s.Status == ClearinghouseResponseStatus.VALID); + var isInvalid = validData == null; checklistService.FinalizeChecklistEntryAndProcessSteps( context, null, item => { - item.ApplicationChecklistEntryStatusId = declined + item.ApplicationChecklistEntryStatusId = isInvalid ? ApplicationChecklistEntryStatusId.FAILED : ApplicationChecklistEntryStatusId.DONE; - item.Comment = data.Message; + + // There is not "Message" param available in the response in case of VALID so, thats why saving ClearinghouseResponseStatus param into the Comments in case of VALID only. + item.Comment = isInvalid + ? string.Join(", ", data.ValidationUnits.Where(s => s.Status != ClearinghouseResponseStatus.VALID).Select(x => x.Reason?.DetailMessage)) + : validData!.Status.ToString(); }, - declined + isInvalid ? [ProcessStepTypeId.MANUAL_TRIGGER_OVERRIDE_CLEARING_HOUSE] : [ProcessStepTypeId.START_SELF_DESCRIPTION_LP]); } diff --git a/src/externalsystems/Clearinghouse.Library/ClearinghouseService.cs b/src/externalsystems/Clearinghouse.Library/ClearinghouseService.cs index 9aba67c506..bb880d3e76 100644 --- a/src/externalsystems/Clearinghouse.Library/ClearinghouseService.cs +++ b/src/externalsystems/Clearinghouse.Library/ClearinghouseService.cs @@ -44,7 +44,7 @@ public async Task TriggerCompanyDataPost(ClearinghouseTransferData data, Cancell async ValueTask<(bool, string?)> CreateErrorMessage(HttpResponseMessage errorResponse) => (false, (await errorResponse.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None))); - await httpClient.PostAsJsonAsync("/api/v1/validation", data, cancellationToken) + await httpClient.PostAsJsonAsync("/api/v2/validation", data, cancellationToken) .CatchingIntoServiceExceptionFor("clearinghouse-post", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, CreateErrorMessage).ConfigureAwait(false); } } diff --git a/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseData.cs b/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseData.cs index 5bbc66e570..21e46ec639 100644 --- a/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseData.cs +++ b/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseData.cs @@ -23,6 +23,17 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; public record ClearinghouseResponseData( - [property: JsonPropertyName("bpn")] string BusinessPartnerNumber, - [property: JsonPropertyName("status")] ClearinghouseResponseStatus Status, - [property: JsonPropertyName("message")] string? Message); + [property: JsonPropertyName("validationMode")] string ValidationMode, + [property: JsonPropertyName("validationUnits")] IEnumerable ValidationUnits +); + +public record ValidationUnits( + [property: JsonPropertyName("result")] ClearinghouseResponseStatus Status, + [property: JsonPropertyName("type")] string Type, + [property: JsonPropertyName("reason")] Reason? Reason +); + +public record Reason( + [property: JsonPropertyName("message")] string Message, + [property: JsonPropertyName("detailMessage")] string DetailMessage +); diff --git a/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseStatus.cs b/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseStatus.cs index f35e19f1cf..2c0ab8ccf3 100644 --- a/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseStatus.cs +++ b/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseResponseStatus.cs @@ -22,7 +22,18 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; public enum ClearinghouseResponseStatus { - CONFIRM = 1, + /// + /// In case the identifier has been found in the trust sources of clearing house. + /// + VALID = 1, - DECLINE = 2 + /// + /// In case the identifier format is not valid or the identifier was not found in the trust source of clearing house. + /// + INVALID = 2, + + /// + /// In case the validation can't be performed, due to the unavailablity of the trust source of clearing house. + /// + INCONCLUSIVE = 3 } diff --git a/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseTransferData.cs b/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseTransferData.cs index 402389d281..35796ae111 100644 --- a/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseTransferData.cs +++ b/src/externalsystems/Clearinghouse.Library/Models/ClearinghouseTransferData.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2022 Microsoft and BMW Group AG * Copyright (c) 2022 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -18,17 +17,36 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using System.Text.Json.Serialization; namespace Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; public record ClearinghouseTransferData( - [property: JsonPropertyName("participantDetails")] ParticipantDetails ParticipantDetails, - [property: JsonPropertyName("identityDetails")] IdentityDetails IdentityDetails, - [property: JsonPropertyName("callbackUrl")] string CallbackUrl, - [property: JsonPropertyName("exceptProfile")] bool ExceptProfile); + [property: JsonPropertyName("legalEntity")] LegalEntity LegalEntity, + [property: JsonPropertyName("validationMode")] string ValidationMode, + [property: JsonPropertyName("callback")] CallBack Callback +); -public record IdentityDetails( - [property: JsonPropertyName("did")] string Did, - [property: JsonPropertyName("uniqueIds")] IEnumerable UniqueIds); +public record LegalEntity( + [property: JsonPropertyName("legalName")] string LegalName, + [property: JsonPropertyName("address")] LegalAddress Address, + [property: JsonPropertyName("identifiers")] IEnumerable Identifiers +); + +public record LegalAddress( + [property: JsonPropertyName("country")] string CountryAlpha2Code, + [property: JsonPropertyName("region")] string Region, + [property: JsonPropertyName("locality")] string City, + [property: JsonPropertyName("postalCode")] string? ZipCode, + [property: JsonPropertyName("addressLine")] string AddressLine +); + +public record UniqueIdData( + [property: JsonPropertyName("type")] string Type, + [property: JsonPropertyName("value")] string Value +); + +public record CallBack( + [property: JsonPropertyName("url")] string Url, + [property: JsonPropertyName("headers")] IReadOnlyDictionary Headers +); diff --git a/src/externalsystems/Clearinghouse.Library/ValidationModes.cs b/src/externalsystems/Clearinghouse.Library/ValidationModes.cs new file mode 100644 index 0000000000..d473c1479f --- /dev/null +++ b/src/externalsystems/Clearinghouse.Library/ValidationModes.cs @@ -0,0 +1,36 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +namespace Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library; + +public static class ValidationModes +{ + /// + /// DEFAULT - validates whether the identifiers themselves exists, indepenedent of their relationship to the legal entity provided + /// + public const string IDENTIFIER = "IDENTIFIER"; + /// + /// Validates whether the identifier is valid, and whether the name of the legal entity it is associated with matches the provided legal name + /// + public const string LEGAL_NAME = "LEGAL_NAME"; + /// + /// Validates whether the identifier is valid, and whether the name of the legal entity, as well as the addresss it is associated with matches the provided ones. + /// + public const string LEGAL_NAME_AND_ADDRESS = "LEGAL_NAME_AND_ADDRESS"; +} diff --git a/src/keycloak/Keycloak.Authentication/ControllerExtensions.cs b/src/keycloak/Keycloak.Authentication/ControllerExtensions.cs index c1e25a613a..5fdc8558e1 100644 --- a/src/keycloak/Keycloak.Authentication/ControllerExtensions.cs +++ b/src/keycloak/Keycloak.Authentication/ControllerExtensions.cs @@ -27,9 +27,14 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Keycloak.Authentication; /// public static class ControllerExtensions { + private static readonly string BusinessPartnerNumberHeader = "Business-Partner-Number"; + public static T WithBearerToken(this ControllerBase controller, Func tokenConsumingFunction) => tokenConsumingFunction(controller.GetBearerToken()); + public static T WithBpn(this ControllerBase controller, Func bpnConsumingFunction) => + bpnConsumingFunction(controller.GetBpn()); + private static string GetBearerToken(this ControllerBase controller) { var authorization = controller.Request.Headers.Authorization.FirstOrDefault(); @@ -47,4 +52,20 @@ private static string GetBearerToken(this ControllerBase controller) return bearer; } + + private static string GetBpn(this ControllerBase controller) + { + if (!controller.Request.Headers.TryGetValue(BusinessPartnerNumberHeader, out var values)) + { + throw new ControllerArgumentException("Request does not contain Business-Partner-Number header", BusinessPartnerNumberHeader); + } + + var bpn = values.FirstOrDefault(); + if (string.IsNullOrWhiteSpace(bpn)) + { + throw new ControllerArgumentException("Business-Partner-Number in header must not be empty", BusinessPartnerNumberHeader); + } + + return bpn; + } } diff --git a/src/portalbackend/PortalBackend.DBAccess/Extensions/UniqueIdentifiersExtension.cs b/src/portalbackend/PortalBackend.DBAccess/Extensions/UniqueIdentifiersExtension.cs new file mode 100644 index 0000000000..94c42a4b7c --- /dev/null +++ b/src/portalbackend/PortalBackend.DBAccess/Extensions/UniqueIdentifiersExtension.cs @@ -0,0 +1,36 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; + +namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Extensions; + +public static class UniqueIdentifiersExtension +{ + public static string GetUniqueIdentifierValue(this UniqueIdentifierId uniqueIdentifierId) => + uniqueIdentifierId switch + { + UniqueIdentifierId.COMMERCIAL_REG_NUMBER => "schema:taxID", + UniqueIdentifierId.VAT_ID => "schema:vatID", + UniqueIdentifierId.LEI_CODE => "schema:leiCode", + UniqueIdentifierId.VIES => "EUID", + UniqueIdentifierId.EORI => "gx:eori", + _ => throw new ArgumentOutOfRangeException(nameof(uniqueIdentifierId), uniqueIdentifierId, "Unique Identifier not found for Clearing House Conversion") + }; +} diff --git a/src/portalbackend/PortalBackend.DBAccess/Models/ClearinghouseData.cs b/src/portalbackend/PortalBackend.DBAccess/Models/ClearinghouseData.cs index 5d80bce312..abefac927c 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Models/ClearinghouseData.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Models/ClearinghouseData.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2022 BMW Group AG * Copyright (c) 2022 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -19,24 +18,27 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; -using System.Text.Json.Serialization; namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; public record ClearinghouseData( CompanyApplicationStatusId ApplicationStatusId, - ParticipantDetails ParticipantDetails, - IEnumerable UniqueIds); + string? Bpn, + string Name, + ClearinghouseAddressData? Address, + IEnumerable Identifiers +); -public record ParticipantDetails( - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("city")] string? City, - [property: JsonPropertyName("street")] string Street, - [property: JsonPropertyName("bpn")] string? Bpn, - [property: JsonPropertyName("region")] string? Region, - [property: JsonPropertyName("zipCode")] string? ZipCode, - [property: JsonPropertyName("country")] string? Country, - [property: JsonPropertyName("countryAlpha2Code")] string CountryAlpha2Code +public record ClearinghouseAddressData( + string CountryAlpha2Code, + string? Region, + string City, + string? Zipcode, + string? Streetnumber, + string Streetname ); -public record UniqueIdData([property: JsonPropertyName("type")] string Type, [property: JsonPropertyName("value")] string Value); +public record CompanyUniqueIdentifier( + UniqueIdentifierId UniqueIdentifierId, + string Value +); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationRepository.cs index 13933b7861..03061d4066 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationRepository.cs @@ -22,6 +22,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.Identity; using Org.Eclipse.TractusX.Portal.Backend.Framework.Processes.Library.Enums; using Org.Eclipse.TractusX.Portal.Backend.Framework.Processes.Library.Models; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Extensions; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; @@ -404,16 +405,20 @@ public IQueryable GetAllCompanyApplicationsDetailsQuery(stri .Select(ca => new { ca.ApplicationStatusId, ca.Company, ca.Company!.Address, ca.Company.CompanyIdentifiers }) .Select(ca => new ClearinghouseData( ca.ApplicationStatusId, - new ParticipantDetails( - ca.Company!.Name, - ca.Address!.City, - ca.Address.Streetname, - ca.Company.BusinessPartnerNumber, - ca.Address.Region, - ca.Address.Zipcode, - ca.Address.Country!.CountryLongNames.Where(cln => cln.ShortName == "en").Select(cln => cln.LongName).SingleOrDefault(), - ca.Address.CountryAlpha2Code), - ca.CompanyIdentifiers.Select(ci => new UniqueIdData(ci.UniqueIdentifier!.Label, ci.Value)))) + ca.Company!.BusinessPartnerNumber, + ca.Company.Name, + ca.Address == null + ? null + : new ClearinghouseAddressData( + ca.Address.CountryAlpha2Code, + ca.Address.Region, + ca.Address.City, + ca.Address.Zipcode, + ca.Address.Streetnumber, + ca.Address.Streetname + ), + ca.CompanyIdentifiers.Select(ci => new CompanyUniqueIdentifier(ci.UniqueIdentifierId, ci.Value)) + )) .SingleOrDefaultAsync(); /// diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 8b8d63aeab..dfada3bdbc 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -383,8 +383,11 @@ public async Task ProcessClearinghouseResponseAsync_WithValidData_CallsExpected( .Returns(Enumerable.Repeat(ApplicationId, 1).ToAsyncEnumerable()); // Act - var data = new ClearinghouseResponseData(BusinessPartnerNumber, ClearinghouseResponseStatus.CONFIRM, null); - await _logic.ProcessClearinghouseResponseAsync(data, CancellationToken.None); + var validationUnits = new List { + new (ClearinghouseResponseStatus.VALID, "vatId", null) + }.AsEnumerable(); + var data = new ClearinghouseResponseData("COMPLETED", validationUnits); + await _logic.ProcessClearinghouseResponseAsync(data, BusinessPartnerNumber, CancellationToken.None); // Assert A.CallTo(() => _clearinghouseBusinessLogic.ProcessEndClearinghouse(ApplicationId, data, A._)) @@ -399,8 +402,11 @@ public async Task ProcessClearinghouseResponseAsync_WithMultipleApplications_Thr .Returns(new[] { CompanyId, Guid.NewGuid() }.ToAsyncEnumerable()); // Act - var data = new ClearinghouseResponseData(BusinessPartnerNumber, ClearinghouseResponseStatus.CONFIRM, null); - Task Act() => _logic.ProcessClearinghouseResponseAsync(data, CancellationToken.None); + var validationUnits = new List { + new (ClearinghouseResponseStatus.VALID, "vatId", null) + }.AsEnumerable(); + var data = new ClearinghouseResponseData("COMPLETED", validationUnits); + Task Act() => _logic.ProcessClearinghouseResponseAsync(data, BusinessPartnerNumber, CancellationToken.None); // Assert var ex = await Assert.ThrowsAsync(Act); @@ -415,8 +421,11 @@ public async Task ProcessClearinghouseResponseAsync_WithNoApplication_ThrowsNotF .Returns(Enumerable.Empty().ToAsyncEnumerable()); // Act - var data = new ClearinghouseResponseData(BusinessPartnerNumber, ClearinghouseResponseStatus.CONFIRM, null); - Task Act() => _logic.ProcessClearinghouseResponseAsync(data, CancellationToken.None); + var validationUnits = new List { + new (ClearinghouseResponseStatus.VALID, "vatId", null) + }.AsEnumerable(); + var data = new ClearinghouseResponseData("COMPLETED", validationUnits); + Task Act() => _logic.ProcessClearinghouseResponseAsync(data, BusinessPartnerNumber, CancellationToken.None); // Assert var ex = await Assert.ThrowsAsync(Act); diff --git a/tests/administration/Administration.Service.Tests/Controllers/RegistrationControllerTest.cs b/tests/administration/Administration.Service.Tests/Controllers/RegistrationControllerTest.cs index 456d91ff17..724054e828 100644 --- a/tests/administration/Administration.Service.Tests/Controllers/RegistrationControllerTest.cs +++ b/tests/administration/Administration.Service.Tests/Controllers/RegistrationControllerTest.cs @@ -17,12 +17,15 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Controllers; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.Dim.Library.Models; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.Identity; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; @@ -35,6 +38,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.Contr public class RegistrationControllerTest { private static readonly string AccessToken = "THISISTHEACCESSTOKEN"; + private static readonly string Bpn = "CAXLSHAREDIDPZZ"; private readonly IRegistrationBusinessLogic _logic; private readonly RegistrationController _controller; private readonly IFixture _fixture; @@ -121,15 +125,47 @@ public async Task ProcessClearinghouseResponse_ReturnsExpectedResult() //Arrange var data = _fixture.Create(); var cancellationToken = CancellationToken.None; + _controller.HttpContext.Request.Headers.Append("Business-Partner-Number", new(Bpn)); //Act var result = await _controller.ProcessClearinghouseResponse(data, cancellationToken); //Assert - A.CallTo(() => _logic.ProcessClearinghouseResponseAsync(data, cancellationToken)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _logic.ProcessClearinghouseResponseAsync(data, Bpn, cancellationToken)).MustHaveHappenedOnceExactly(); Assert.IsType(result); } + [Fact] + public async Task ProcessClearinghouseResponse_NoBusinessPartnerNumberHeader_Throws() + { + //Arrange + var data = _fixture.Create(); + var cancellationToken = CancellationToken.None; + + //Act + var error = await Assert.ThrowsAsync(() => _controller.ProcessClearinghouseResponse(data, cancellationToken)); + + //Assert + error.Message.Should().Be("Request does not contain Business-Partner-Number header (Parameter 'Business-Partner-Number')"); + A.CallTo(() => _logic.ProcessClearinghouseResponseAsync(A._, A._, A._)).MustNotHaveHappened(); + } + + [Fact] + public async Task ProcessClearinghouseResponse_EmptyBusinessPartnerNumberHeader_Throws() + { + //Arrange + var data = _fixture.Create(); + var cancellationToken = CancellationToken.None; + _controller.HttpContext.Request.Headers.Append("Business-Partner-Number", new(" ")); + + //Act + var error = await Assert.ThrowsAsync(() => _controller.ProcessClearinghouseResponse(data, cancellationToken)); + + //Assert + error.Message.Should().Be("Business-Partner-Number in header must not be empty (Parameter 'Business-Partner-Number')"); + A.CallTo(() => _logic.ProcessClearinghouseResponseAsync(A._, A._, A._)).MustNotHaveHappened(); + } + [Fact] public async Task GetChecklistForApplication_ReturnsExpectedResult() { diff --git a/tests/endtoend/InterfacePartnerHealthCheck/ClearinghouseEndToEndTests.cs b/tests/endtoend/InterfacePartnerHealthCheck/ClearinghouseEndToEndTests.cs index 1129f40778..3c8b89c0c4 100644 --- a/tests/endtoend/InterfacePartnerHealthCheck/ClearinghouseEndToEndTests.cs +++ b/tests/endtoend/InterfacePartnerHealthCheck/ClearinghouseEndToEndTests.cs @@ -18,6 +18,7 @@ ********************************************************************************/ using FluentAssertions; +using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library; using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using RestAssured.Response.Logging; @@ -54,16 +55,17 @@ public async Task ClearinghouseInterface_HealthCheck() var body = DataHandleHelper.SerializeData( new ClearinghouseTransferData( - new ParticipantDetails( - "SmokeTest CH", "Stuttgart", "Test Street", "BPNL000SMOKE0011", "Bavaria", "01108", - "Germany", "DE" + new LegalEntity( + "SmokeTest CH", + new LegalAddress("DE", "Bavaria", "Stuttgart", "01108", "Abc Street"), + [new("local", "HB8272819")] ), - new IdentityDetails( - "did:sov:RPgthNMDkVdzYQhXzahh3P", // hardcode due to initial requirements in CPLP-2803 - new List { new("local", "HB8272819") } - ), - $"{TestResources.BasePortalBackendUrl}/api/administration/registration/clearinghouse", - false) + ValidationModes.LEGAL_NAME, + new CallBack( + $"{TestResources.BasePortalBackendUrl}/api/administration/registration/clearinghouse", + new Dictionary { { "bpn", "BPNL000SMOKE0011" } } + ) + ) ); var data = Given() diff --git a/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs b/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs index c2e4a66787..af570dcbcf 100644 --- a/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs +++ b/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs @@ -43,8 +43,6 @@ public class ClearinghouseBusinessLogicTests private static readonly Guid IdWithBpn = new("c244f79a-7faf-4c59-bb85-fbfdf72ce46f"); private const string ValidBpn = "BPNL123698762345"; - private const string ValidDid = "thisisavaliddid"; - private readonly IFixture _fixture; private readonly IApplicationRepository _applicationRepository; @@ -53,7 +51,6 @@ public class ClearinghouseBusinessLogicTests private readonly ClearinghouseBusinessLogic _logic; private readonly IClearinghouseService _clearinghouseService; private readonly IApplicationChecklistService _checklistService; - private readonly ICustodianBusinessLogic _custodianBusinessLogic; private readonly IDateTimeProvider _dateTimeProvider; private readonly IApplicationChecklistRepository _applicationChecklistRepository; @@ -68,7 +65,6 @@ public ClearinghouseBusinessLogicTests() _applicationChecklistRepository = A.Fake(); _portalRepositories = A.Fake(); _clearinghouseService = A.Fake(); - _custodianBusinessLogic = A.Fake(); _checklistService = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_applicationRepository); @@ -77,7 +73,7 @@ public ClearinghouseBusinessLogicTests() _dateTimeProvider = A.Fake(); A.CallTo(() => _dateTimeProvider.OffsetNow).Returns(DateTimeOffset.UtcNow); - _logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings + _logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings { CallbackUrl = "https://api.com", UseDimWallet = false @@ -137,7 +133,7 @@ public async Task HandleStartClearingHouse_WithNotExistingApplication_ThrowsConf // Assert var ex = await Assert.ThrowsAsync(Act); - ex.Message.Should().Be($"Decentralized Identifier for application {context.ApplicationId} is not set"); + ex.Message.Should().Be($"Application {context.ApplicationId} does not exists."); } [Fact] @@ -217,106 +213,6 @@ public async Task HandleStartClearingHouse_WithValidData_CallsExpected(ProcessSt result.Modified.Should().BeTrue(); } - [Fact] - public async Task HandleStartClearingHouse_WithDimActiveAndNonExistingApplication_ThrowsConflictException() - { - // Arrange - var checklist = ImmutableDictionary.CreateRange([ - new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) - ]); - - var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); - A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) - .Returns<(bool, string?)>(default); - var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings - { - CallbackUrl = "https://api.com", - UseDimWallet = true - })); - async Task Act() => await logic.HandleClearinghouse(context, CancellationToken.None); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be($"Did must be set for Application {context.ApplicationId}"); - A.CallTo(() => _applicationRepository.GetDidForApplicationId(context.ApplicationId)) - .MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task HandleStartClearingHouse_WithDimActiveAndDidNotSet_ThrowsConflictException() - { - // Arrange - var checklist = ImmutableDictionary.CreateRange([ - new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) - ]); - - var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); - A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) - .Returns((true, null)); - var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings - { - CallbackUrl = "https://api.com", - UseDimWallet = true - })); - async Task Act() => await logic.HandleClearinghouse(context, CancellationToken.None); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be($"Did must be set for Application {context.ApplicationId}"); - A.CallTo(() => _applicationRepository.GetDidForApplicationId(context.ApplicationId)) - .MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task HandleStartClearingHouse_WithDimActive_CallsExpected() - { - // Arrange - var entry = new ApplicationChecklistEntry(Guid.NewGuid(), ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); - - var checklist = ImmutableDictionary.CreateRange([ - new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), - new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) - ]); - - var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); - A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) - .Returns((true, "did:web:test123456")); - SetupForHandleStartClearingHouse(); - var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings - { - CallbackUrl = "https://api.com", - UseDimWallet = true - })); - - // Act - var result = await logic.HandleClearinghouse(context, CancellationToken.None); - - // Assert - A.CallTo(() => _applicationRepository.GetDidForApplicationId(context.ApplicationId)) - .MustHaveHappenedOnceExactly(); - result.ModifyChecklistEntry.Should().NotBeNull(); - result.ModifyChecklistEntry!.Invoke(entry); - entry.ApplicationChecklistEntryStatusId.Should().Be(ApplicationChecklistEntryStatusId.IN_PROGRESS); - A.CallTo(() => _clearinghouseService.TriggerCompanyDataPost(A._, A._)) - .MustHaveHappenedOnceExactly(); - result.ScheduleStepTypeIds.Should().HaveCount(1); - result.ScheduleStepTypeIds.Should().Contain(ProcessStepTypeId.AWAIT_CLEARING_HOUSE_RESPONSE); - result.SkipStepTypeIds.Should().BeNull(); - result.Modified.Should().BeTrue(); - } - #endregion #region ProcessClearinghouseResponse @@ -326,10 +222,19 @@ public async Task ProcessClearinghouseResponseAsync_WithConfirmation_UpdatesEntr { // Arrange var entry = new ApplicationChecklistEntry(IdWithBpn, ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); + var reasonMessage = _fixture.Build() + .With(x => x.DetailMessage, default(string?)) + .Create(); + + var validationUnits = _fixture.Build() + .With(x => x.Status, ClearinghouseResponseStatus.VALID) + .With(x => x.Reason, reasonMessage) + .CreateMany(2); + var data = _fixture.Build() - .With(x => x.Status, ClearinghouseResponseStatus.CONFIRM) - .With(x => x.Message, default(string?)) + .With(x => x.ValidationUnits, validationUnits) .Create(); + SetupForProcessClearinghouseResponse(entry); // Act @@ -338,7 +243,7 @@ public async Task ProcessClearinghouseResponseAsync_WithConfirmation_UpdatesEntr // Assert A.CallTo(() => _checklistService.FinalizeChecklistEntryAndProcessSteps(A._, A>._, A>._, A>.That.Matches(x => x.Count(y => y == ProcessStepTypeId.START_SELF_DESCRIPTION_LP) == 1))).MustHaveHappenedOnceExactly(); A.CallTo(() => _portalRepositories.SaveAsync()).MustNotHaveHappened(); - entry.Comment.Should().BeNull(); + entry.Comment.Should().Be("VALID"); entry.ApplicationChecklistEntryStatusId.Should().Be(ApplicationChecklistEntryStatusId.DONE); } @@ -347,9 +252,17 @@ public async Task ProcessClearinghouseResponseAsync_WithDecline_UpdatesEntry() { // Arrange var entry = new ApplicationChecklistEntry(IdWithBpn, ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); + var reasonMessage = _fixture.Build() + .With(x => x.DetailMessage, "Comment about the error") + .Create(); + + var validationUnits = _fixture.Build() + .With(x => x.Status, ClearinghouseResponseStatus.INVALID) + .With(x => x.Reason, reasonMessage) + .CreateMany(2); + var data = _fixture.Build() - .With(x => x.Status, ClearinghouseResponseStatus.DECLINE) - .With(x => x.Message, "Comment about the error") + .With(x => x.ValidationUnits, validationUnits) .Create(); SetupForProcessClearinghouseResponse(entry); @@ -359,7 +272,7 @@ public async Task ProcessClearinghouseResponseAsync_WithDecline_UpdatesEntry() // Assert A.CallTo(() => _checklistService.FinalizeChecklistEntryAndProcessSteps(A._, A>._, A>._, A>.That.Matches(x => x.Count(y => y == ProcessStepTypeId.MANUAL_TRIGGER_OVERRIDE_CLEARING_HOUSE) == 1))).MustHaveHappenedOnceExactly(); A.CallTo(() => _portalRepositories.SaveAsync()).MustNotHaveHappened(); - entry.Comment.Should().Be("Comment about the error"); + entry.Comment.Should().Be("Comment about the error, Comment about the error"); entry.ApplicationChecklistEntryStatusId.Should().Be(ApplicationChecklistEntryStatusId.FAILED); } @@ -392,26 +305,17 @@ public async Task CheckEndClearinghouseProcesses_WithEntry_CreatesProcessStep() private void SetupForHandleStartClearingHouse() { - A.CallTo(() => _custodianBusinessLogic.GetWalletByBpnAsync(A.That.Matches(x => x == IdWithoutBpn || x == IdWithBpn || x == IdWithApplicationCreated), A._)) - .Returns(new WalletData("Name", ValidBpn, ValidDid, DateTime.UtcNow, false, null)); - A.CallTo(() => _custodianBusinessLogic.GetWalletByBpnAsync(IdWithCustodianUnavailable, A._)) - .Returns(null); - A.CallTo(() => _custodianBusinessLogic.GetWalletByBpnAsync(A.That.Not.Matches(x => x == IdWithoutBpn || x == IdWithBpn || x == IdWithApplicationCreated || x == IdWithCustodianUnavailable), A._)) - .Returns(new WalletData("Name", ValidBpn, null, DateTime.UtcNow, false, null)); - - var participantDetailsWithoutBpn = _fixture.Build() - .With(x => x.Bpn, default(string?)) + var addressData = _fixture.Build() .Create(); var clearinghouseDataWithoutBpn = _fixture.Build() .With(x => x.ApplicationStatusId, CompanyApplicationStatusId.SUBMITTED) - .With(x => x.ParticipantDetails, participantDetailsWithoutBpn) - .Create(); - var participantDetails = _fixture.Build() - .With(x => x.Bpn, ValidBpn) + .With(x => x.Address, addressData) + .With(x => x.Bpn, default(string?)) .Create(); var clearinghouseData = _fixture.Build() .With(x => x.ApplicationStatusId, CompanyApplicationStatusId.SUBMITTED) - .With(x => x.ParticipantDetails, participantDetails) + .With(x => x.Address, addressData) + .With(x => x.Bpn, ValidBpn) .Create(); var chDataWithApplicationCreated = _fixture.Build() .With(x => x.ApplicationStatusId, CompanyApplicationStatusId.CREATED) diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/ApplicationRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/ApplicationRepositoryTests.cs index 23170fa938..c9ed18cc08 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/ApplicationRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/ApplicationRepositoryTests.cs @@ -263,7 +263,7 @@ public async Task GetClearinghouseDataForApplicationId_WithValidApplicationId_Re // Assert data.Should().NotBeNull(); - data!.ParticipantDetails.Bpn.Should().Be("BPNL00000003CRHL"); + data!.Bpn.Should().Be("BPNL00000003CRHL"); data.ApplicationStatusId.Should().Be(CompanyApplicationStatusId.SUBMITTED); }