diff --git a/.github/workflows/merge-main.yml b/.github/workflows/merge-main.yml index 4ecef383d8..4f80e9699b 100644 --- a/.github/workflows/merge-main.yml +++ b/.github/workflows/merge-main.yml @@ -297,6 +297,7 @@ jobs: -p ZONE=${{ env.ZONE }} -p NAME=${{ github.event.repository.name }} -p PROMOTE=${{ github.repository }}/processor:${{ env.ZONE }} -p URL_ZONE=${{ env.ZONE }} + -p BCREGISTRY_URI='https://bcregistry-prod.apigee.net' image-promotions: name: Promote images to PROD @@ -449,3 +450,4 @@ jobs: -p ZONE=${{ env.ZONE }} -p NAME=${{ github.event.repository.name }} -p PROMOTE=${{ github.repository }}/processor:${{ env.PREV }} -p URL_ZONE=${{ env.ZONE }} + -p BCREGISTRY_URI='https://bcregistry-prod.apigee.net' diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index 00e91c29f4..c4bc0fd775 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -256,6 +256,7 @@ jobs: -p ZONE=${{ github.event.number }} -p NAME=${{ github.event.repository.name }} -p PROMOTE=${{ github.repository }}/processor:${{ github.event.number }} -p URL_ZONE=$((${{ github.event.number }} % 50)) + -p BCREGISTRY_URI='https://bcregistry-prod.apigee.net' cypress-run: name: "User flow test" diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index caea7e445b..fea9f860ae 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -20,7 +20,7 @@ concurrency: jobs: tests-java: - name: Integrated Tests + name: Backend Tests if: github.event_name != 'pull_request' || !github.event.pull_request.draft runs-on: ubuntu-22.04 steps: @@ -70,7 +70,7 @@ jobs: -Dsonar.projectKey=nr-forest-client_processor -Dsonar.coverage.jacoco.xmlReportPaths=target/coverage-reports/merged-test-report/jacoco.xml -Dsonar.java.checkstyle.reportPaths=target/checkstyle-result.xml - -Dsonar.coverage.exclusions=**/configuration/**,**/dto/**,**/entity/**,**/repository/**,**/*$*Builder*,**/ProcessApplication***/ApplicationConstant* + -Dsonar.coverage.exclusions=**/configuration/**,**/dto/**,**/exception/**,**/entity/**,**/repository/**,**/*$*Builder*,**/ProcessApplication***/ApplicationConstant* sonar_project_token: ${{ secrets.SONAR_TOKEN_PROCESSOR }} - name: Archive CycloneDX diff --git a/backend/pom.xml b/backend/pom.xml index 523c33f6cc..7fed6d9c60 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -138,8 +138,8 @@ test - com.github.tomakehurst - wiremock-jre8-standalone + org.wiremock + wiremock-standalone 3.0.1 test diff --git a/frontend/src/assets/styles/global.scss b/frontend/src/assets/styles/global.scss index 908525dbc9..11d30f3322 100644 --- a/frontend/src/assets/styles/global.scss +++ b/frontend/src/assets/styles/global.scss @@ -43,6 +43,7 @@ --light-theme-miscellaneous-interactive: #0073e6; --light-theme-background-background-selected: #93939533; --light-theme-text-text-disabled: rgba(19, 19, 21, 0.25); + --light-theme-text-text-on-color: #FFFFFF; //Buttons --cds-button-disabled: #c6c6c8; @@ -296,6 +297,7 @@ div#app { align-items: center; background: var(--light-theme-layer-layer-02, #fff); flex-grow: 1; + margin-top: 3.5rem; } .full { @@ -1005,6 +1007,15 @@ cds-header-global-action { background: var(--light-theme-layer-layer-02, #FFF) !important; } +cds-header-menu-button::part(svg) { + width: 1rem; + height: 1rem; +} + +cds-header-menu-button:not([active])::part(svg) { + fill: var(--light-theme-text-text-on-color, #FFF); +} + cds-header-global-action svg { fill: var(--dark-theme-focus-focus); } diff --git a/frontend/src/components/MainHeaderComponent.vue b/frontend/src/components/MainHeaderComponent.vue index 5555dce6b4..d4c0ecf348 100644 --- a/frontend/src/components/MainHeaderComponent.vue +++ b/frontend/src/components/MainHeaderComponent.vue @@ -99,7 +99,9 @@ const onClickLogout = () => { + button-label-inactive="Open menu" + v-shadow="2" + > diff --git a/legacy/pom.xml b/legacy/pom.xml index 519d15438b..f5f55504e9 100644 --- a/legacy/pom.xml +++ b/legacy/pom.xml @@ -150,8 +150,8 @@ test - com.github.tomakehurst - wiremock-jre8-standalone + org.wiremock + wiremock-standalone 3.0.1 test diff --git a/processor/openshift.deploy.yml b/processor/openshift.deploy.yml index a90c9ba3c0..f3670728cb 100644 --- a/processor/openshift.deploy.yml +++ b/processor/openshift.deploy.yml @@ -40,6 +40,9 @@ parameters: - name: URL_ZONE description: Zone to use for URL required: true + - name: BCREGISTRY_URI + description: Bc Registry API address + required: true objects: - apiVersion: v1 kind: ImageStream @@ -195,6 +198,18 @@ objects: value: 30S - name: TZ value: America/Vancouver + - name: BCREGISTRY_URI + value: ${BCREGISTRY_URI} + - name: BCREGISTRY_KEY + valueFrom: + secretKeyRef: + name: ${NAME}-${ZONE} + key: bcregistry-key + - name: BCREGISTRY_ACCOUNT + valueFrom: + secretKeyRef: + name: ${NAME}-${ZONE} + key: bcregistry-account ports: - containerPort: 3000 protocol: TCP diff --git a/processor/pom.xml b/processor/pom.xml index bf91d2c1da..0ff61af729 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -160,9 +160,10 @@ test - com.github.tomakehurst - wiremock-jre8-standalone + org.wiremock + wiremock-standalone 3.0.1 + test diff --git a/processor/src/main/java/ca/bc/gov/app/ApplicationConstant.java b/processor/src/main/java/ca/bc/gov/app/ApplicationConstant.java index f5d30eb1c6..eb9a163154 100644 --- a/processor/src/main/java/ca/bc/gov/app/ApplicationConstant.java +++ b/processor/src/main/java/ca/bc/gov/app/ApplicationConstant.java @@ -1,5 +1,9 @@ package ca.bc.gov.app; +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentAccessRequestDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentAccessTypeDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentRequestBodyDto; +import java.util.List; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -17,6 +21,7 @@ public final class ApplicationConstant { public static final String REVIEW_CHANNEL = "reviewChannel"; public static final String SUBMISSION_MAIL_CHANNEL = "submissionMailChannel"; public static final String SUBMISSION_LEGACY_CLIENT_CHANNEL = "submissionLegacyClientChannel"; + public static final String SUBMISSION_LEGACY_CLIENT_PERSIST_CHANNEL = "submissionLegacyClientPersistChannel"; public static final String SUBMISSION_LEGACY_LOCATION_CHANNEL = "submissionLegacyLocationChannel"; public static final String SUBMISSION_LEGACY_CONTACT_CHANNEL = "submissionLegacyContactChannel"; public static final String SUBMISSION_LEGACY_AGGREGATE_CHANNEL = "submissionLegacyAggregateChannel"; @@ -42,4 +47,20 @@ public final class ApplicationConstant { public static final String LOCATION_CODE = "locationCode"; public static final String SUBMISSION_MAIL_BUILD_CHANNEL = "submissionMailBuildChannel"; public static final String CLIENT_NUMBER = "CLIENT_NUMBER"; + public static final String CLIENT_TYPE_CODE = "CLIENT_TYPE_CODE"; + public static final String SUBMISSION_LEGACY_INDIVIDUAL_CHANNEL = "submissionLegacyIndividualChannel"; + public static final String SUBMISSION_LEGACY_USP_CHANNEL = "submissionLegacyUSPChannel"; + public static final String SUBMISSION_LEGACY_RSP_CHANNEL = "submissionLegacyRSPChannel"; + public static final String SUBMISSION_LEGACY_OTHER_CHANNEL = "submissionLegacyOtherChannel"; + public static final String CLIENT_EXISTS = "client-exists"; + + public static final BcRegistryDocumentRequestBodyDto + BUSINESS_SUMMARY_FILING_HISTORY = + new BcRegistryDocumentRequestBodyDto( + new BcRegistryDocumentAccessRequestDto( + List.of( + new BcRegistryDocumentAccessTypeDto("BUSINESS_SUMMARY_FILING_HISTORY") + ) + ) + ); } diff --git a/processor/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java b/processor/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java index e9ce5068ba..130553a648 100644 --- a/processor/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java +++ b/processor/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java @@ -27,6 +27,9 @@ public class ForestClientConfiguration { @NestedConfigurationProperty private BackendConfiguration backend; + @NestedConfigurationProperty + private BcRegistryConfiguration bcregistry; + @Data @Builder @NoArgsConstructor @@ -43,4 +46,18 @@ public static class BackendConfiguration { private String uri; } + /** + * The BC Registry configuration. + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class BcRegistryConfiguration { + + private String uri; + private String apiKey; + private String accountId; + } + } diff --git a/processor/src/main/java/ca/bc/gov/app/configuration/GlobalServiceConfiguration.java b/processor/src/main/java/ca/bc/gov/app/configuration/GlobalServiceConfiguration.java index c8d3bda245..471171bf6a 100644 --- a/processor/src/main/java/ca/bc/gov/app/configuration/GlobalServiceConfiguration.java +++ b/processor/src/main/java/ca/bc/gov/app/configuration/GlobalServiceConfiguration.java @@ -25,4 +25,20 @@ public WebClient forestClientApi(ForestClientConfiguration configuration) { .build(); } + /** + * Returns a configured instance of WebClient for accessing the BC Registry API. + * + * @param configuration The configuration for the ForestClient. + * @return A configured instance of WebClient for accessing the BC Registry API. + */ + @Bean + public WebClient bcRegistryApi(ForestClientConfiguration configuration) { + return WebClient + .builder() + .baseUrl(configuration.getBcregistry().getUri()) + .defaultHeader("x-apikey", configuration.getBcregistry().getApiKey()) + .defaultHeader("Account-Id", configuration.getBcregistry().getAccountId()) + .build(); + } + } diff --git a/processor/src/main/java/ca/bc/gov/app/configuration/ProcessorIntegrationConfiguration.java b/processor/src/main/java/ca/bc/gov/app/configuration/ProcessorIntegrationConfiguration.java index fcbe464450..69f0e26977 100644 --- a/processor/src/main/java/ca/bc/gov/app/configuration/ProcessorIntegrationConfiguration.java +++ b/processor/src/main/java/ca/bc/gov/app/configuration/ProcessorIntegrationConfiguration.java @@ -92,6 +92,32 @@ public FluxMessageChannel submissionLegacyNotifyChannel() { return new FluxMessageChannel(); } + @Bean + public FluxMessageChannel submissionLegacyClientPersistChannel() { + return new FluxMessageChannel(); + } + + @Bean + public FluxMessageChannel submissionLegacyIndividualChannel() { + return new FluxMessageChannel(); + } + + @Bean + public FluxMessageChannel submissionLegacyUSPChannel() { + return new FluxMessageChannel(); + } + + @Bean + public FluxMessageChannel submissionLegacyRSPChannel() { + return new FluxMessageChannel(); + } + + @Bean + public FluxMessageChannel submissionLegacyOtherChannel() { + return new FluxMessageChannel(); + } + + @Bean public R2dbcMessageSource submissionMessages( diff --git a/processor/src/main/java/ca/bc/gov/app/dto/SubmissionInformationDto.java b/processor/src/main/java/ca/bc/gov/app/dto/SubmissionInformationDto.java index ed0f7faac8..8dbbdb7c85 100644 --- a/processor/src/main/java/ca/bc/gov/app/dto/SubmissionInformationDto.java +++ b/processor/src/main/java/ca/bc/gov/app/dto/SubmissionInformationDto.java @@ -1,11 +1,14 @@ package ca.bc.gov.app.dto; +import java.time.LocalDate; import lombok.With; @With public record SubmissionInformationDto( - String legalName, + String corporationName, + LocalDate dateOfBirth, String incorporationNumber, - String goodStanding + String goodStanding, + String clientType ) { } diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentAccessRequestDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentAccessRequestDto.java new file mode 100644 index 0000000000..6f553306dd --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentAccessRequestDto.java @@ -0,0 +1,12 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.util.List; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryDocumentAccessRequestDto( + List documents +) { +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentAccessTypeDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentAccessTypeDto.java new file mode 100644 index 0000000000..5f03073856 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentAccessTypeDto.java @@ -0,0 +1,13 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryDocumentAccessTypeDto( + @JsonProperty("type") + String documentType +) { +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentDto.java new file mode 100644 index 0000000000..3ac6d5f7cc --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentDto.java @@ -0,0 +1,23 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.util.List; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryDocumentDto( + List parties +) { + + public BcRegistryPartyDto getProprietor() { + if (parties == null) { + return null; + } + return parties + .stream() + .filter(BcRegistryPartyDto::isProprietor) + .findFirst() + .orElse(null); + } +} \ No newline at end of file diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestBodyDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestBodyDto.java new file mode 100644 index 0000000000..c4cbcaa44d --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestBodyDto.java @@ -0,0 +1,11 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryDocumentRequestBodyDto( + BcRegistryDocumentAccessRequestDto documentAccessRequest +) { +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestDocumentDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestDocumentDto.java new file mode 100644 index 0000000000..cde4ef9e06 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestDocumentDto.java @@ -0,0 +1,14 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryDocumentRequestDocumentDto( + String documentKey, + String documentType, + String fileName, + Long id +) { +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestResponseDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestResponseDto.java new file mode 100644 index 0000000000..ef00c42a5e --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryDocumentRequestResponseDto.java @@ -0,0 +1,16 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.util.List; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryDocumentRequestResponseDto( + String businessIdentifier, + String businessName, + List documents, + String paymentStatus, + String status +) { +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryExceptionMessageDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryExceptionMessageDto.java new file mode 100644 index 0000000000..0126c1bcf2 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryExceptionMessageDto.java @@ -0,0 +1,12 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryExceptionMessageDto( + String errorMessage, + String rootCause +) { +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryOfficerDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryOfficerDto.java new file mode 100644 index 0000000000..880c75b761 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryOfficerDto.java @@ -0,0 +1,13 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryOfficerDto( + String email, + String firstName, + String lastName, + String middleInitial, + String partyType +) { +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryPartyDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryPartyDto.java new file mode 100644 index 0000000000..ee2ce9aa19 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryPartyDto.java @@ -0,0 +1,24 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.util.List; +import lombok.With; +import org.apache.commons.lang3.StringUtils; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryPartyDto( + BcRegistryOfficerDto officer, + List roles +) { + + public boolean isProprietor() { + if (roles == null) { + return false; + } + return roles + .stream() + .filter(BcRegistryRoleDto::active) + .anyMatch(role -> StringUtils.equalsIgnoreCase(role.roleType(), "Proprietor")); + } +} diff --git a/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryRoleDto.java b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryRoleDto.java new file mode 100644 index 0000000000..debea97e3e --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/dto/bcregistry/BcRegistryRoleDto.java @@ -0,0 +1,17 @@ +package ca.bc.gov.app.dto.bcregistry; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.time.LocalDate; +import lombok.With; + +@With +@JsonIgnoreProperties(ignoreUnknown = true) +public record BcRegistryRoleDto( + LocalDate appointmentDate, + LocalDate cessationDate, + String roleType +) { + public boolean active() { + return cessationDate == null || LocalDate.now().isBefore(cessationDate); + } +} diff --git a/processor/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java b/processor/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java index 481981a2dd..1a9d54db4c 100644 --- a/processor/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java +++ b/processor/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java @@ -1,12 +1,14 @@ package ca.bc.gov.app.entity.client; import ca.bc.gov.app.ApplicationConstant; +import java.time.LocalDate; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.With; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; @@ -42,4 +44,7 @@ public class SubmissionDetailEntity { @Column("good_standing_ind") private String goodStandingInd; + + @Column("birthdate") + private LocalDate birthdate; } diff --git a/processor/src/main/java/ca/bc/gov/app/entity/legacy/ClientDoingBusinessAsEntity.java b/processor/src/main/java/ca/bc/gov/app/entity/legacy/ClientDoingBusinessAsEntity.java new file mode 100644 index 0000000000..a93c71283e --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/entity/legacy/ClientDoingBusinessAsEntity.java @@ -0,0 +1,46 @@ +package ca.bc.gov.app.entity.legacy; + +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +@With +@Table(name = "CLIENT_DOING_BUSINESS_AS", schema = "THE") +public class ClientDoingBusinessAsEntity { + + @Id + @Column("CLIENT_DBA_ID") + private Integer id; + + @Column("CLIENT_NUMBER") + private String clientNumber; + + @Column("DOING_BUSINESS_AS_NAME") + private String doingBusinessAsName; + + @Column("ADD_TIMESTAMP") + private LocalDateTime createdAt; + @Column("ADD_USERID") + private String createdBy; + @Column("UPDATE_TIMESTAMP") + private LocalDateTime updatedAt; + @Column("UPDATE_USERID") + private String updatedBy; + @Column("UPDATE_ORG_UNIT") + private Long updateOrgUnit; + @Column("ADD_ORG_UNIT") + private Long addOrgUnit; + @Column("REVISION_COUNT") + private Long revision; + +} diff --git a/processor/src/main/java/ca/bc/gov/app/exception/InvalidAccessTokenException.java b/processor/src/main/java/ca/bc/gov/app/exception/InvalidAccessTokenException.java new file mode 100644 index 0000000000..7d1c09c0ea --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/exception/InvalidAccessTokenException.java @@ -0,0 +1,14 @@ +package ca.bc.gov.app.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.server.ResponseStatusException; + +@ResponseStatus(HttpStatus.UNAUTHORIZED) +public class InvalidAccessTokenException extends ResponseStatusException { + + public InvalidAccessTokenException() { + super(HttpStatus.UNAUTHORIZED, "Provided access token is missing or invalid"); + } + +} diff --git a/processor/src/main/java/ca/bc/gov/app/exception/NoClientDataFound.java b/processor/src/main/java/ca/bc/gov/app/exception/NoClientDataFound.java new file mode 100644 index 0000000000..68b0312708 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/exception/NoClientDataFound.java @@ -0,0 +1,15 @@ +package ca.bc.gov.app.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.server.ResponseStatusException; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class NoClientDataFound extends ResponseStatusException { + + public NoClientDataFound(String clientNumber) { + super(HttpStatus.NOT_FOUND, + String.format("No data found for client number %s", clientNumber)); + } + +} diff --git a/processor/src/main/java/ca/bc/gov/app/repository/legacy/ClientDoingBusinessAsRepository.java b/processor/src/main/java/ca/bc/gov/app/repository/legacy/ClientDoingBusinessAsRepository.java new file mode 100644 index 0000000000..a88b803025 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/repository/legacy/ClientDoingBusinessAsRepository.java @@ -0,0 +1,25 @@ +package ca.bc.gov.app.repository.legacy; + +import ca.bc.gov.app.entity.legacy.ClientDoingBusinessAsEntity; +import org.springframework.data.r2dbc.repository.Query; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Repository +public interface ClientDoingBusinessAsRepository extends + ReactiveCrudRepository, + ReactiveSortingRepository { + + Mono existsByClientNumber(String clientNumber); + + @Query(""" + SELECT * + FROM THE.CLIENT_DOING_BUSINESS_AS + WHERE + UTL_MATCH.JARO_WINKLER_SIMILARITY(UPPER(DOING_BUSINESS_AS_NAME),UPPER(:companyName)) >= 95 + ORDER BY CLIENT_NUMBER""") + Flux matchBy(String companyName); +} diff --git a/processor/src/main/java/ca/bc/gov/app/repository/legacy/ForestClientRepository.java b/processor/src/main/java/ca/bc/gov/app/repository/legacy/ForestClientRepository.java index 98bb558ca3..a58b118b61 100644 --- a/processor/src/main/java/ca/bc/gov/app/repository/legacy/ForestClientRepository.java +++ b/processor/src/main/java/ca/bc/gov/app/repository/legacy/ForestClientRepository.java @@ -1,6 +1,7 @@ package ca.bc.gov.app.repository.legacy; import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import java.time.LocalDateTime; import org.springframework.data.r2dbc.repository.Query; import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor; import org.springframework.data.repository.reactive.ReactiveCrudRepository; @@ -31,4 +32,16 @@ public interface ForestClientRepository extends ReactiveCrudRepository findByIncorporationNumber(String incorporationNumber); + @Query(""" + SELECT * + FROM THE.FOREST_CLIENT + WHERE + UPPER(LEGAL_FIRST_NAME) = UPPER(:firstName) + AND UPPER(CLIENT_NAME) = UPPER(:lastName) + AND BIRTHDATE = :dob + AND CLIENT_STATUS_CODE = 'ACT' + AND CLIENT_TYPE_CODE = 'I' + ORDER BY CLIENT_NUMBER""") + Flux findByIndividual(String firstName, String lastName, LocalDateTime dob); + } diff --git a/processor/src/main/java/ca/bc/gov/app/service/bcregistry/BcRegistryService.java b/processor/src/main/java/ca/bc/gov/app/service/bcregistry/BcRegistryService.java new file mode 100644 index 0000000000..62f6a917b4 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/bcregistry/BcRegistryService.java @@ -0,0 +1,113 @@ +package ca.bc.gov.app.service.bcregistry; + +import static ca.bc.gov.app.ApplicationConstant.BUSINESS_SUMMARY_FILING_HISTORY; + +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentRequestDocumentDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentRequestResponseDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryExceptionMessageDto; +import ca.bc.gov.app.exception.InvalidAccessTokenException; +import ca.bc.gov.app.exception.NoClientDataFound; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Slf4j +@Service +public class BcRegistryService { + + private final WebClient bcRegistryApi; + + public BcRegistryService(@Qualifier("bcRegistryApi") WebClient bcRegistryApi) { + this.bcRegistryApi = bcRegistryApi; + } + + /** + * Sends a request to retrieve the document data for a given value using the BC Registry API. The + * method returns a Flux of {@link BcRegistryDocumentDto}, which represents the document data. + * + * @param value the value used to identify the document data + * @return a Flux of {@link BcRegistryDocumentDto} representing the requested document data + * @throws NoClientDataFound if the API responds with a 404 status code indicating that + * no data was found for the given value + * @throws InvalidAccessTokenException if the API responds with a 401 status code indicating that + * the access token used for the request is invalid + */ + public Flux requestDocumentData(String value) { + log.info("Requesting document for {}", value); + return + bcRegistryApi + .post() + .uri("/registry-search/api/v1/businesses/{identifier}/documents/requests", + Map.of("identifier", value) + ) + .accept(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(BUSINESS_SUMMARY_FILING_HISTORY)) + .retrieve() + // handle different HTTP error codes + .onStatus( + statusCode -> statusCode.isSameCodeAs(HttpStatusCode.valueOf(404)), + exception -> Mono.error(new NoClientDataFound(value)) + ) + .onStatus( + statusCode -> statusCode.isSameCodeAs(HttpStatusCode.valueOf(401)), + exception -> Mono.error(new InvalidAccessTokenException()) + ) + .onStatus( + statusCode -> statusCode.isSameCodeAs(HttpStatusCode.valueOf(400)), + exception -> + exception + .bodyToMono(BcRegistryExceptionMessageDto.class) + .map(BcRegistryExceptionMessageDto::rootCause) + .doOnNext( + message -> log.error("Error while requesting data for {} -- {}", value, + message)) + .map(message -> message.contains("not found")) + .filter(message -> message) + .switchIfEmpty(Mono.error(new InvalidAccessTokenException())) + .flatMap(message -> Mono.error(new NoClientDataFound(value))) + + ) + .bodyToMono(BcRegistryDocumentRequestResponseDto.class) + .flatMapIterable(BcRegistryDocumentRequestResponseDto::documents) + .map(BcRegistryDocumentRequestDocumentDto::documentKey) + .doOnNext(documentKey -> log.info("Loading document {} for identifier {}", documentKey, + value)) + .flatMap(documentKey -> getDocumentData(value, documentKey)); + } + + private Mono getDocumentData(String identifier, String documentKey) { + return + bcRegistryApi + .get() + .uri("/registry-search/api/v1/businesses/{identifier}/documents/{documentKey}", + Map.of("identifier", identifier, "documentKey", documentKey) + ) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + // handle different HTTP error codes + .onStatus( + statusCode -> statusCode.isSameCodeAs(HttpStatusCode.valueOf(404)), + exception -> Mono.error(new NoClientDataFound(identifier)) + ) + .onStatus( + statusCode -> statusCode.isSameCodeAs(HttpStatusCode.valueOf(401)), + exception -> Mono.error(new InvalidAccessTokenException()) + ) + .onStatus( + statusCode -> statusCode.isSameCodeAs(HttpStatusCode.valueOf(400)), + exception -> Mono.error(new InvalidAccessTokenException()) + ) + .bodyToMono(BcRegistryDocumentDto.class) + .doOnNext( + document -> log.info("Document loaded for {} {} as {}", identifier, documentKey, + document)); + } +} diff --git a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionAutoProcessingService.java b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionAutoProcessingService.java index d7f7e1fd82..e268fe8659 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionAutoProcessingService.java +++ b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionAutoProcessingService.java @@ -33,15 +33,15 @@ public class ClientSubmissionAutoProcessingService { private final SubmissionRepository submissionRepository; private final SubmissionMatchDetailRepository submissionMatchDetailRepository; + /** + * This method is responsible for marking the submission as approved + * and sending to the nexty step. + */ @ServiceActivator( inputChannel = ApplicationConstant.AUTO_APPROVE_CHANNEL, outputChannel = ApplicationConstant.SUBMISSION_POSTPROCESSOR_CHANNEL, async = "true" ) - /** - * This method is responsible for marking the submission as approved - * and sending to the nexty step. - */ public Mono> approved(Message> message) { int submissionId = Objects.requireNonNull( @@ -69,14 +69,14 @@ public Mono> approved(Message> message) { } + /** + * This method is responsible for marking the submission as processed + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_COMPLETION_CHANNEL, outputChannel = ApplicationConstant.SUBMISSION_MAIL_CHANNEL, async = "true" ) - /** - * This method is responsible for marking the submission as processed - */ public Mono> completeProcessing(Message message) { return submissionMatchDetailRepository @@ -88,14 +88,14 @@ public Mono> completeProcessing(Message> reviewed(Message> message) { return persistData( diff --git a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionLoadingService.java b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionLoadingService.java index a6c82ace80..9505bb9ebe 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionLoadingService.java +++ b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionLoadingService.java @@ -5,8 +5,8 @@ import ca.bc.gov.app.dto.SubmissionInformationDto; import ca.bc.gov.app.repository.client.SubmissionContactRepository; import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import java.time.LocalDate; import java.util.Map; -import java.util.Objects; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.integration.annotation.ServiceActivator; @@ -15,25 +15,27 @@ import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; -@Service -@RequiredArgsConstructor -@Slf4j + /** * This class is responsible for loading the submission details and submission contact details */ +@Service +@RequiredArgsConstructor +@Slf4j public class ClientSubmissionLoadingService { private final SubmissionDetailRepository submissionDetailRepository; private final SubmissionContactRepository contactRepository; + + /** + * Load the submission details to be processed later on + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_LIST_CHANNEL, outputChannel = ApplicationConstant.MATCH_CHECKING_CHANNEL, async = "true" ) - /** - * Load the submission details to be processed later on - */ public Mono> loadSubmissionDetails(Integer submissionId) { return @@ -43,8 +45,10 @@ public Mono> loadSubmissionDetails(Integer sub //Grab what we need for the match part .map(details -> new SubmissionInformationDto( details.getOrganizationName(), + details.getBirthdate(), details.getIncorporationNumber(), - details.getGoodStandingInd() + details.getGoodStandingInd(), + details.getClientTypeCode() ) ) @@ -57,14 +61,15 @@ public Mono> loadSubmissionDetails(Integer sub ); } + + /** + * Build the email request dto to be sent to the email service + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_MAIL_BUILD_CHANNEL, outputChannel = ApplicationConstant.SUBMISSION_MAIL_CHANNEL, async = "true" ) - /** - * Build the email request dto to be sent to the email service - */ public Mono> sendNotification(Message message) { return diff --git a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionProcessingService.java b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionProcessingService.java index 8490bd858b..74dc0a2bde 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionProcessingService.java +++ b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionProcessingService.java @@ -31,16 +31,18 @@ public class ClientSubmissionProcessingService { private final SubmissionMatchDetailRepository submissionMatchDetailRepository; private final SubmissionContactRepository contactRepository; + + /** + * This method will process the submission and send the notification to the user. + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_POSTPROCESSOR_CHANNEL, outputChannel = ApplicationConstant.NOTIFICATION_PROCESSING_CHANNEL, async = "true" ) - /** - * This method will process the submission and send the notification to the user. - */ public Mono> processSubmission( - Message submissionMessage) { + Message submissionMessage + ) { Integer submissionId = submissionMessage.getPayload(); return @@ -73,15 +75,16 @@ public Mono> processSubmission( ); } + + /** + * This method will process the submission and send the notification to the user. + */ + @SuppressWarnings("java:S1452") @ServiceActivator( inputChannel = ApplicationConstant.NOTIFICATION_PROCESSING_CHANNEL, outputChannel = ApplicationConstant.FORWARD_CHANNEL, async = "true" ) - /** - * This method will process the submission and send the notification to the user. - */ - @SuppressWarnings("java:S1452") public Mono> notificationProcessing(Message message) { SubmissionStatusEnum status = ProcessorUtil.readHeader(message, @@ -159,4 +162,13 @@ public Mono> notificationProcessing(Message message) { + submissionMatchDetailRepository + .findBySubmissionId(message.getPayload()) + .map(match -> match.withProcessed(true)) + .flatMap(submissionMatchDetailRepository::save) + .subscribe(match -> log.info("Updated match for submission {}", match.getSubmissionId())); + } + } diff --git a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyPersistenceService.java b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceService.java similarity index 64% rename from processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyPersistenceService.java rename to processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceService.java index 3da13ea433..08b5cda4e9 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyPersistenceService.java +++ b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceService.java @@ -1,13 +1,14 @@ - package ca.bc.gov.app.service.legacy; +import static java.util.function.Predicate.not; + import ca.bc.gov.app.ApplicationConstant; -import ca.bc.gov.app.dto.EmailRequestDto; import ca.bc.gov.app.entity.client.SubmissionContactEntity; import ca.bc.gov.app.entity.client.SubmissionDetailEntity; import ca.bc.gov.app.entity.client.SubmissionLocationContactEntity; import ca.bc.gov.app.entity.client.SubmissionLocationEntity; import ca.bc.gov.app.entity.client.SubmissionTypeCodeEnum; +import ca.bc.gov.app.entity.legacy.ClientDoingBusinessAsEntity; import ca.bc.gov.app.entity.legacy.ForestClientContactEntity; import ca.bc.gov.app.entity.legacy.ForestClientEntity; import ca.bc.gov.app.entity.legacy.ForestClientLocationEntity; @@ -17,7 +18,7 @@ import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; import ca.bc.gov.app.repository.client.SubmissionLocationRepository; import ca.bc.gov.app.repository.client.SubmissionRepository; -import ca.bc.gov.app.repository.legacy.ForestClientContactRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; import ca.bc.gov.app.util.ProcessorUtil; import jakarta.annotation.PostConstruct; import java.time.LocalDateTime; @@ -25,9 +26,12 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.IntFunction; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RegExUtils; @@ -35,38 +39,48 @@ import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.r2dbc.core.R2dbcEntityOperations; -import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; import org.springframework.data.relational.core.query.Criteria; import org.springframework.data.relational.core.query.Query; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -@Service -@Slf4j -@RequiredArgsConstructor /** * This class is responsible for persisting the submission into the legacy database. */ -public class LegacyPersistenceService { +@Service +@Slf4j +@RequiredArgsConstructor +public abstract class LegacyAbstractPersistenceService { + @Getter private final SubmissionDetailRepository submissionDetailRepository; private final SubmissionRepository submissionRepository; private final SubmissionLocationRepository locationRepository; + @Getter private final SubmissionContactRepository contactRepository; private final SubmissionLocationContactRepository locationContactRepository; private final R2dbcEntityOperations legacyR2dbcEntityTemplate; private final CountryCodeRepository countryCodeRepository; + private final ClientDoingBusinessAsRepository doingBusinessAsRepository; private final Map countryList = new HashMap<>(); - @PostConstruct + + abstract Mono> generateForestClient(Message message); + + abstract boolean filterByType(String clientTypeCode); + + abstract String getNextChannel(); + /** * Loads the country list from the database. */ + @PostConstruct public void setUp() { countryCodeRepository .findAll() @@ -80,15 +94,14 @@ public void setUp() { .subscribe(); } - + /** + * Loads the submission from the database and prepares the message for next step. + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_LEGACY_CHANNEL, outputChannel = ApplicationConstant.SUBMISSION_LEGACY_CLIENT_CHANNEL, async = "true" ) - /** - * Loads the submission from the database and prepares the message for next step. - */ public Mono> loadSubmission(Message message) { return submissionRepository .findById(message.getPayload()) @@ -105,18 +118,21 @@ public Mono> loadSubmission(Message message) { } + /** + * Checks if the client number exists for that submission and prepares the message for next step. + * The next step will be identified and handled by individual channels + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_LEGACY_CLIENT_CHANNEL, - outputChannel = ApplicationConstant.SUBMISSION_LEGACY_LOCATION_CHANNEL, + outputChannel = ApplicationConstant.FORWARD_CHANNEL, //Dummy channel name async = "true" ) - /** - * Creates a client if does not exist on oracle and get back the client number. - */ - public Mono> createForestClient(Message message) { + public Mono> checkClientData(Message message) { + + AtomicBoolean existingClient = new AtomicBoolean(false); + AtomicReference clientTypeCode = new AtomicReference<>(StringUtils.EMPTY); - // Load the details of the submission - Mono submission = submissionDetailRepository + return submissionDetailRepository .findBySubmissionId(message.getPayload()) .doOnNext( submissionDetail -> @@ -126,63 +142,91 @@ public Mono> createForestClient(Message message) { submissionDetail.getOrganizationName(), submissionDetail.getIncorporationNumber() ) - ); - - // Checks if the client number exists for that submission - Mono clientExists = submission - .flatMap(submissionDetail -> Mono.justOrEmpty( - Optional.ofNullable(submissionDetail.getClientNumber()) - ) ) + .doOnNext(submissionDetail -> + clientTypeCode.set(submissionDetail.getClientTypeCode())) + .flatMap(submissionDetail -> + Mono.justOrEmpty( + Optional + .ofNullable(submissionDetail.getClientNumber()) + .filter(StringUtils::isNotBlank) + ) + .doOnNext(clientNumber -> log.info( + "Client number {} exists for submission {}", + clientNumber, + message.getPayload() + )) + .doOnNext(clientNumber -> existingClient.set(true)) + .switchIfEmpty(getNextClientNumber()) + ) + .filter(data -> filterByType(clientTypeCode.get())) .doOnNext(clientNumber -> log.info( - "Client number {} exists for submission {}", + "Client number {}{} for submission {}", clientNumber, + existingClient.get() ? " exists" : " is new", message.getPayload() ) + ) + .map(clientNumber -> + MessageBuilder + .withPayload(existingClient.get() ? message.getPayload() : clientNumber) + .copyHeaders(message.getHeaders()) + .setHeader(ApplicationConstant.SUBMISSION_ID, message.getPayload()) + .setHeader(ApplicationConstant.CLIENT_EXISTS, existingClient.get()) + .setHeader(ApplicationConstant.CLIENT_TYPE_CODE, clientTypeCode.get()) + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, clientNumber) + .setReplyChannelName( + existingClient.get() ? ApplicationConstant.SUBMISSION_LEGACY_LOCATION_CHANNEL + : getNextChannel()) + .setHeader("output-channel", + existingClient.get() ? ApplicationConstant.SUBMISSION_LEGACY_LOCATION_CHANNEL + : getNextChannel()) + .setHeader(MessageHeaders.REPLY_CHANNEL, + existingClient.get() ? ApplicationConstant.SUBMISSION_LEGACY_LOCATION_CHANNEL + : getNextChannel()) + .build() ); + } - // Convert the submission into a forest client entity - Mono client = submission - .map(this::toForestClientEntity) - .doOnNext(forestClient -> forestClient.setCreatedBy(getUser(message, - ApplicationConstant.CREATED_BY))) - .doOnNext(forestClient -> forestClient.setUpdatedBy(getUser(message, - ApplicationConstant.UPDATED_BY))); + /** + * Creates a client if does not exist on oracle and get back the client number. + */ + @ServiceActivator( + inputChannel = ApplicationConstant.SUBMISSION_LEGACY_CLIENT_PERSIST_CHANNEL, + outputChannel = ApplicationConstant.SUBMISSION_LEGACY_LOCATION_CHANNEL, + async = "true" + ) + public Mono> createForestClient(Message message) { - // Grabs the next forest client number for insertion - Mono nextClientNumber = legacyR2dbcEntityTemplate - .selectOne( - Query - .empty() - .sort(Sort.by(Direction.DESC, ApplicationConstant.CLIENT_NUMBER)) - .limit(1), - ForestClientEntity.class - ) - .map(ForestClientEntity::getClientNumber) - .map(lastForestClientNumber -> String.format("%08d", - Integer.parseInt(lastForestClientNumber) + 1) - ); + if (!filterByType( + message.getHeaders().get(ApplicationConstant.CLIENT_TYPE_CODE, String.class))) { + return Mono.empty(); + } - // Saves the forest client entity and gets the client number and updates on the database - Mono savedClient = - nextClientNumber - .flatMap(clientNumber -> - client - .doOnNext(forestClient -> forestClient.setClientNumber(clientNumber)) - .flatMap(forestClient -> - legacyR2dbcEntityTemplate - .insert(ForestClientEntity.class) - .using(forestClient) - ) + log.info("Creating Forest Client {} {}", + message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NAME), + message.getPayload().getClientNumber() + ); + return + legacyR2dbcEntityTemplate + .insert(ForestClientEntity.class) + .using(message.getPayload()) + .flatMap(forestClient -> + Mono.just(isRegisteredSoleProprietorship(forestClient)) + .filter(Boolean::booleanValue) + .flatMap(isRSP -> createClientDoingBusinessAs(message, forestClient)) + .thenReturn(forestClient.getClientNumber()) + .defaultIfEmpty(forestClient.getClientNumber()) ) - .map(ForestClientEntity::getClientNumber) .flatMap(clientNumber -> submissionDetailRepository - .findBySubmissionId(message.getPayload()) + .findBySubmissionId( + message.getHeaders().get(ApplicationConstant.SUBMISSION_ID, Integer.class) + ) .map(submissionDetail -> submissionDetail.withClientNumber(clientNumber)) .flatMap(submissionDetailRepository::save) - .map(submissionDetail -> clientNumber) + .map(SubmissionDetailEntity::getClientNumber) ) .doOnNext(forestClientNumber -> log.info( @@ -190,39 +234,33 @@ public Mono> createForestClient(Message message) { message.getPayload(), forestClientNumber ) - ); - - return - // If the clients number already exists, move on, else do the save - clientExists - .switchIfEmpty(savedClient) - // Get the details and prepare the message for next step - .flatMap(forestClientNumber -> - submission - .map(forestClientDetail -> - MessageBuilder - .fromMessage(message) - .copyHeaders(message.getHeaders()) - .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, forestClientNumber) - .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, - forestClientDetail.getOrganizationName()) - .setHeader(ApplicationConstant.INCORPORATION_NUMBER, - forestClientDetail.getIncorporationNumber()) - .build() - ) + ) + .map(forestClientNumber -> + MessageBuilder + .withPayload( + message.getHeaders().get(ApplicationConstant.SUBMISSION_ID, Integer.class)) + .copyHeaders(message.getHeaders()) + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, forestClientNumber) + .build() ); } + + /** + * Creates a location if does not exist on oracle. + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_LEGACY_LOCATION_CHANNEL, outputChannel = ApplicationConstant.SUBMISSION_LEGACY_CONTACT_CHANNEL, async = "true" ) - /** - * Creates a location if does not exist on oracle. - */ public Flux> createLocations(Message message) { + if (!filterByType( + message.getHeaders().get(ApplicationConstant.CLIENT_TYPE_CODE, String.class))) { + return Flux.empty(); + } + Flux data = locationRepository.findBySubmissionId( message.getPayload() ); @@ -301,17 +339,22 @@ public Flux> createLocations(Message message) { } + /** + * Creates a contact if does not exist on oracle. It first checks for an existing entry and if it + * does not have, create it. + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_LEGACY_CONTACT_CHANNEL, outputChannel = ApplicationConstant.SUBMISSION_LEGACY_AGGREGATE_CHANNEL, async = "true" ) - /** - * Creates a contact if does not exist on oracle. - * It first checks for an existing entry and if it does not have, create it. - */ public Mono> createContact(Message message) { + if (!filterByType( + message.getHeaders().get(ApplicationConstant.CLIENT_TYPE_CODE, String.class))) { + return Mono.empty(); + } + // Load the contact in case it exists IntFunction> forestContact = contactId -> @@ -323,7 +366,8 @@ public Mono> createContact(Message message) { Query .query( Criteria - .where(ApplicationConstant.CLIENT_NUMBER).is(getClientNumber(message)) + .where(ApplicationConstant.CLIENT_NUMBER) + .is(getClientNumber(message)) .and("CLIENT_LOCN_CODE").is( Objects.requireNonNull(message.getHeaders() .get(ApplicationConstant.LOCATION_CODE, String.class) @@ -347,7 +391,7 @@ public Mono> createContact(Message message) { ); // Load the next contact id - Mono nextContactId = legacyR2dbcEntityTemplate + IntFunction> nextContactId = increment -> legacyR2dbcEntityTemplate .selectOne( Query .empty() @@ -357,7 +401,7 @@ public Mono> createContact(Message message) { ) .map(ForestClientContactEntity::getClientContactId) .map(lastForestClientContactId -> String.valueOf( - Integer.parseInt(lastForestClientContactId) + 1)); + Integer.parseInt(lastForestClientContactId) + increment)); // Load the contact and converts it into a forest client contact entity IntFunction> toContact = contactId -> @@ -384,50 +428,63 @@ public Mono> createContact(Message message) { ); // Convert the contact into a forest client contact entity and save it - Function> createContact = locationContact -> - toContact - .apply(locationContact.getSubmissionContactId()) - .flatMap(forestClientContact -> - nextContactId - .doOnNext(forestClientContact::setClientContactId) - .thenReturn(forestClientContact) - ) - .flatMap(contact -> - legacyR2dbcEntityTemplate - .insert(ForestClientContactEntity.class) - .using(contact) - ) - .doOnNext(forestClientContact -> - log.info( - "Saved forest client contact {} {} {}", - message.getPayload(), - getClientNumber(message), - forestClientContact.getContactName() + BiFunction> createContact = + (locationContact, increment) -> + toContact + .apply(locationContact.getSubmissionContactId()) + .flatMap(forestClientContact -> + nextContactId + .apply(increment) + .doOnNext(forestClientContact::setClientContactId) + .thenReturn(forestClientContact) ) - ); + .flatMap(contact -> + legacyR2dbcEntityTemplate + .insert(ForestClientContactEntity.class) + .using(contact) + ) + .doOnNext(forestClientContact -> + log.info( + "Saved forest client contact {} {} {}", + message.getPayload(), + getClientNumber(message), + forestClientContact.getContactName() + ) + ); return locationContactRepository .findBySubmissionLocationId( message.getHeaders().get(ApplicationConstant.LOCATION_ID, Integer.class) ) - .flatMap(locationContact -> + .index() + .flatMap(locationContactTuple -> forestContact - .apply(locationContact.getSubmissionContactId()) - .switchIfEmpty(createContact.apply(locationContact)) + .apply(locationContactTuple.getT2().getSubmissionContactId()) + .switchIfEmpty( + createContact + .apply( + locationContactTuple.getT2(), + locationContactTuple.getT1().intValue() + 1 + ) + ) ) .collectList() .thenReturn(message); } + /** + * Sends a notification to the user that the submission has been processed + */ @ServiceActivator( inputChannel = ApplicationConstant.SUBMISSION_LEGACY_NOTIFY_CHANNEL, outputChannel = ApplicationConstant.SUBMISSION_MAIL_BUILD_CHANNEL, async = "true" ) - /** - * Sends a notification to the user that the submission has been processed - */ public Mono> sendNotification(Message message) { + if (!filterByType( + message.getHeaders().get(ApplicationConstant.CLIENT_TYPE_CODE, String.class))) { + return Mono.empty(); + } return Mono.just( MessageBuilder .fromMessage(message) @@ -436,27 +493,14 @@ public Mono> sendNotification(Message message) { ); } - - private ForestClientEntity toForestClientEntity( - SubmissionDetailEntity submissionDetail + protected ForestClientEntity getBaseForestClient( + String createdBy, + String updatedBy ) { return ForestClientEntity .builder() .clientNumber("000") - .legalFirstName(null) - .legalMiddleName(null) - .clientIdTypeCode(null) - .clientIdentification(null) - .clientAcronym(null) - .wcbFirmNumber(null) - .ocgSupplierNmbr(null) - .clientComment(null) .clientStatusCode("ACT") - .clientName(submissionDetail.getOrganizationName().toUpperCase()) - .clientTypeCode(submissionDetail.getClientTypeCode()) - .registryCompanyTypeCode( - ProcessorUtil.extractLetters(submissionDetail.getIncorporationNumber())) - .corpRegnNmbr(ProcessorUtil.extractNumbers(submissionDetail.getIncorporationNumber())) .createdAt(LocalDateTime.now()) .updatedAt(LocalDateTime.now()) .revision(1L) @@ -464,9 +508,94 @@ private ForestClientEntity toForestClientEntity( .updatedBy(ApplicationConstant.PROCESSOR_USER_NAME) .addOrgUnit(ApplicationConstant.ORG_UNIT) .updateOrgUnit(ApplicationConstant.ORG_UNIT) + .createdBy(createdBy) + .updatedBy(updatedBy) .build(); } + + protected String getUser(Message message, String headerName) { + return ProcessorUtil + .readHeader( + message, + headerName, + String.class + ) + .orElse(ApplicationConstant.PROCESSOR_USER_NAME); + } + + private Mono getNextClientNumber() { + return legacyR2dbcEntityTemplate + .selectOne( + Query + .empty() + .sort(Sort.by(Direction.DESC, ApplicationConstant.CLIENT_NUMBER)) + .limit(1), + ForestClientEntity.class + ) + .map(ForestClientEntity::getClientNumber) + .map(lastForestClientNumber -> String.format("%08d", + Integer.parseInt(lastForestClientNumber) + 1) + ); + } + + private Mono getNextDoingBusinessAs() { + return legacyR2dbcEntityTemplate + .selectOne( + Query + .empty() + .sort(Sort.by(Direction.DESC, "CLIENT_DBA_ID")) + .limit(1), + ClientDoingBusinessAsEntity.class + ) + .map(ClientDoingBusinessAsEntity::getId) + .map(lastId -> lastId + 1); + } + + private Mono createClientDoingBusinessAs( + Message message, ForestClientEntity forestClient) { + return doingBusinessAsRepository + .existsByClientNumber(forestClient.getClientNumber()) + .filter(not(Boolean::booleanValue)) + .flatMap(doesNotExist -> getNextDoingBusinessAs()) + .map(nextId -> + ClientDoingBusinessAsEntity + .builder() + .id(nextId) + .clientNumber(forestClient.getClientNumber()) + .doingBusinessAsName(message + .getHeaders() + .get(ApplicationConstant.FOREST_CLIENT_NAME, + String.class) + ) + .revision(1L) + .createdBy(ApplicationConstant.PROCESSOR_USER_NAME) + .createdAt(LocalDateTime.now()) + .updatedBy(ApplicationConstant.PROCESSOR_USER_NAME) + .updatedAt(LocalDateTime.now()) + .addOrgUnit(ApplicationConstant.ORG_UNIT) + .updateOrgUnit(ApplicationConstant.ORG_UNIT) + .build() + ) + .flatMap(doingBusinessAs -> + legacyR2dbcEntityTemplate + .insert(ClientDoingBusinessAsEntity.class) + .using(doingBusinessAs) + ); + } + + private boolean isRegisteredSoleProprietorship(ForestClientEntity forestClient) { + return forestClient + .getClientTypeCode() + .equalsIgnoreCase("I") + && + StringUtils.equalsIgnoreCase( + forestClient + .getClientIdTypeCode(), + "OTHR" + ); + } + private Mono toForestClientLocationEntity( long index, SubmissionLocationEntity submissionLocation @@ -506,6 +635,16 @@ private Mono toForestClientLocationEntity( ); } + private String getClientNumber(Message message) { + return ProcessorUtil + .readHeader( + message, + ApplicationConstant.FOREST_CLIENT_NUMBER, + String.class + ) + .orElse(StringUtils.EMPTY); + } + private ForestClientContactEntity toForestClientContactEntity( SubmissionContactEntity submissionContact ) { @@ -514,8 +653,10 @@ private ForestClientContactEntity toForestClientContactEntity( .contactCode(submissionContact.getContactTypeCode()) .contactName(String.format("%s %s", submissionContact.getFirstName(), submissionContact.getLastName()).toUpperCase()) - .businessPhone(RegExUtils.replaceAll(submissionContact.getBusinessPhoneNumber(), "\\D", - StringUtils.EMPTY)) + .businessPhone( + RegExUtils.replaceAll(submissionContact.getBusinessPhoneNumber(), "\\D", + StringUtils.EMPTY) + ) .emailAddress(submissionContact.getEmailAddress()) .createdAt(LocalDateTime.now()) .updatedAt(LocalDateTime.now()) @@ -527,26 +668,4 @@ private ForestClientContactEntity toForestClientContactEntity( .build(); } - private String getUser(Message message, String headerName) { - return ProcessorUtil - .readHeader( - message, - headerName, - String.class - ) - .orElse(ApplicationConstant.PROCESSOR_USER_NAME); - } - - private String getClientNumber(Message message) { - return ProcessorUtil - .readHeader( - message, - ApplicationConstant.FOREST_CLIENT_NUMBER, - String.class - ) - .orElse(StringUtils.EMPTY); - } - } - - diff --git a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyClientPersistenceService.java b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyClientPersistenceService.java new file mode 100644 index 0000000000..52fc087e57 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyClientPersistenceService.java @@ -0,0 +1,122 @@ + +package ca.bc.gov.app.service.legacy; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import ca.bc.gov.app.util.ProcessorUtil; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.r2dbc.core.R2dbcEntityOperations; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + + +/** + * This class is responsible for persisting the submission into the legacy database. + */ +@Service +@Slf4j +public class LegacyClientPersistenceService extends LegacyAbstractPersistenceService { + + public LegacyClientPersistenceService( + SubmissionDetailRepository submissionDetailRepository, + SubmissionRepository submissionRepository, + SubmissionLocationRepository locationRepository, + SubmissionContactRepository contactRepository, + SubmissionLocationContactRepository locationContactRepository, + R2dbcEntityOperations legacyR2dbcEntityTemplate, + CountryCodeRepository countryCodeRepository, + ClientDoingBusinessAsRepository doingBusinessAsRepository + ) { + super( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository + ); + } + + /** + * This method is responsible for filtering the submission based on the type. + * + * @param clientTypeCode - the client type code. + * @return - true if the type is not RSP, USP or I, otherwise false. + */ + @Override + boolean filterByType(String clientTypeCode) { + return !List.of("RSP", "USP", "I").contains(clientTypeCode); + } + + @Override + String getNextChannel() { + return ApplicationConstant.SUBMISSION_LEGACY_OTHER_CHANNEL; + } + + @ServiceActivator( + inputChannel = ApplicationConstant.SUBMISSION_LEGACY_OTHER_CHANNEL, + outputChannel = ApplicationConstant.SUBMISSION_LEGACY_CLIENT_PERSIST_CHANNEL, + async = "true" + ) + @Override + public Mono> generateForestClient(Message message) { + return + getSubmissionDetailRepository() + .findBySubmissionId( + message + .getHeaders() + .get(ApplicationConstant.SUBMISSION_ID, Integer.class) + ) + .map(submissionDetail -> + getBaseForestClient( + getUser(message, ApplicationConstant.CREATED_BY), + getUser(message, ApplicationConstant.UPDATED_BY) + ) + .withClientComment( + "Client details acquired from BC Registry " + + submissionDetail.getIncorporationNumber() + ) + .withClientName(submissionDetail.getOrganizationName().toUpperCase()) + .withClientTypeCode(submissionDetail.getClientTypeCode()) + .withRegistryCompanyTypeCode( + ProcessorUtil.extractLetters(submissionDetail.getIncorporationNumber()) + ) + .withCorpRegnNmbr( + ProcessorUtil.extractNumbers(submissionDetail.getIncorporationNumber()) + ) + .withClientNumber(message.getHeaders() + .get(ApplicationConstant.FOREST_CLIENT_NUMBER, String.class)) + ) + .map(forestClient -> + MessageBuilder + .withPayload(forestClient) + .copyHeaders(message.getHeaders()) + .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, + forestClient.getClientName() + ) + .setHeader(ApplicationConstant.INCORPORATION_NUMBER, + String.join(StringUtils.EMPTY, + forestClient.getRegistryCompanyTypeCode(), + forestClient.getCorpRegnNmbr() + ) + ) + .build() + ); + + } + +} diff --git a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyIndividualPersistenceService.java b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyIndividualPersistenceService.java new file mode 100644 index 0000000000..77e51f9560 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyIndividualPersistenceService.java @@ -0,0 +1,115 @@ + +package ca.bc.gov.app.service.legacy; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import ca.bc.gov.app.util.ProcessorUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.r2dbc.core.R2dbcEntityOperations; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + + +/** + * This class is responsible for persisting the submission of individuals into the legacy database. + */ +@Service +@Slf4j +public class LegacyIndividualPersistenceService extends LegacyAbstractPersistenceService { + + public LegacyIndividualPersistenceService( + SubmissionDetailRepository submissionDetailRepository, + SubmissionRepository submissionRepository, + SubmissionLocationRepository locationRepository, + SubmissionContactRepository contactRepository, + SubmissionLocationContactRepository locationContactRepository, + R2dbcEntityOperations legacyR2dbcEntityTemplate, + CountryCodeRepository countryCodeRepository, + ClientDoingBusinessAsRepository doingBusinessAsRepository + ) { + super( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository + ); + } + + /** + * This method is responsible for filtering the submission based on the type. + * @param clientTypeCode - the client type code. + * @return - true if the type is I, otherwise false. + */ + @Override + boolean filterByType(String clientTypeCode) { + return StringUtils.equalsIgnoreCase(clientTypeCode,"I"); + } + + @Override + String getNextChannel() { + return ApplicationConstant.SUBMISSION_LEGACY_INDIVIDUAL_CHANNEL; + } + + /** + * Generate the individual to be persisted into forest client database. + */ + @ServiceActivator( + inputChannel = ApplicationConstant.SUBMISSION_LEGACY_INDIVIDUAL_CHANNEL, + outputChannel = ApplicationConstant.SUBMISSION_LEGACY_CLIENT_PERSIST_CHANNEL, + async = "true" + ) + @Override + public Mono> generateForestClient(Message message) { + return + getSubmissionDetailRepository() + .findBySubmissionId( + message + .getHeaders() + .get(ApplicationConstant.SUBMISSION_ID, Integer.class) + ) + .map(detailEntity -> + getBaseForestClient( + getUser(message, ApplicationConstant.CREATED_BY), + getUser(message, ApplicationConstant.UPDATED_BY) + ) + .withLegalFirstName(ProcessorUtil.splitName(detailEntity.getOrganizationName())[1].toUpperCase()) + .withClientName(ProcessorUtil.splitName(detailEntity.getOrganizationName())[0].toUpperCase()) + .withClientComment("Individual with data acquired from BC Services Card") + .withClientTypeCode("I") + .withClientNumber(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER, String.class)) + ) + .map(forestClient -> + MessageBuilder + .withPayload(forestClient) + .copyHeaders(message.getHeaders()) + .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, + String.join(" ", + forestClient.getLegalFirstName(), + forestClient.getClientName() + ) + ) + .setHeader(ApplicationConstant.INCORPORATION_NUMBER, + "not applicable") + .build() + ); + } + + +} + + diff --git a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyLoadingService.java b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyLoadingService.java index 940fd53410..3271d46a75 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyLoadingService.java +++ b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyLoadingService.java @@ -56,6 +56,7 @@ public Mono>> matchCheck( public Mono> validateSubmission(SubmissionInformationDto message) { return Flux .fromIterable(matchers) + .filter(matcher -> matcher.enabled(message)) .doOnNext(matcher -> log.info("Running {}", matcher.name())) //If matcher returns empty, all good, if not, it is a problem .flatMap(matcher -> matcher.matches(message)) diff --git a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyRegisteredSPPersistenceService.java b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyRegisteredSPPersistenceService.java new file mode 100644 index 0000000000..213ffda263 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyRegisteredSPPersistenceService.java @@ -0,0 +1,184 @@ + +package ca.bc.gov.app.service.legacy; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.dto.bcregistry.BcRegistryPartyDto; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import ca.bc.gov.app.service.bcregistry.BcRegistryService; +import ca.bc.gov.app.util.ProcessorUtil; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.r2dbc.core.R2dbcEntityOperations; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; +import reactor.util.function.Tuple2; + + +/** + * This class is responsible for persisting the submission of registered sole proprietorship into + * the legacy database. + */ +@Service +@Slf4j +public class LegacyRegisteredSPPersistenceService extends LegacyAbstractPersistenceService { + + private final BcRegistryService bcRegistryService; + + public LegacyRegisteredSPPersistenceService( + SubmissionDetailRepository submissionDetailRepository, + SubmissionRepository submissionRepository, + SubmissionLocationRepository locationRepository, + SubmissionContactRepository contactRepository, + SubmissionLocationContactRepository locationContactRepository, + R2dbcEntityOperations legacyR2dbcEntityTemplate, + CountryCodeRepository countryCodeRepository, + ClientDoingBusinessAsRepository doingBusinessAsRepository, + BcRegistryService bcRegistryService + ) { + super( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository + ); + this.bcRegistryService = bcRegistryService; + } + + /** + * This method is responsible for filtering the submission based on the type. + * + * @param clientTypeCode - the client type code. + * @return - true if the type is RSP, otherwise false. + */ + @Override + boolean filterByType(String clientTypeCode) { + return StringUtils.equalsIgnoreCase(clientTypeCode, "RSP"); + } + + @Override + String getNextChannel() { + return ApplicationConstant.SUBMISSION_LEGACY_RSP_CHANNEL; + } + + @ServiceActivator( + inputChannel = ApplicationConstant.SUBMISSION_LEGACY_RSP_CHANNEL, + outputChannel = ApplicationConstant.SUBMISSION_LEGACY_CLIENT_PERSIST_CHANNEL, + async = "true" + ) + @Override + public Mono> generateForestClient(Message message) { + + return + getSubmissionDetailRepository() + .findBySubmissionId( + message + .getHeaders() + .get(ApplicationConstant.SUBMISSION_ID, Integer.class) + ) + .map(submissionDetail -> + getBaseForestClient( + getUser(message, ApplicationConstant.CREATED_BY), + getUser(message, ApplicationConstant.UPDATED_BY) + ) + .withClientIdentification(submissionDetail.getIncorporationNumber()) + .withClientComment( + String.join(" ", + "Sole proprietorship registered on BC Registry with number", + submissionDetail.getIncorporationNumber(), + "and company name", + submissionDetail.getOrganizationName().toUpperCase() + ) + ) + .withRegistryCompanyTypeCode(ProcessorUtil.extractLetters( + submissionDetail.getIncorporationNumber())) + .withCorpRegnNmbr(ProcessorUtil.extractNumbers( + submissionDetail.getIncorporationNumber())) + ) + //Load the details to set the remaining fields + .flatMap(forestClient -> + bcRegistryService + .requestDocumentData(forestClient.getClientIdentification()) + // Should only be one + .next() + // Get the proprietor or empty if none + .flatMap(document -> + Mono.justOrEmpty( + Optional.ofNullable( + document.getProprietor() + ) + ) + ) + .map(BcRegistryPartyDto::officer) + .map(contact -> + new String[]{ + contact.firstName().toUpperCase(), + contact.lastName().toUpperCase() + }) + .switchIfEmpty( + //In case of negative results from BC Registry (rare) + // load from contacts as a fallback + getContactRepository() + //Get all contacts for the submission + .findBySubmissionId( + message + .getHeaders() + .get(ApplicationConstant.SUBMISSION_ID, Integer.class) + ) + //Handle as a flux with the index + .index() + //Only deal with the first 2 + .filter(index -> index.getT1() < 2) + .map(Tuple2::getT2) + .map(contact -> new String[]{ + contact.getFirstName().toUpperCase(), + contact.getLastName().toUpperCase() + }) + //Grab the last, as it should cover the case of the second being removed + .last() + ) + .map(contact -> + forestClient + .withLegalFirstName(contact[0]) + .withClientName(contact[1]) + .withClientTypeCode("I") + .withClientIdTypeCode("OTHR") + .withClientNumber(message.getHeaders() + .get(ApplicationConstant.FOREST_CLIENT_NUMBER, String.class)) + ) + ) + .map(forestClient -> + MessageBuilder + .withPayload(forestClient) + .copyHeaders(message.getHeaders()) + .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, + forestClient + .getClientComment() + .split("and company name ")[1] + ) + .setHeader(ApplicationConstant.INCORPORATION_NUMBER, + String.join(StringUtils.EMPTY, + forestClient.getRegistryCompanyTypeCode(), + forestClient.getCorpRegnNmbr() + ) + ) + .build() + ); + + } + +} diff --git a/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyUnregisteredSPPersistenceService.java b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyUnregisteredSPPersistenceService.java new file mode 100644 index 0000000000..a4d24d4714 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/legacy/LegacyUnregisteredSPPersistenceService.java @@ -0,0 +1,129 @@ + +package ca.bc.gov.app.service.legacy; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import ca.bc.gov.app.util.ProcessorUtil; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.r2dbc.core.R2dbcEntityOperations; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + + +/** + * This class is responsible for persisting the submission of unregistered sole proprietorship + * into the legacy database. + */ +@Service +@Slf4j +public class LegacyUnregisteredSPPersistenceService extends LegacyAbstractPersistenceService { + + public LegacyUnregisteredSPPersistenceService( + SubmissionDetailRepository submissionDetailRepository, + SubmissionRepository submissionRepository, + SubmissionLocationRepository locationRepository, + SubmissionContactRepository contactRepository, + SubmissionLocationContactRepository locationContactRepository, + R2dbcEntityOperations legacyR2dbcEntityTemplate, + CountryCodeRepository countryCodeRepository, + ClientDoingBusinessAsRepository doingBusinessAsRepository + ) { + super( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository + ); + } + + /** + * This method is responsible for filtering the submission based on the type. + * @param clientTypeCode - the client type code. + * @return - true if the type is USP, otherwise false. + */ + @Override + boolean filterByType(String clientTypeCode) { + return StringUtils.equalsIgnoreCase(clientTypeCode,"USP"); + } + + @Override + String getNextChannel() { + return ApplicationConstant.SUBMISSION_LEGACY_USP_CHANNEL; + } + + /** + * This method is responsible for generating the forest client for unregistered sole proprietorship. + * @param message - the message containing the submission id. + * @return - the forest client. + */ + @ServiceActivator( + inputChannel = ApplicationConstant.SUBMISSION_LEGACY_USP_CHANNEL, + outputChannel = ApplicationConstant.SUBMISSION_LEGACY_CLIENT_PERSIST_CHANNEL, + async = "true" + ) + @Override + public Mono> generateForestClient(Message message) { + return + getSubmissionDetailRepository() + .findBySubmissionId( + message + .getHeaders() + .get(ApplicationConstant.SUBMISSION_ID, Integer.class) + ) + .map(detail -> + getBaseForestClient( + getUser(message, ApplicationConstant.CREATED_BY), + getUser(message, ApplicationConstant.UPDATED_BY) + ) + .withLegalFirstName(ProcessorUtil.splitName(detail.getOrganizationName())[1].toUpperCase()) + .withClientName(ProcessorUtil.splitName(detail.getOrganizationName())[0].toUpperCase()) + .withLegalMiddleName(ProcessorUtil.splitName(detail.getOrganizationName())[2].toUpperCase()) + .withClientComment( + "Sole proprietorship with data acquired from BC Business eID") + .withClientTypeCode("I") + .withClientNumber(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER, String.class)) + ) + .doOnNext(forestClient -> + log.info("generated forest client for USP {} {}", + forestClient.getClientNumber(), + message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER, String.class) + ) + ) + .map(forestClient -> + MessageBuilder + .withPayload(forestClient) + .copyHeaders(message.getHeaders()) + .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, + Stream.of( + forestClient.getLegalFirstName(), + forestClient.getLegalMiddleName(), + forestClient.getClientName() + ) + .filter(StringUtils::isNotBlank) + .collect(Collectors.joining(" ")) + ) + .setHeader(ApplicationConstant.INCORPORATION_NUMBER, + "not applicable") + .build() + ); + } + + +} diff --git a/processor/src/main/java/ca/bc/gov/app/service/processor/DoingBusinessAsProcessorMatcher.java b/processor/src/main/java/ca/bc/gov/app/service/processor/DoingBusinessAsProcessorMatcher.java new file mode 100644 index 0000000000..2cf6c66c0c --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/processor/DoingBusinessAsProcessorMatcher.java @@ -0,0 +1,57 @@ +package ca.bc.gov.app.service.processor; + +import static java.util.function.Predicate.not; + +import ca.bc.gov.app.dto.MatcherResult; +import ca.bc.gov.app.dto.SubmissionInformationDto; +import ca.bc.gov.app.entity.legacy.ClientDoingBusinessAsEntity; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +@Slf4j +public class DoingBusinessAsProcessorMatcher implements ProcessorMatcher { + + private final ClientDoingBusinessAsRepository doingBusinessAsRepository; + + @Override + public boolean enabled(SubmissionInformationDto submission) { + return "RSP".equalsIgnoreCase(submission.clientType()); + } + + @Override + public String name() { + return "Doing Business As Fuzzy Matcher"; + } + + @Override + public Mono matches(SubmissionInformationDto submission) { + + log.info("{} :: Validating {}", name(), submission.corporationName()); + + return + matchBy(submission.corporationName()) + .map(ClientDoingBusinessAsEntity::getClientNumber) + .collectList() + .filter(not(List::isEmpty)) + .map(values -> + new MatcherResult("corporationName", String.join(",", values)) + ); + } + + private Flux matchBy(String companyName) { + return + doingBusinessAsRepository + .matchBy(companyName) + .doOnNext(entity -> log.info("Found a match {}", entity)); + + } + +} diff --git a/processor/src/main/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcher.java b/processor/src/main/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcher.java index 4c4d644915..815e0d1e2c 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcher.java +++ b/processor/src/main/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcher.java @@ -10,6 +10,12 @@ @Component @Slf4j public class GoodStandingProcessorMatcher implements ProcessorMatcher { + + @Override + public boolean enabled(SubmissionInformationDto submission) { + return true; + } + @Override public String name() { return "Good Standing Matcher"; diff --git a/processor/src/main/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcher.java b/processor/src/main/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcher.java index 72810bee1a..d9a7c9f5c6 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcher.java +++ b/processor/src/main/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcher.java @@ -9,6 +9,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @@ -18,6 +19,11 @@ public class IncorporationNumberProcessorMatcher implements ProcessorMatcher { private final ForestClientRepository forestClientRepository; + @Override + public boolean enabled(SubmissionInformationDto submission) { + return StringUtils.isNotBlank(submission.incorporationNumber()); + } + @Override public String name() { return "Incorporation Number Matcher"; diff --git a/processor/src/main/java/ca/bc/gov/app/service/processor/IndividualProcessorMatcher.java b/processor/src/main/java/ca/bc/gov/app/service/processor/IndividualProcessorMatcher.java new file mode 100644 index 0000000000..fabb1c11e4 --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/processor/IndividualProcessorMatcher.java @@ -0,0 +1,53 @@ +package ca.bc.gov.app.service.processor; + +import static java.util.function.Predicate.not; + +import ca.bc.gov.app.dto.MatcherResult; +import ca.bc.gov.app.dto.SubmissionInformationDto; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.legacy.ForestClientRepository; +import ca.bc.gov.app.util.ProcessorUtil; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +@Slf4j +public class IndividualProcessorMatcher implements ProcessorMatcher { + + private final ForestClientRepository forestClientRepository; + + @Override + public boolean enabled(SubmissionInformationDto submission) { + return "I".equalsIgnoreCase(submission.clientType()); + } + + @Override + public String name() { + return "Individual Matcher"; + } + + @Override + public Mono matches(SubmissionInformationDto submission) { + + log.info("{} :: Validating {}", name(), submission.corporationName()); + + return + forestClientRepository + .findByIndividual( + ProcessorUtil.splitName(submission.corporationName())[1], + ProcessorUtil.splitName(submission.corporationName())[0], + submission.dateOfBirth()!= null ? submission.dateOfBirth().atStartOfDay() : null + ) + .map(ForestClientEntity::getClientNumber) + .collectList() + .filter(not(List::isEmpty)) + .map(values -> + new MatcherResult("corporationName", String.join(",", values)) + ); + } + +} diff --git a/processor/src/main/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcher.java b/processor/src/main/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcher.java index aa6882ef21..871d75b55d 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcher.java +++ b/processor/src/main/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcher.java @@ -20,23 +20,28 @@ public class LegalNameProcessorMatcher implements ProcessorMatcher { private final ForestClientRepository forestClientRepository; + @Override + public boolean enabled(SubmissionInformationDto submission) { + return List.of("I", "USP", "RSP").contains(submission.clientType()); + } + @Override public String name() { - return "Legal Name Matcher"; + return "Legal Name Fuzzy Matcher"; } @Override public Mono matches(SubmissionInformationDto submission) { - log.info("{} :: Validating {}",name(),submission.legalName()); + log.info("{} :: Validating {}", name(), submission.corporationName()); return - matchBy(submission.legalName()) + matchBy(submission.corporationName()) .map(ForestClientEntity::getClientNumber) .collectList() .filter(not(List::isEmpty)) .map(values -> - new MatcherResult("legalName", String.join(",", values)) + new MatcherResult("corporationName", String.join(",", values)) ); } diff --git a/processor/src/main/java/ca/bc/gov/app/service/processor/ProcessorMatcher.java b/processor/src/main/java/ca/bc/gov/app/service/processor/ProcessorMatcher.java index 9988605caa..1c892dcc63 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/processor/ProcessorMatcher.java +++ b/processor/src/main/java/ca/bc/gov/app/service/processor/ProcessorMatcher.java @@ -6,6 +6,7 @@ public interface ProcessorMatcher { + boolean enabled(SubmissionInformationDto submission); String name(); Mono matches(SubmissionInformationDto submission); diff --git a/processor/src/main/java/ca/bc/gov/app/service/processor/SoleProprietorProcessorMatcher.java b/processor/src/main/java/ca/bc/gov/app/service/processor/SoleProprietorProcessorMatcher.java new file mode 100644 index 0000000000..2aa05d4d8c --- /dev/null +++ b/processor/src/main/java/ca/bc/gov/app/service/processor/SoleProprietorProcessorMatcher.java @@ -0,0 +1,53 @@ +package ca.bc.gov.app.service.processor; + +import static java.util.function.Predicate.not; + +import ca.bc.gov.app.dto.MatcherResult; +import ca.bc.gov.app.dto.SubmissionInformationDto; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.legacy.ForestClientRepository; +import ca.bc.gov.app.util.ProcessorUtil; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SoleProprietorProcessorMatcher implements ProcessorMatcher { + + private final ForestClientRepository forestClientRepository; + + @Override + public boolean enabled(SubmissionInformationDto submission) { + return List.of("USP", "RSP").contains(submission.clientType()); + } + + @Override + public String name() { + return "Sole Proprietor Matcher"; + } + + @Override + public Mono matches(SubmissionInformationDto submission) { + + log.info("{} :: Validating {}", name(), submission.corporationName()); + + return + forestClientRepository + .findByIndividual( + ProcessorUtil.splitName(submission.corporationName())[1], + ProcessorUtil.splitName(submission.corporationName())[0], + submission.dateOfBirth()!= null ? submission.dateOfBirth().atStartOfDay() : null + ) + .doOnNext(entity -> log.info("Found a match {}", entity)) + .map(ForestClientEntity::getClientNumber) + .collectList() + .filter(not(List::isEmpty)) + .map(values -> + new MatcherResult("corporationName", String.join(",", values)) + ); + } +} diff --git a/processor/src/main/java/ca/bc/gov/app/util/ProcessorUtil.java b/processor/src/main/java/ca/bc/gov/app/util/ProcessorUtil.java index bc30c14f32..a212a2da3e 100644 --- a/processor/src/main/java/ca/bc/gov/app/util/ProcessorUtil.java +++ b/processor/src/main/java/ca/bc/gov/app/util/ProcessorUtil.java @@ -30,6 +30,46 @@ public static String extractNumbers(String input) { return getStringFromPattern(input, pattern); } + public static String[] splitName(String input) { + //If is null or empty return empty array + if (StringUtils.isBlank(input)) { + return new String[]{StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY}; + } + + //If contains comma, split by comma and join back together with space, else just use the input + String cleanedInput = input.contains(",") ? + String.join(" ", + input + //Remove the commas + .replace(",", StringUtils.EMPTY) + //Remove the first word + .replace(input.split(",")[0], StringUtils.EMPTY), + //Add the first word to the end + input.split(",")[0].trim() + ).trim() : + input; + + //Split by space + String[] words = cleanedInput.replace(",", StringUtils.EMPTY).split("\\s+"); + + //Keeping the OG switch statement as native has some issues with new switch + switch (words.length) { + case 1: + //Return the word for first 2 elements and empty string for the last + return new String[]{words[0].trim(), words[0].trim(), StringUtils.EMPTY}; + case 2: + //Return the second word for first, first word for second and empty string for the last + return new String[]{words[1].trim(), words[0].trim(), StringUtils.EMPTY}; + default: + //Return the last word for first, first word for second and the rest for the last + return new String[]{ + words[words.length - 1].trim(), + words[0].trim(), + StringUtils.join(words, ' ', 1, words.length - 1).trim() + }; + } + } + private static String getStringFromPattern(String input, Pattern pattern) { Matcher matcher = pattern.matcher(input); if (matcher.find()) { diff --git a/processor/src/main/resources/application.yml b/processor/src/main/resources/application.yml index f0c2d9107f..d414535509 100644 --- a/processor/src/main/resources/application.yml +++ b/processor/src/main/resources/application.yml @@ -56,3 +56,7 @@ ca: username: ${POSTGRESQL_USER:user} password: ${POSTGRESQL_PASSWORD:passwd} url: r2dbc:postgresql://${ca.bc.gov.nrs.postgres.host}/${ca.bc.gov.nrs.postgres.database} + bcregistry: + uri: ${BCREGISTRY_URI:https://bcregistry-sandbox.apigee.net} + apiKey: ${BCREGISTRY_KEY:123456} + accountId: ${BCREGISTRY_ACCOUNT:123456} diff --git a/processor/src/test/java/ca/bc/gov/app/TestConstants.java b/processor/src/test/java/ca/bc/gov/app/TestConstants.java index 497f55c2e5..910b9d8957 100644 --- a/processor/src/test/java/ca/bc/gov/app/TestConstants.java +++ b/processor/src/test/java/ca/bc/gov/app/TestConstants.java @@ -2,8 +2,15 @@ import ca.bc.gov.app.dto.EmailRequestDto; import ca.bc.gov.app.dto.SubmissionInformationDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryOfficerDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryPartyDto; +import ca.bc.gov.app.dto.bcregistry.BcRegistryRoleDto; import ca.bc.gov.app.entity.client.SubmissionContactEntity; import ca.bc.gov.app.entity.client.SubmissionDetailEntity; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import java.time.LocalDate; +import java.util.List; import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -18,12 +25,12 @@ public class TestConstants { .incorporationNumber("00000000") .organizationName("TEST") .businessTypeCode("T") - .clientTypeCode("T") + .clientTypeCode("C") .goodStandingInd("Y") .build(); public static final SubmissionInformationDto SUBMISSION_INFORMATION = - new SubmissionInformationDto("TEST", "00000000", "Y"); + new SubmissionInformationDto("TEST", null, "00000000", "Y", "C"); public static final EmailRequestDto EMAIL_REQUEST = new EmailRequestDto( "ABC1234", @@ -43,23 +50,23 @@ public class TestConstants { ); public static final String EMAIL_REQUEST_JSON = """ - { - "incorporation": "ABC1234", - "name": "Test Corp", - "userId": "testuserid", - "userName": "Test User", - "email": "testuser@mail.tst", - "templateName": "test", - "subject": "Processor Tests", - "variables": { - "name": "Test User", - "business": { - "name": "Test Corp", - "identifier": "ABC1234" - } + { + "incorporation": "ABC1234", + "name": "Test Corp", + "userId": "testuserid", + "userName": "Test User", + "email": "testuser@mail.tst", + "templateName": "test", + "subject": "Processor Tests", + "variables": { + "name": "Test User", + "business": { + "name": "Test Corp", + "identifier": "ABC1234" } } - """; + } + """; public static final SubmissionContactEntity SUBMISSION_CONTACT = SubmissionContactEntity .builder() @@ -82,6 +89,72 @@ public class TestConstants { "clientNumber", "00001000" ) ) + ); + public static final BcRegistryDocumentDto BCREG_DOC_DATA = + new BcRegistryDocumentDto( + List.of( + new BcRegistryPartyDto( + new BcRegistryOfficerDto( + "baxterj@baxter.com", + "James", + "Baxter", + "W", + "Director" + ), + List.of( + new BcRegistryRoleDto( + LocalDate.of(2005, 7, 27), + null, + "Proprietor" + ) + ) + ) + ) ); + public static final String BCREG_DOC_REQ_RES = """ + { + "businessIdentifier": "AA0000001", + "documents": [ + { + "documentKey": "aa0a00a0a", + "documentType": "BUSINESS_SUMMARY_FILING_HISTORY", + "id": 18315 + } + ] + }"""; + + public static final String BCREG__RES1 = """ + { + "parties": [ + { + "officer": { + "email": "", + "firstName": "JAMES", + "lastName": "BAXTER", + "middleInitial": "G.", + "partyType": "person" + }, + "roles": [ + { + "appointmentDate": "1992-09-11", + "cessationDate": null, + "roleType": "Proprietor" + } + ] + } + ] + }"""; + + public static final String BCREG_RES2 = """ + { + "errorMessage": "API backend third party service error.", + "rootCause": "message:ResourceErrorCodes.NOT_FOUND_ERR: no Document found for 7QbI0M6uBxx. " + }"""; + + public static final ForestClientEntity CLIENT_ENTITY = ForestClientEntity.builder() + .clientNumber("00001000") + .clientTypeCode("C") + .build(); + } diff --git a/processor/src/test/java/ca/bc/gov/app/extensions/WiremockLogNotifier.java b/processor/src/test/java/ca/bc/gov/app/extensions/WiremockLogNotifier.java new file mode 100644 index 0000000000..26e916b271 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/extensions/WiremockLogNotifier.java @@ -0,0 +1,23 @@ +package ca.bc.gov.app.extensions; + +import com.github.tomakehurst.wiremock.common.Notifier; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class WiremockLogNotifier implements Notifier { + + @Override + public void info(String message) { + log.info(message); + } + + @Override + public void error(String message) { + log.error(message); + } + + @Override + public void error(String message, Throwable t) { + log.error(message, t); + } +} diff --git a/processor/src/test/java/ca/bc/gov/app/service/bcregistry/BcRegistryServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/bcregistry/BcRegistryServiceTest.java new file mode 100644 index 0000000000..64e1696b74 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/bcregistry/BcRegistryServiceTest.java @@ -0,0 +1,114 @@ +package ca.bc.gov.app.service.bcregistry; + +import static ca.bc.gov.app.TestConstants.BCREG_DOC_REQ_RES; +import static ca.bc.gov.app.TestConstants.BCREG_RES2; +import static ca.bc.gov.app.TestConstants.BCREG__RES1; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import ca.bc.gov.app.dto.bcregistry.BcRegistryDocumentDto; +import ca.bc.gov.app.extensions.WiremockLogNotifier; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | BC Registry Service") +class BcRegistryServiceTest { + + @RegisterExtension + static WireMockExtension bcRegistryStub = WireMockExtension + .newInstance() + .options( + wireMockConfig() + .port(10040) + .notifier(new WiremockLogNotifier()) + .asynchronousResponseEnabled(true) + .stubRequestLoggingDisabled(false) + ) + .configureStaticDsl(true) + .build(); + + private final BcRegistryService service = new BcRegistryService( + WebClient + .builder() + .baseUrl("http://localhost:10040") + .build() + ); + + @ParameterizedTest + @MethodSource("documentData") + @DisplayName("should read document data") + void shouldReadDocumentData( + String clientNumber, + int requestStatus, + String requestBody, + int detailsStatus, + String detailsBody + ) { + + bcRegistryStub + .stubFor(post(urlPathEqualTo( + "/registry-search/api/v1/businesses/" + clientNumber + "/documents/requests")) + .willReturn( + status(requestStatus) + .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBody(requestBody) + ) + ); + + bcRegistryStub + .stubFor(get(urlPathEqualTo( + "/registry-search/api/v1/businesses/" + clientNumber + "/documents/aa0a00a0a" + ) + ) + .willReturn( + status(detailsStatus) + .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBody(detailsBody) + ) + ); + + StepVerifier.FirstStep test = + service + .requestDocumentData(clientNumber) + .as(StepVerifier::create); + + if (detailsStatus == 200 && requestStatus == 200) { + test + .assertNext(document -> assertTrue(document.getProprietor().isProprietor())) + .verifyComplete(); + }else{ + test + .expectError() + .verify(); + } + + + } + + private static Stream documentData() { + return Stream.of( + Arguments.of("CP0000001",200,BCREG_DOC_REQ_RES, 200, BCREG__RES1), + Arguments.of("CP0000002",200,BCREG_DOC_REQ_RES, 404, BCREG_RES2), + + Arguments.of("CP0000002",404,BCREG_RES2, 404, BCREG_RES2), + Arguments.of("CP0000002",401,BCREG_RES2, 404, BCREG_RES2), + Arguments.of("CP0000002",400,BCREG_RES2, 404, BCREG_RES2), + + Arguments.of("CP0000002",200,BCREG_DOC_REQ_RES, 401, BCREG_RES2), + Arguments.of("CP0000002",200,BCREG_DOC_REQ_RES, 400, BCREG_RES2) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/client/ClientSubmissionMailServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/client/ClientSubmissionMailServiceTest.java index 42ccdad4b4..ca7b9a2da7 100644 --- a/processor/src/test/java/ca/bc/gov/app/service/client/ClientSubmissionMailServiceTest.java +++ b/processor/src/test/java/ca/bc/gov/app/service/client/ClientSubmissionMailServiceTest.java @@ -9,7 +9,9 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.awaitility.Awaitility.await; +import ca.bc.gov.app.ApplicationConstant; import ca.bc.gov.app.TestConstants; +import ca.bc.gov.app.entity.client.SubmissionTypeCodeEnum; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import java.time.Duration; import org.junit.jupiter.api.BeforeAll; @@ -63,4 +65,29 @@ void shouldSendEmail() { }); } + @Test + @DisplayName("preview email on review") + void shouldPreventReviewMails() { + service.sendMail( + MessageBuilder + .withPayload(TestConstants.EMAIL_REQUEST) + .setHeader(ApplicationConstant.SUBMISSION_TYPE, SubmissionTypeCodeEnum.RNC) + .build() + ); + + await() + .alias("Email sent") + .atMost(Duration.ofSeconds(2)) + .untilAsserted(() -> { + wireMockExtension + .verify( + 0, + postRequestedFor(urlEqualTo("/ches/email")) + .withHeader("Content-Type", containing(MediaType.APPLICATION_JSON_VALUE)) + .withRequestBody(equalToJson(TestConstants.EMAIL_REQUEST_JSON) + ) + ); + }); + } + } \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/client/ClientSubmissionProcessingServiceIntegrationTest.java b/processor/src/test/java/ca/bc/gov/app/service/client/ClientSubmissionProcessingServiceIntegrationTest.java new file mode 100644 index 0000000000..af819d3be2 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/client/ClientSubmissionProcessingServiceIntegrationTest.java @@ -0,0 +1,233 @@ +package ca.bc.gov.app.service.client; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.dto.EmailRequestDto; +import ca.bc.gov.app.entity.client.SubmissionContactEntity; +import ca.bc.gov.app.entity.client.SubmissionDetailEntity; +import ca.bc.gov.app.entity.client.SubmissionEntity; +import ca.bc.gov.app.entity.client.SubmissionMatchDetailEntity; +import ca.bc.gov.app.entity.client.SubmissionStatusEnum; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionMatchDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import java.time.Duration; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Client Service") +class ClientSubmissionProcessingServiceIntegrationTest { + + private final SubmissionDetailRepository submissionDetailRepository = mock( + SubmissionDetailRepository.class); + private final SubmissionRepository submissionRepository = mock(SubmissionRepository.class); + private final SubmissionMatchDetailRepository submissionMatchDetailRepository = mock( + SubmissionMatchDetailRepository.class); + private final SubmissionContactRepository contactRepository = mock( + SubmissionContactRepository.class); + private final ClientSubmissionProcessingService service = new ClientSubmissionProcessingService( + submissionDetailRepository, + submissionRepository, + submissionMatchDetailRepository, + contactRepository + ); + + @ParameterizedTest + @MethodSource("submissionLoads") + @DisplayName("submission loads") + void shouldLoadSubmission( + Mono submission, + Mono submissionDetail, + Mono submissionMatchDetail, + Message expected + ) { + + when(submissionDetailRepository.findBySubmissionId(1)).thenReturn(submissionDetail); + when(submissionRepository.findById(1)).thenReturn(submission); + when(submissionMatchDetailRepository.findBySubmissionId(1)).thenReturn(submissionMatchDetail); + + StepVerifier.FirstStep> test = + service + .processSubmission( + MessageBuilder + .withPayload(1) + .build() + ) + .as(StepVerifier::create); + + if (expected != null) { + test.assertNext(message -> { + assertEquals(expected.getPayload(), message.getPayload()); + assertEquals( + expected.getHeaders().get(ApplicationConstant.SUBMISSION_ID), + message.getHeaders().get(ApplicationConstant.SUBMISSION_ID) + ); + assertEquals( + expected.getHeaders().get(ApplicationConstant.SUBMISSION_STATUS), + message.getHeaders().get(ApplicationConstant.SUBMISSION_STATUS) + ); + assertEquals( + expected.getHeaders().get(ApplicationConstant.SUBMISSION_CLIENTID), + message.getHeaders().get(ApplicationConstant.SUBMISSION_CLIENTID) + ); + assertEquals( + expected.getHeaders().get(ApplicationConstant.SUBMISSION_NAME), + message.getHeaders().get(ApplicationConstant.SUBMISSION_NAME) + ); + }) + .verifyComplete(); + } else { + test.verifyComplete(); + } + + + } + + @ParameterizedTest + @MethodSource("notification") + @DisplayName("notification is sent") + void shouldNotify( + SubmissionStatusEnum statusEnum, + Mono contact, + Mono detail, + Mono match, + Object response + ) { + + when(contactRepository.findFirstBySubmissionId(1)).thenReturn(contact); + when(submissionDetailRepository.findBySubmissionId(1)).thenReturn(detail); + when(submissionMatchDetailRepository.findBySubmissionId(1)).thenReturn(match); + + service + .notificationProcessing( + MessageBuilder + .withPayload(SubmissionMatchDetailEntity.builder().build()) + .setHeader(ApplicationConstant.SUBMISSION_STATUS, statusEnum) + .setHeader(ApplicationConstant.SUBMISSION_ID, 1) + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> + assertEquals(response, message.getPayload()) + ) + .verifyComplete(); + } + + @Test + @DisplayName("update match") + void shouldUpdateMatch() { + when(submissionMatchDetailRepository.findBySubmissionId(1)) + .thenReturn(Mono.just(SubmissionMatchDetailEntity.builder().build())); + service + .updateMatch(MessageBuilder.withPayload(1).build()); + + await() + .alias("Submission matches") + .atMost(Duration.ofSeconds(5)) + .untilAsserted(() -> { + verify(submissionMatchDetailRepository, atLeastOnce()) + .save(any(SubmissionMatchDetailEntity.class)); + verify(submissionMatchDetailRepository, atLeastOnce()) + .findBySubmissionId(eq(1)); + } + ); + } + + private static Stream submissionLoads() { + return + Stream.of( + Arguments.of( + Mono.empty(), + Mono.empty(), + Mono.empty(), + null + ), + Arguments.of( + Mono.just( + SubmissionEntity.builder().submissionStatus(SubmissionStatusEnum.N).build()), + Mono.just(SubmissionDetailEntity.builder().organizationName("TEST").build()), + Mono.empty(), + MessageBuilder + .withPayload(SubmissionMatchDetailEntity.builder().build()) + .setHeader(ApplicationConstant.SUBMISSION_ID, 1) + .setHeader(ApplicationConstant.SUBMISSION_STATUS, SubmissionStatusEnum.N) + .setHeader(ApplicationConstant.SUBMISSION_CLIENTID, StringUtils.EMPTY) + .setHeader(ApplicationConstant.SUBMISSION_NAME, "TEST") + .build() + + ), + Arguments.of( + Mono.just( + SubmissionEntity.builder().submissionStatus(SubmissionStatusEnum.N).build()), + Mono.just(SubmissionDetailEntity.builder().organizationName("TEST").build()), + Mono.just(SubmissionMatchDetailEntity.builder().build()), + MessageBuilder + .withPayload(SubmissionMatchDetailEntity.builder().build()) + .setHeader(ApplicationConstant.SUBMISSION_ID, 1) + .setHeader(ApplicationConstant.SUBMISSION_STATUS, SubmissionStatusEnum.N) + .setHeader(ApplicationConstant.SUBMISSION_CLIENTID, StringUtils.EMPTY) + .setHeader(ApplicationConstant.SUBMISSION_NAME, "TEST") + .build() + + ) + ); + } + + private static Stream notification() { + return + Stream + .of( + Arguments.of( + SubmissionStatusEnum.A, + Mono.empty(), + Mono.empty(), + Mono.empty(), + 1 + ), + Arguments.of( + SubmissionStatusEnum.R, + Mono.just(SubmissionContactEntity.builder().userId("uid1").firstName("Test") + .emailAddress("Mail") + .build()), + Mono.just(SubmissionDetailEntity.builder().organizationName("TEST") + .incorporationNumber("XX012345").build()), + Mono.just( + SubmissionMatchDetailEntity.builder().matchingMessage("Test").build()), + new EmailRequestDto( + "XX012345", + "TEST", + "uid1", + "Test", + "Mail", + "rejection", + "Failure", + Map.of( + "userName", "Test", + "name", "TEST", + "reason", "Test" + ) + ) + ) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyPersistenceServiceIntegrationTest.java b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceServiceIntegrationTest.java similarity index 91% rename from processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyPersistenceServiceIntegrationTest.java rename to processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceServiceIntegrationTest.java index 343bc3628f..92ddb9cf56 100644 --- a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyPersistenceServiceIntegrationTest.java +++ b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceServiceIntegrationTest.java @@ -12,10 +12,10 @@ import reactor.test.StepVerifier; @DisplayName("Integration Test | Legacy Persistence Service") -class LegacyPersistenceServiceIntegrationTest extends AbstractTestContainer { +class LegacyAbstractPersistenceServiceIntegrationTest extends AbstractTestContainer { @Autowired - private LegacyPersistenceService service; + private LegacyClientPersistenceService service; @Test @DisplayName("load submission with id 2") diff --git a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyPersistenceServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceServiceTest.java similarity index 77% rename from processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyPersistenceServiceTest.java rename to processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceServiceTest.java index 2eb44272dc..1212ff208d 100644 --- a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyPersistenceServiceTest.java +++ b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyAbstractPersistenceServiceTest.java @@ -8,11 +8,13 @@ import static org.mockito.Mockito.when; import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.TestConstants; import ca.bc.gov.app.entity.client.CountryCodeEntity; import ca.bc.gov.app.entity.client.SubmissionContactEntity; import ca.bc.gov.app.entity.client.SubmissionDetailEntity; import ca.bc.gov.app.entity.client.SubmissionLocationContactEntity; import ca.bc.gov.app.entity.client.SubmissionLocationEntity; +import ca.bc.gov.app.entity.legacy.ClientDoingBusinessAsEntity; import ca.bc.gov.app.entity.legacy.ForestClientContactEntity; import ca.bc.gov.app.entity.legacy.ForestClientEntity; import ca.bc.gov.app.entity.legacy.ForestClientLocationEntity; @@ -22,10 +24,15 @@ import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; import ca.bc.gov.app.repository.client.SubmissionLocationRepository; import ca.bc.gov.app.repository.client.SubmissionRepository; -import ca.bc.gov.app.repository.legacy.ForestClientRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; import org.springframework.data.r2dbc.core.ReactiveInsertOperation.ReactiveInsert; import org.springframework.integration.support.MessageBuilder; @@ -34,31 +41,31 @@ import reactor.test.StepVerifier; @DisplayName("Unit Test | Legacy Persistence Service") -class LegacyPersistenceServiceTest { +class LegacyAbstractPersistenceServiceTest { private final SubmissionDetailRepository submissionDetailRepository = mock( SubmissionDetailRepository.class); private final SubmissionRepository submissionRepository = mock(SubmissionRepository.class); private final SubmissionLocationRepository locationRepository = mock( SubmissionLocationRepository.class); - private final ForestClientRepository forestClientRepository = mock(ForestClientRepository.class); - private final SubmissionContactRepository contactRepository = mock( SubmissionContactRepository.class); private final SubmissionLocationContactRepository locationContactRepository = mock( SubmissionLocationContactRepository.class); private final R2dbcEntityTemplate legacyR2dbcEntityTemplate = mock(R2dbcEntityTemplate.class); - private final CountryCodeRepository countryCodeRepository = mock(CountryCodeRepository.class); + private final ClientDoingBusinessAsRepository doingBusinessAsRepository = mock( + ClientDoingBusinessAsRepository.class); - private final LegacyPersistenceService service = new LegacyPersistenceService( + private final LegacyClientPersistenceService service = new LegacyClientPersistenceService( submissionDetailRepository, submissionRepository, locationRepository, contactRepository, locationContactRepository, legacyR2dbcEntityTemplate, - countryCodeRepository + countryCodeRepository, + doingBusinessAsRepository ); @BeforeEach @@ -67,93 +74,6 @@ void beforeEach() { .thenReturn(Flux.just(new CountryCodeEntity("CA", "Canada"))); } - @Test - @DisplayName("create forest client") - void shouldCreateForestClient() { - ReactiveInsert reactiveInsert = mock(ReactiveInsert.class); - - SubmissionDetailEntity entity = SubmissionDetailEntity.builder() - .submissionId(2) - .submissionDetailId(2) - .organizationName("STAR DOT STAR VENTURES") - .incorporationNumber("FM0159297") - .businessTypeCode("R") - .clientTypeCode("P") - .goodStandingInd("Y") - .build(); - - when(submissionDetailRepository.findBySubmissionId(eq(2))) - .thenReturn(Mono.just(entity)); - when(forestClientRepository.save(any(ForestClientEntity.class))) - .thenReturn(Mono.just(ForestClientEntity.builder() - .clientNumber("00000000") - .build() - ) - ); - when(submissionDetailRepository.save(any(SubmissionDetailEntity.class))) - .thenReturn(Mono.just(entity)); - when(legacyR2dbcEntityTemplate.selectOne(any(), any())) - .thenReturn(Mono.just(ForestClientEntity.builder() - .clientNumber("00000000") - .build() - ) - ); - when(legacyR2dbcEntityTemplate.insert(eq(ForestClientEntity.class))) - .thenReturn(reactiveInsert); - when(reactiveInsert.using(any())) - .thenReturn(Mono.just(ForestClientEntity.builder() - .clientNumber("00000000") - .build() - ) - ); - - service - .createForestClient( - MessageBuilder - .withPayload(2) - .setHeader(ApplicationConstant.SUBMISSION_ID, 2) - .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) - .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) - .build() - ) - .as(StepVerifier::create) - .assertNext(message -> { - assertThat(message) - .isNotNull() - .hasFieldOrPropertyWithValue("payload", 2); - - assertThat(message.getHeaders().get(ApplicationConstant.SUBMISSION_ID)) - .isNotNull() - .isInstanceOf(Integer.class) - .isEqualTo(2); - - assertThat(message.getHeaders().get(ApplicationConstant.CREATED_BY)) - .isNotNull() - .isInstanceOf(String.class); - - assertThat(message.getHeaders().get(ApplicationConstant.UPDATED_BY)) - .isNotNull() - .isInstanceOf(String.class); - - assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NAME)) - .isNotNull() - .isInstanceOf(String.class) - .isEqualTo("STAR DOT STAR VENTURES"); - - assertThat(message.getHeaders().get(ApplicationConstant.INCORPORATION_NUMBER)) - .isNotNull() - .isInstanceOf(String.class) - .isEqualTo("FM0159297"); - - assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) - .isNotNull() - .isInstanceOf(String.class) - .isEqualTo("00000000"); - - }) - .verifyComplete(); - } - @Test @DisplayName("create locations") void shouldCreateLocations() { @@ -195,6 +115,7 @@ void shouldCreateLocations() { .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000000") .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, "STAR DOT STAR VENTURES") .setHeader(ApplicationConstant.INCORPORATION_NUMBER, "FM0159297") + .setHeader(ApplicationConstant.CLIENT_TYPE_CODE, "C") .build() ) .as(StepVerifier::create) @@ -304,6 +225,7 @@ void shouldCreateContacts() { .setHeader(ApplicationConstant.LOCATION_ID, 1) .setHeader(ApplicationConstant.TOTAL, 1L) .setHeader(ApplicationConstant.INDEX, 0L) + .setHeader(ApplicationConstant.CLIENT_TYPE_CODE, "C") .build() ) .as(StepVerifier::create) @@ -382,6 +304,7 @@ void shouldSendEmail() { .setHeader(ApplicationConstant.LOCATION_ID, 1) .setHeader(ApplicationConstant.TOTAL, 1L) .setHeader(ApplicationConstant.INDEX, 0L) + .setHeader(ApplicationConstant.CLIENT_TYPE_CODE, "C") .build() ) .as(StepVerifier::create) @@ -394,4 +317,145 @@ void shouldSendEmail() { .verifyComplete(); } -} \ No newline at end of file + @ParameterizedTest + @MethodSource("clientData") + @DisplayName("check client data") + void shouldCheckClientData( + String clientTypeCode, + String clientNumber) { + + when(submissionDetailRepository.findBySubmissionId(any())) + .thenReturn(Mono.just( + SubmissionDetailEntity + .builder() + .submissionId(2) + .incorporationNumber("XX0000000") + .organizationName("Sample test") + .clientTypeCode(clientTypeCode) + .clientNumber(clientNumber) + .build() + ) + ); + + when(legacyR2dbcEntityTemplate.selectOne(any(), any())) + .thenReturn( + Mono.just(ForestClientEntity.builder().clientNumber("00000000").build())); + + service + .checkClientData( + MessageBuilder + .withPayload(2) + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + assertThat(message) + .isNotNull() + .hasFieldOrPropertyWithValue("payload", + StringUtils.isNotBlank(clientNumber) ? 2 : "00000001"); + + assertThat(message.getHeaders().get(ApplicationConstant.SUBMISSION_ID)) + .as("submission id") + .isNotNull() + .isInstanceOf(Integer.class) + .isEqualTo(2); + + assertThat(message.getHeaders().get(ApplicationConstant.CREATED_BY)) + .as("created by") + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.UPDATED_BY)) + .as("updated by") + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .as("forest client number") + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo(StringUtils.defaultString(clientNumber, "00000001")); + + assertThat(message.getHeaders().get(ApplicationConstant.CLIENT_TYPE_CODE)) + .as("client type code") + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo(clientTypeCode); + + assertThat(message.getHeaders().get(ApplicationConstant.CLIENT_EXISTS)) + .as("client exists") + .isNotNull() + .isInstanceOf(Boolean.class) + .isEqualTo(StringUtils.isNotBlank(clientNumber)); + }) + .verifyComplete(); + } + + + @Test + @DisplayName("create client that is not individual") + void shouldCreateClient() { + + ReactiveInsert reactiveInsert = mock(ReactiveInsert.class); + + SubmissionDetailEntity detailEntity = SubmissionDetailEntity + .builder() + .submissionId(2) + .incorporationNumber("XX0000000") + .organizationName("Sample test") + .clientTypeCode("C") + .clientNumber("00000000") + .build(); + + when(submissionDetailRepository.findBySubmissionId(any())) + .thenReturn(Mono.just(detailEntity)); + when(submissionDetailRepository.save(any())) + .thenReturn(Mono.just(detailEntity)); + when(legacyR2dbcEntityTemplate.insert(eq(ForestClientEntity.class))) + .thenReturn(reactiveInsert); + when(reactiveInsert.using(any())) + .thenReturn(Mono.just(TestConstants.CLIENT_ENTITY)); + when(legacyR2dbcEntityTemplate.selectOne(any(), any())) + .thenReturn(Mono.empty()); + + service + .createForestClient( + MessageBuilder + .withPayload(TestConstants.CLIENT_ENTITY) + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.CLIENT_TYPE_CODE, "C") + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000000") + .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, "CHAMPAGNE SUPERNOVA") + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + assertThat(message) + .as("message") + .isNotNull() + .hasFieldOrPropertyWithValue("payload",2); + + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .as("forest client number") + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("00000000"); + }) + .verifyComplete(); + + } + + private static Stream clientData() { + return Stream.of( + Arguments.of("C", "00000000"), + Arguments.of("C", null) + ); + } + +} diff --git a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyClientPersistenceServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyClientPersistenceServiceTest.java new file mode 100644 index 0000000000..d5b76d2c0b --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyClientPersistenceServiceTest.java @@ -0,0 +1,157 @@ +package ca.bc.gov.app.service.legacy; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.entity.client.SubmissionDetailEntity; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.data.r2dbc.core.ReactiveInsertOperation.ReactiveInsert; +import org.springframework.integration.support.MessageBuilder; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Legacy Client Persistence Service") +class LegacyClientPersistenceServiceTest { + + private final SubmissionDetailRepository submissionDetailRepository = mock( + SubmissionDetailRepository.class); + private final SubmissionRepository submissionRepository = mock(SubmissionRepository.class); + private final SubmissionLocationRepository locationRepository = mock( + SubmissionLocationRepository.class); + private final SubmissionContactRepository contactRepository = mock( + SubmissionContactRepository.class); + private final SubmissionLocationContactRepository locationContactRepository = mock( + SubmissionLocationContactRepository.class); + private final R2dbcEntityTemplate legacyR2dbcEntityTemplate = mock(R2dbcEntityTemplate.class); + private final CountryCodeRepository countryCodeRepository = mock(CountryCodeRepository.class); + private final ClientDoingBusinessAsRepository doingBusinessAsRepository = mock( + ClientDoingBusinessAsRepository.class); + + private final LegacyClientPersistenceService service = new LegacyClientPersistenceService( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository + ); + + @ParameterizedTest(name = "type: {0} expected: {1}") + @MethodSource("byType") + @DisplayName("filter by type") + void shouldFilterByType(String type, boolean expected){ + assertEquals(expected, service.filterByType(type)); + } + + @Test + @DisplayName("get next channel") + void shouldGetNextChannel(){ + assertEquals(ApplicationConstant.SUBMISSION_LEGACY_OTHER_CHANNEL, service.getNextChannel()); + } + + + @Test + @DisplayName("create forest client") + void shouldCreateForestClient() { + ReactiveInsert reactiveInsert = mock(ReactiveInsert.class); + + SubmissionDetailEntity entity = SubmissionDetailEntity.builder() + .submissionId(2) + .submissionDetailId(2) + .organizationName("STAR DOT STAR VENTURES") + .incorporationNumber("FM0159297") + .businessTypeCode("R") + .clientTypeCode("C") + .goodStandingInd("Y") + .build(); + + when(submissionDetailRepository.findBySubmissionId(eq(2))) + .thenReturn(Mono.just(entity)); + + service + .generateForestClient( + MessageBuilder + .withPayload("00000001") + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000001") + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + assertThat(message) + .isNotNull() + .hasFieldOrProperty("payload"); + + assertThat(message.getPayload()) + .isNotNull() + .hasFieldOrPropertyWithValue("clientName", "STAR DOT STAR VENTURES") + .hasFieldOrPropertyWithValue("clientTypeCode", "C") + .hasFieldOrPropertyWithValue("registryCompanyTypeCode", "FM") + .hasFieldOrPropertyWithValue("corpRegnNmbr", "0159297") + .hasFieldOrPropertyWithValue("clientNumber", "00000001") + .hasFieldOrPropertyWithValue("clientComment", "Client details acquired from BC Registry FM0159297"); + + assertThat(message.getHeaders().get(ApplicationConstant.SUBMISSION_ID)) + .isNotNull() + .isInstanceOf(Integer.class) + .isEqualTo(2); + + assertThat(message.getHeaders().get(ApplicationConstant.CREATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.UPDATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NAME)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("STAR DOT STAR VENTURES"); + + assertThat(message.getHeaders().get(ApplicationConstant.INCORPORATION_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("FM0159297"); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("00000001"); + + }) + .verifyComplete(); + } + + private static Stream byType(){ + return Stream.of( + Arguments.of("I", false), + Arguments.of("C", true), + Arguments.of("USP", false), + Arguments.of("RSP", false) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyIndividualPersistenceServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyIndividualPersistenceServiceTest.java new file mode 100644 index 0000000000..8138d43d25 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyIndividualPersistenceServiceTest.java @@ -0,0 +1,157 @@ +package ca.bc.gov.app.service.legacy; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.entity.client.SubmissionDetailEntity; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.data.r2dbc.core.ReactiveInsertOperation.ReactiveInsert; +import org.springframework.integration.support.MessageBuilder; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Legacy Individual Persistence Service") +class LegacyIndividualPersistenceServiceTest { + + + private final SubmissionDetailRepository submissionDetailRepository = mock( + SubmissionDetailRepository.class); + private final SubmissionRepository submissionRepository = mock(SubmissionRepository.class); + private final SubmissionLocationRepository locationRepository = mock( + SubmissionLocationRepository.class); + private final SubmissionContactRepository contactRepository = mock( + SubmissionContactRepository.class); + private final SubmissionLocationContactRepository locationContactRepository = mock( + SubmissionLocationContactRepository.class); + private final R2dbcEntityTemplate legacyR2dbcEntityTemplate = mock(R2dbcEntityTemplate.class); + private final CountryCodeRepository countryCodeRepository = mock(CountryCodeRepository.class); + private final ClientDoingBusinessAsRepository doingBusinessAsRepository = mock( + ClientDoingBusinessAsRepository.class); + + private final LegacyIndividualPersistenceService service = new LegacyIndividualPersistenceService( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository + ); + + @ParameterizedTest(name = "type: {0} expected: {1}") + @MethodSource("byType") + @DisplayName("filter by type") + void shouldFilterByType(String type, boolean expected){ + assertEquals(expected, service.filterByType(type)); + } + + + @Test + @DisplayName("get next channel") + void shouldGetNextChannel(){ + assertEquals(ApplicationConstant.SUBMISSION_LEGACY_INDIVIDUAL_CHANNEL, service.getNextChannel()); + } + + + @Test + @DisplayName("create forest client") + void shouldCreateForestClient() { + ReactiveInsert reactiveInsert = mock(ReactiveInsert.class); + + SubmissionDetailEntity entity = SubmissionDetailEntity.builder() + .submissionId(2) + .submissionDetailId(2) + .organizationName("James Baxter") + .businessTypeCode("U") + .clientTypeCode("I") + .goodStandingInd("Y") + .build(); + + when(submissionDetailRepository.findBySubmissionId(eq(2))) + .thenReturn(Mono.just(entity)); + + service + .generateForestClient( + MessageBuilder + .withPayload("00000001") + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000001") + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + assertThat(message) + .isNotNull() + .hasFieldOrProperty("payload"); + + assertThat(message.getPayload()) + .isNotNull() + .hasFieldOrPropertyWithValue("clientName", "BAXTER") + .hasFieldOrPropertyWithValue("legalFirstName", "JAMES") + .hasFieldOrPropertyWithValue("clientComment", "Individual with data acquired from BC Services Card") + .hasFieldOrPropertyWithValue("clientTypeCode", "I") + .hasFieldOrPropertyWithValue("clientNumber", "00000001"); + + assertThat(message.getHeaders().get(ApplicationConstant.SUBMISSION_ID)) + .isNotNull() + .isInstanceOf(Integer.class) + .isEqualTo(2); + + assertThat(message.getHeaders().get(ApplicationConstant.CREATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.UPDATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NAME)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("JAMES BAXTER"); + + assertThat(message.getHeaders().get(ApplicationConstant.INCORPORATION_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("not applicable"); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("00000001"); + + }) + .verifyComplete(); + } + + private static Stream byType(){ + return Stream.of( + Arguments.of("I", true), + Arguments.of("C", false), + Arguments.of("USP", false), + Arguments.of("RSP", false) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyLoadingServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyLoadingServiceTest.java index 5af37cf399..196ae90fbe 100644 --- a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyLoadingServiceTest.java +++ b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyLoadingServiceTest.java @@ -86,6 +86,11 @@ private static Stream matchCheck() { private static class TestProcessorMatcher implements ProcessorMatcher{ + @Override + public boolean enabled(SubmissionInformationDto submission) { + return true; + } + @Override public String name() { return "Test matcher"; diff --git a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyRegisteredSPPersistenceServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyRegisteredSPPersistenceServiceTest.java new file mode 100644 index 0000000000..942cc28aed --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyRegisteredSPPersistenceServiceTest.java @@ -0,0 +1,349 @@ +package ca.bc.gov.app.service.legacy; + +import static ca.bc.gov.app.TestConstants.SUBMISSION_CONTACT; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.TestConstants; +import ca.bc.gov.app.entity.client.SubmissionDetailEntity; +import ca.bc.gov.app.entity.legacy.ClientDoingBusinessAsEntity; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.entity.legacy.ForestClientLocationEntity; +import ca.bc.gov.app.extensions.WiremockLogNotifier; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import ca.bc.gov.app.service.bcregistry.BcRegistryService; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.data.r2dbc.core.ReactiveInsertOperation.ReactiveInsert; +import org.springframework.http.MediaType; +import org.springframework.integration.support.MessageBuilder; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Legacy Registered Sole Proprietor Persistence Service") +class LegacyRegisteredSPPersistenceServiceTest { + + private final SubmissionDetailRepository submissionDetailRepository = mock( + SubmissionDetailRepository.class); + private final SubmissionRepository submissionRepository = mock(SubmissionRepository.class); + private final SubmissionLocationRepository locationRepository = mock( + SubmissionLocationRepository.class); + private final SubmissionContactRepository contactRepository = mock( + SubmissionContactRepository.class); + private final SubmissionLocationContactRepository locationContactRepository = mock( + SubmissionLocationContactRepository.class); + private final R2dbcEntityTemplate legacyR2dbcEntityTemplate = mock(R2dbcEntityTemplate.class); + private final CountryCodeRepository countryCodeRepository = mock(CountryCodeRepository.class); + private final ClientDoingBusinessAsRepository doingBusinessAsRepository = mock( + ClientDoingBusinessAsRepository.class); + private final BcRegistryService bcRegistryService = mock(BcRegistryService.class); + + private final LegacyRegisteredSPPersistenceService service = new LegacyRegisteredSPPersistenceService( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository, + bcRegistryService + ); + + @ParameterizedTest(name = "type: {0} expected: {1}") + @MethodSource("byType") + @DisplayName("filter by type") + void shouldFilterByType(String type, boolean expected){ + assertEquals(expected, service.filterByType(type)); + } + + @Test + @DisplayName("get next channel") + void shouldGetNextChannel(){ + assertEquals(ApplicationConstant.SUBMISSION_LEGACY_RSP_CHANNEL, service.getNextChannel()); + } + + @Test + @DisplayName("create forest client") + void shouldCreateForestClient() { + + SubmissionDetailEntity entity = SubmissionDetailEntity.builder() + .submissionId(2) + .submissionDetailId(2) + .organizationName("Baxter Corp") + .incorporationNumber("FM00184546") + .businessTypeCode("R") + .clientTypeCode("RSP") + .goodStandingInd("Y") + .build(); + + when(bcRegistryService.requestDocumentData(any(String.class))) + .thenReturn(Flux.just(TestConstants.BCREG_DOC_DATA)); + + when(submissionDetailRepository.findBySubmissionId(any())) + .thenReturn(Mono.just(entity)); + + when(contactRepository.findBySubmissionId(any())) + .thenReturn(Flux.empty()); + + service + .generateForestClient( + MessageBuilder + .withPayload("00000001") + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000001") + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + assertThat(message) + .isNotNull() + .hasFieldOrProperty("payload"); + + assertThat(message.getPayload()) + .isNotNull() + .hasFieldOrPropertyWithValue("clientName", "BAXTER") + .hasFieldOrPropertyWithValue("legalFirstName", "JAMES") + .hasFieldOrPropertyWithValue("clientIdTypeCode", "OTHR") + .hasFieldOrPropertyWithValue("clientIdentification", "FM00184546") + .hasFieldOrPropertyWithValue("registryCompanyTypeCode", "FM") + .hasFieldOrPropertyWithValue("corpRegnNmbr", "00184546") + .hasFieldOrPropertyWithValue("clientComment", + String.join(" ", + "Sole proprietorship registered on BC Registry with number", + "FM00184546", + "and company name", + "BAXTER CORP" + ) + ) + .hasFieldOrPropertyWithValue("clientTypeCode", "I") + .hasFieldOrPropertyWithValue("clientNumber", "00000001"); + + assertThat(message.getHeaders().get(ApplicationConstant.SUBMISSION_ID)) + .isNotNull() + .isInstanceOf(Integer.class) + .isEqualTo(2); + + assertThat(message.getHeaders().get(ApplicationConstant.CREATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.UPDATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NAME)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("BAXTER CORP"); + + assertThat(message.getHeaders().get(ApplicationConstant.INCORPORATION_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("FM00184546"); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("00000001"); + + }) + .verifyComplete(); + } + + @Test + @DisplayName("create forest client") + void shouldCreateForestClientWhenNoBcRegData() { + + SubmissionDetailEntity entity = SubmissionDetailEntity.builder() + .submissionId(2) + .submissionDetailId(2) + .organizationName("Baxter Corp") + .incorporationNumber("FM00184546") + .businessTypeCode("R") + .clientTypeCode("RSP") + .goodStandingInd("Y") + .build(); + + when(bcRegistryService.requestDocumentData(any(String.class))) + .thenReturn(Flux.empty()); + + when(submissionDetailRepository.findBySubmissionId(any())) + .thenReturn(Mono.just(entity)); + + when(contactRepository.findBySubmissionId(any())) + .thenReturn(Flux.just(SUBMISSION_CONTACT.withLastName("BAXTER"))); + + service + .generateForestClient( + MessageBuilder + .withPayload("00000001") + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000001") + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + assertThat(message) + .isNotNull() + .hasFieldOrProperty("payload"); + + assertThat(message.getPayload()) + .isNotNull() + .hasFieldOrPropertyWithValue("clientName", "BAXTER") + .hasFieldOrPropertyWithValue("legalFirstName", "JAMES") + .hasFieldOrPropertyWithValue("clientIdTypeCode", "OTHR") + .hasFieldOrPropertyWithValue("clientIdentification", "FM00184546") + .hasFieldOrPropertyWithValue("registryCompanyTypeCode", "FM") + .hasFieldOrPropertyWithValue("corpRegnNmbr", "00184546") + .hasFieldOrPropertyWithValue("clientComment", + String.join(" ", + "Sole proprietorship registered on BC Registry with number", + "FM00184546", + "and company name", + "BAXTER CORP" + ) + ) + .hasFieldOrPropertyWithValue("clientTypeCode", "I") + .hasFieldOrPropertyWithValue("clientNumber", "00000001"); + + assertThat(message.getHeaders().get(ApplicationConstant.SUBMISSION_ID)) + .isNotNull() + .isInstanceOf(Integer.class) + .isEqualTo(2); + + assertThat(message.getHeaders().get(ApplicationConstant.CREATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.UPDATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NAME)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("BAXTER CORP"); + + assertThat(message.getHeaders().get(ApplicationConstant.INCORPORATION_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("FM00184546"); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("00000001"); + + }) + .verifyComplete(); + } + + @Test + @DisplayName("create client with doing business") + void shouldCreateClient() { + ReactiveInsert doingBusinessInsert = mock(ReactiveInsert.class); + ReactiveInsert clientInsert = mock(ReactiveInsert.class); + + SubmissionDetailEntity detailEntity = SubmissionDetailEntity + .builder() + .submissionId(2) + .incorporationNumber("XX0000000") + .organizationName("Sample test") + .clientTypeCode("RSP") + .clientNumber("00000000") + .build(); + + when(submissionDetailRepository.findBySubmissionId(any())) + .thenReturn(Mono.just(detailEntity)); + when(submissionDetailRepository.save(any())) + .thenReturn(Mono.just(detailEntity)); + when(doingBusinessAsRepository.existsByClientNumber(any())) + .thenReturn(Mono.just(false)); + when(legacyR2dbcEntityTemplate.selectOne(any(),any())) + .thenReturn(Mono.just(ClientDoingBusinessAsEntity.builder().id(1).build())); + when(legacyR2dbcEntityTemplate.insert(ForestClientEntity.class)) + .thenReturn(clientInsert); + when(legacyR2dbcEntityTemplate.insert(ClientDoingBusinessAsEntity.class)) + .thenReturn(doingBusinessInsert); + when(doingBusinessInsert.using(any())) + .thenReturn(Mono.just(ClientDoingBusinessAsEntity.builder().clientNumber("00000000") + .build() + ) + ); + when(clientInsert.using(any())).thenReturn(Mono.just(TestConstants.CLIENT_ENTITY)); + + service + .createForestClient( + MessageBuilder + .withPayload( + TestConstants + .CLIENT_ENTITY + .withClientTypeCode("I") + .withClientIdTypeCode("OTHR") + ) + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.CLIENT_TYPE_CODE, "RSP") + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000000") + .setHeader(ApplicationConstant.FOREST_CLIENT_NAME, "CHAMPAGNE SUPERNOVA") + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + Assertions.assertThat(message) + .as("message") + .isNotNull() + .hasFieldOrPropertyWithValue("payload",2); + + + Assertions.assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .as("forest client number") + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("00000000"); + }) + .verifyComplete(); + + } + + private static Stream byType(){ + return Stream.of( + Arguments.of("I", false), + Arguments.of("C", false), + Arguments.of("USP", false), + Arguments.of("RSP", true) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyUnregisteredSPPersistenceServiceTest.java b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyUnregisteredSPPersistenceServiceTest.java new file mode 100644 index 0000000000..9cc137b830 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/legacy/LegacyUnregisteredSPPersistenceServiceTest.java @@ -0,0 +1,156 @@ +package ca.bc.gov.app.service.legacy; + +import static ca.bc.gov.app.TestConstants.SUBMISSION_CONTACT; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.ApplicationConstant; +import ca.bc.gov.app.entity.client.SubmissionDetailEntity; +import ca.bc.gov.app.repository.client.CountryCodeRepository; +import ca.bc.gov.app.repository.client.SubmissionContactRepository; +import ca.bc.gov.app.repository.client.SubmissionDetailRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationContactRepository; +import ca.bc.gov.app.repository.client.SubmissionLocationRepository; +import ca.bc.gov.app.repository.client.SubmissionRepository; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.integration.support.MessageBuilder; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Legacy Unregistered Sole Proprietor Persistence Service") +class LegacyUnregisteredSPPersistenceServiceTest { + + + private final SubmissionDetailRepository submissionDetailRepository = mock( + SubmissionDetailRepository.class); + private final SubmissionRepository submissionRepository = mock(SubmissionRepository.class); + private final SubmissionLocationRepository locationRepository = mock( + SubmissionLocationRepository.class); + private final SubmissionContactRepository contactRepository = mock( + SubmissionContactRepository.class); + private final SubmissionLocationContactRepository locationContactRepository = mock( + SubmissionLocationContactRepository.class); + private final R2dbcEntityTemplate legacyR2dbcEntityTemplate = mock(R2dbcEntityTemplate.class); + private final CountryCodeRepository countryCodeRepository = mock(CountryCodeRepository.class); + private final ClientDoingBusinessAsRepository doingBusinessAsRepository = mock( + ClientDoingBusinessAsRepository.class); + + private final LegacyUnregisteredSPPersistenceService service = new LegacyUnregisteredSPPersistenceService( + submissionDetailRepository, + submissionRepository, + locationRepository, + contactRepository, + locationContactRepository, + legacyR2dbcEntityTemplate, + countryCodeRepository, + doingBusinessAsRepository + ); + + @ParameterizedTest(name = "type: {0} expected: {1}") + @MethodSource("byType") + @DisplayName("filter by type") + void shouldFilterByType(String type, boolean expected){ + assertEquals(expected, service.filterByType(type)); + } + + @Test + @DisplayName("get next channel") + void shouldGetNextChannel(){ + assertEquals(ApplicationConstant.SUBMISSION_LEGACY_USP_CHANNEL, service.getNextChannel()); + } + + @Test + @DisplayName("create forest client") + void shouldCreateForestClient() { + + SubmissionDetailEntity entity = SubmissionDetailEntity.builder() + .submissionId(2) + .submissionDetailId(2) + .organizationName("Baxter, James") + .businessTypeCode("U") + .clientTypeCode("USP") + .goodStandingInd("Y") + .build(); + + when(submissionDetailRepository.findBySubmissionId(eq(2))) + .thenReturn(Mono.just(entity)); + + service + .generateForestClient( + MessageBuilder + .withPayload("00000001") + .setHeader(ApplicationConstant.SUBMISSION_ID, 2) + .setHeader(ApplicationConstant.FOREST_CLIENT_NUMBER, "00000001") + .setHeader(ApplicationConstant.CREATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .setHeader(ApplicationConstant.UPDATED_BY, ApplicationConstant.PROCESSOR_USER_NAME) + .build() + ) + .as(StepVerifier::create) + .assertNext(message -> { + assertThat(message) + .isNotNull() + .hasFieldOrProperty("payload"); + + assertThat(message.getPayload()) + .isNotNull() + .hasFieldOrPropertyWithValue("clientName", "BAXTER") + .hasFieldOrPropertyWithValue("legalFirstName", "JAMES") + .hasFieldOrPropertyWithValue("legalMiddleName", StringUtils.EMPTY) + .hasFieldOrPropertyWithValue("clientComment","Sole proprietorship with data acquired from BC Business eID") + .hasFieldOrPropertyWithValue("clientTypeCode", "I") + .hasFieldOrPropertyWithValue("clientNumber", "00000001"); + + assertThat(message.getHeaders().get(ApplicationConstant.SUBMISSION_ID)) + .isNotNull() + .isInstanceOf(Integer.class) + .isEqualTo(2); + + assertThat(message.getHeaders().get(ApplicationConstant.CREATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.UPDATED_BY)) + .isNotNull() + .isInstanceOf(String.class); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NAME)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("JAMES BAXTER"); + + assertThat(message.getHeaders().get(ApplicationConstant.INCORPORATION_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("not applicable"); + + assertThat(message.getHeaders().get(ApplicationConstant.FOREST_CLIENT_NUMBER)) + .isNotNull() + .isInstanceOf(String.class) + .isEqualTo("00000001"); + + }) + .verifyComplete(); + } + + private static Stream byType(){ + return Stream.of( + Arguments.of("I", false), + Arguments.of("C", false), + Arguments.of("USP", true), + Arguments.of("RSP", false) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/processor/DoingBusinessAsProcessorMatcherTest.java b/processor/src/test/java/ca/bc/gov/app/service/processor/DoingBusinessAsProcessorMatcherTest.java new file mode 100644 index 0000000000..0024623834 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/processor/DoingBusinessAsProcessorMatcherTest.java @@ -0,0 +1,88 @@ +package ca.bc.gov.app.service.processor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.dto.MatcherResult; +import ca.bc.gov.app.dto.SubmissionInformationDto; +import ca.bc.gov.app.entity.legacy.ClientDoingBusinessAsEntity; +import ca.bc.gov.app.repository.legacy.ClientDoingBusinessAsRepository; +import java.time.LocalDate; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Doing Business As Matcher") +class DoingBusinessAsProcessorMatcherTest { + + private final ClientDoingBusinessAsRepository repository = mock( + ClientDoingBusinessAsRepository.class); + ProcessorMatcher matcher = new DoingBusinessAsProcessorMatcher(repository); + + @Test + @DisplayName("Name matching") + void shouldMatchName() { + assertEquals("Doing Business As Fuzzy Matcher", matcher.name()); + } + + @ParameterizedTest + @MethodSource("legalName") + @DisplayName("Match or not") + void shouldMatchOrNot( + SubmissionInformationDto dto, + boolean success, + MatcherResult result, + Flux mockData + ) { + + when(repository.matchBy(dto.corporationName())) + .thenReturn(mockData); + + StepVerifier.FirstStep verifier = + matcher + .matches(dto) + .as(StepVerifier::create); + + if (success) { + verifier.verifyComplete(); + } else { + verifier + .expectNext(result) + .verifyComplete(); + } + } + + private static Stream legalName() { + return + Stream.of( + Arguments.of( + new SubmissionInformationDto("James", LocalDate.of(1970, 3, 4), "FM001122334", "Y", + "RSP"), + true, + null, + Flux.empty() + ), + Arguments.of( + new SubmissionInformationDto("Marco Polo Navigation Inc", LocalDate.of(1970, 3, 4), + "FM001122334", "Y", "RSP"), + false, + new MatcherResult("corporationName", String.join(",", "00000000")), + Flux.just(new ClientDoingBusinessAsEntity().withClientNumber("00000000")) + ), + Arguments.of( + new SubmissionInformationDto("Lucca", null, null, null, "RSP"), + false, + new MatcherResult("corporationName", String.join(",", "00000000", "00000001")), + Flux.just(new ClientDoingBusinessAsEntity().withClientNumber("00000000"), + new ClientDoingBusinessAsEntity().withClientNumber("00000001")) + ) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcherTest.java b/processor/src/test/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcherTest.java index 07ba9e09ef..d921072eb5 100644 --- a/processor/src/test/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcherTest.java +++ b/processor/src/test/java/ca/bc/gov/app/service/processor/GoodStandingProcessorMatcherTest.java @@ -1,6 +1,7 @@ package ca.bc.gov.app.service.processor; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import ca.bc.gov.app.dto.MatcherResult; import ca.bc.gov.app.dto.SubmissionInformationDto; @@ -24,6 +25,17 @@ void shouldMatchName() { assertEquals("Good Standing Matcher", matcher.name()); } + @ParameterizedTest + @MethodSource("goodStanding") + @DisplayName("Is enabled?") + void shouldBeEnableForAll( + SubmissionInformationDto dto, + boolean success, + MatcherResult result + ) { + assertTrue(matcher.enabled(dto)); + } + @ParameterizedTest @MethodSource("goodStanding") @DisplayName("Match or not") @@ -51,22 +63,22 @@ private static Stream goodStanding() { return Stream.of( Arguments.of( - new SubmissionInformationDto(null, null, StringUtils.EMPTY), + new SubmissionInformationDto(null, null,null, StringUtils.EMPTY,null), false, new MatcherResult("goodStanding", "Value not found") ), Arguments.of( - new SubmissionInformationDto(null, null, null), + new SubmissionInformationDto(null, null,null, null,null), false, new MatcherResult("goodStanding", "Value not found") ), Arguments.of( - new SubmissionInformationDto(null, null, "N"), + new SubmissionInformationDto(null, null,null, "N",null), false, new MatcherResult("goodStanding", "Client not in good standing") ), Arguments.of( - new SubmissionInformationDto(null, null, "Y"), + new SubmissionInformationDto(null, null,null, "Y",null), true, null ) diff --git a/processor/src/test/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcherTest.java b/processor/src/test/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcherTest.java index 8243705604..3d8de2ba1a 100644 --- a/processor/src/test/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcherTest.java +++ b/processor/src/test/java/ca/bc/gov/app/service/processor/IncorporationNumberProcessorMatcherTest.java @@ -30,6 +30,18 @@ void shouldMatchName() { assertEquals("Incorporation Number Matcher", matcher.name()); } + @ParameterizedTest + @MethodSource("incorporation") + @DisplayName("Match or not") + void shouldBeEnabled( + SubmissionInformationDto dto, + boolean success, + MatcherResult result, + Flux mockData + ) { + assertTrue(matcher.enabled(dto)); + } + @ParameterizedTest @MethodSource("incorporation") @DisplayName("Match or not") @@ -61,13 +73,13 @@ private static Stream incorporation() { return Stream.of( Arguments.of( - new SubmissionInformationDto(null, "00000007", null), + new SubmissionInformationDto(null,null, "00000007", null,"C"), true, null, Flux.empty() ), Arguments.of( - new SubmissionInformationDto(null, "00000006", null), + new SubmissionInformationDto(null,null, "00000006", null,"C"), false, new MatcherResult("incorporationNumber", "00000006"), Flux.just(new ForestClientEntity().withClientNumber("00000006")) diff --git a/processor/src/test/java/ca/bc/gov/app/service/processor/IndividualProcessorMatcherTest.java b/processor/src/test/java/ca/bc/gov/app/service/processor/IndividualProcessorMatcherTest.java new file mode 100644 index 0000000000..f6d38000f4 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/processor/IndividualProcessorMatcherTest.java @@ -0,0 +1,89 @@ +package ca.bc.gov.app.service.processor; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.dto.MatcherResult; +import ca.bc.gov.app.dto.SubmissionInformationDto; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.legacy.ForestClientRepository; +import java.time.LocalDate; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Individual Matcher") +class IndividualProcessorMatcherTest { + + + private final ForestClientRepository repository = mock(ForestClientRepository.class); + private final ProcessorMatcher matcher = new IndividualProcessorMatcher(repository); + + + @Test + @DisplayName("Name matching") + void shouldMatchName() { + assertEquals("Individual Matcher", matcher.name()); + } + + @ParameterizedTest + @MethodSource("legalName") + @DisplayName("Match or not") + void shouldMatchOrNot( + SubmissionInformationDto dto, + boolean success, + MatcherResult result, + Flux mockData + ) { + + when(repository.findByIndividual(anyString(),anyString(),any())) + .thenReturn(mockData); + + StepVerifier.FirstStep verifier = + matcher + .matches(dto) + .as(StepVerifier::create); + + if (success) { + verifier.verifyComplete(); + } else { + verifier + .expectNext(result) + .verifyComplete(); + } + } + + private static Stream legalName() { + return + Stream.of( + Arguments.of( + new SubmissionInformationDto("James Frank", LocalDate.of(1985,10,4), null, "Y", "I"), + true, + null, + Flux.empty() + ), + Arguments.of( + new SubmissionInformationDto("Marco Polo", LocalDate.of(1977,3,22), null, "Y", "I"), + false, + new MatcherResult("corporationName", String.join(",", "00000000")), + Flux.just(new ForestClientEntity().withClientNumber("00000000")) + ), + Arguments.of( + new SubmissionInformationDto("Lucca DeBiaggio", LocalDate.of(1951,12,25), null, "Y", "I"), + false, + new MatcherResult("corporationName", String.join(",", "00000000", "00000001")), + Flux.just(new ForestClientEntity().withClientNumber("00000000"), + new ForestClientEntity().withClientNumber("00000001")) + ) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcherTest.java b/processor/src/test/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcherTest.java index a5fad5e087..e09985bfe2 100644 --- a/processor/src/test/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcherTest.java +++ b/processor/src/test/java/ca/bc/gov/app/service/processor/LegalNameProcessorMatcherTest.java @@ -9,6 +9,7 @@ import ca.bc.gov.app.dto.SubmissionInformationDto; import ca.bc.gov.app.entity.legacy.ForestClientEntity; import ca.bc.gov.app.repository.legacy.ForestClientRepository; +import java.time.LocalDate; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -27,7 +28,7 @@ class LegalNameProcessorMatcherTest { @Test @DisplayName("Name matching") void shouldMatchName() { - assertEquals("Legal Name Matcher", matcher.name()); + assertEquals("Legal Name Fuzzy Matcher", matcher.name()); } @ParameterizedTest @@ -40,7 +41,7 @@ void shouldMatchOrNot( Flux mockData ) { - when(repository.matchBy(dto.legalName())) + when(repository.matchBy(dto.corporationName())) .thenReturn(mockData); StepVerifier.FirstStep verifier = @@ -61,21 +62,21 @@ private static Stream legalName() { return Stream.of( Arguments.of( - new SubmissionInformationDto("James", null, null), + new SubmissionInformationDto("James", null,null, null,"C"), true, null, Flux.empty() ), Arguments.of( - new SubmissionInformationDto("Marco", null, null), + new SubmissionInformationDto("Marco", null, null, null,"C"), false, - new MatcherResult("legalName", String.join(",", "00000000")), + new MatcherResult("corporationName", String.join(",", "00000000")), Flux.just(new ForestClientEntity().withClientNumber("00000000")) ), Arguments.of( - new SubmissionInformationDto("Lucca", null, null), + new SubmissionInformationDto("Lucca", null, null, null,"C"), false, - new MatcherResult("legalName", String.join(",", "00000000", "00000001")), + new MatcherResult("corporationName", String.join(",", "00000000", "00000001")), Flux.just(new ForestClientEntity().withClientNumber("00000000"), new ForestClientEntity().withClientNumber("00000001")) ) diff --git a/processor/src/test/java/ca/bc/gov/app/service/processor/SoleProprietorProcessorMatcherTest.java b/processor/src/test/java/ca/bc/gov/app/service/processor/SoleProprietorProcessorMatcherTest.java new file mode 100644 index 0000000000..3aed05a741 --- /dev/null +++ b/processor/src/test/java/ca/bc/gov/app/service/processor/SoleProprietorProcessorMatcherTest.java @@ -0,0 +1,91 @@ +package ca.bc.gov.app.service.processor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.dto.MatcherResult; +import ca.bc.gov.app.dto.SubmissionInformationDto; +import ca.bc.gov.app.entity.legacy.ForestClientEntity; +import ca.bc.gov.app.repository.legacy.ForestClientRepository; +import java.time.LocalDate; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Sole Proprietor Matcher") +class SoleProprietorProcessorMatcherTest { + + private final ForestClientRepository repository = mock(ForestClientRepository.class); + private final ProcessorMatcher matcher = new SoleProprietorProcessorMatcher(repository); + + + @Test + @DisplayName("Name matching") + void shouldMatchName() { + assertEquals("Sole Proprietor Matcher", matcher.name()); + } + + @ParameterizedTest + @MethodSource("legalName") + @DisplayName("Match or not") + void shouldMatchOrNot( + SubmissionInformationDto dto, + boolean success, + MatcherResult result, + Flux mockData + ) { + + when(repository.findByIndividual(anyString(), anyString(), any())) + .thenReturn(mockData); + + StepVerifier.FirstStep verifier = + matcher + .matches(dto) + .as(StepVerifier::create); + + if (success) { + verifier.verifyComplete(); + } else { + verifier + .expectNext(result) + .verifyComplete(); + } + } + + private static Stream legalName() { + return + Stream.of( + Arguments.of( + new SubmissionInformationDto("James Frank", LocalDate.of(2023, 4, 5), null, null, + "USP"), + true, + null, + Flux.empty() + ), + Arguments.of( + new SubmissionInformationDto("Marco Polo", LocalDate.of(2023, 9, 12), null, null, + "RSP"), + false, + new MatcherResult("corporationName", String.join(",", "00000000")), + Flux.just(new ForestClientEntity().withClientNumber("00000000")) + ), + Arguments.of( + new SubmissionInformationDto("Lucca DeBiaggio", LocalDate.of(2023, 10, 11), null, + null, "USP"), + false, + new MatcherResult("corporationName", String.join(",", "00000000", "00000001")), + Flux.just(new ForestClientEntity().withClientNumber("00000000"), + new ForestClientEntity().withClientNumber("00000001")) + ) + ); + } + +} \ No newline at end of file diff --git a/processor/src/test/java/ca/bc/gov/app/util/ProcessorUtilTest.java b/processor/src/test/java/ca/bc/gov/app/util/ProcessorUtilTest.java index e360f8e18c..c93b9f777d 100644 --- a/processor/src/test/java/ca/bc/gov/app/util/ProcessorUtilTest.java +++ b/processor/src/test/java/ca/bc/gov/app/util/ProcessorUtilTest.java @@ -43,6 +43,12 @@ void shouldExtractNumbers(String input, String expectedLetters, String expectedN Assertions.assertEquals(expectedNumbers, ProcessorUtil.extractNumbers(input)); } + @ParameterizedTest(name = "should split name {0} and get {1}") + @MethodSource("splitName") + @DisplayName("should split name") + void shouldSplitName(String input, String[] expected) { + Assertions.assertArrayEquals(expected, ProcessorUtil.splitName(input)); + } private static Stream readHeader() { return Stream.of( @@ -61,5 +67,40 @@ private static Stream extract() { ); } + private static Stream splitName() { + return + Stream.of( + Arguments.of( + "John Doe", + new String[]{"Doe", "John", ""} + ), + Arguments.of( + "Doe, John", + new String[]{"Doe", "John", ""} + ), + Arguments.of( + "John Doe Smith", + new String[]{"Smith", "John", "Doe"} + ), + Arguments.of( + "Doe, John Smith", + new String[]{"Doe", "John", "Smith"} + ), + Arguments.of( + "John Doe Smith Jones", + new String[]{"Jones", "John", "Doe Smith"} + ), + Arguments.of("Jhon", new String[]{"Jhon", "Jhon", ""}), + Arguments.of(StringUtils.EMPTY, + new String[]{StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY} + ), + Arguments.of(null, + new String[]{StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY} + ), + Arguments.of(" ", + new String[]{StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY} + ) + ); + } } \ No newline at end of file diff --git a/processor/src/test/resources/application-default.yml b/processor/src/test/resources/application-default.yml index db7953d9c7..b3b7a4db5c 100644 --- a/processor/src/test/resources/application-default.yml +++ b/processor/src/test/resources/application-default.yml @@ -14,6 +14,15 @@ spring: url: jdbc:oracle:thin:@${ca.bc.gov.nrs.oracle.host}:${ca.bc.gov.nrs.oracle.port}/${ca.bc.gov.nrs.oracle.service} +ca: + bc: + gov: + nrs: + bcregistry: + uri: 'http://127.0.0.1:10040' + apiKey: abc1234 + accountId: 'account 0000' + logging: level: org: diff --git a/processor/src/test/resources/init_pg.sql b/processor/src/test/resources/init_pg.sql index 6e1bb2be65..e2aec3d751 100644 --- a/processor/src/test/resources/init_pg.sql +++ b/processor/src/test/resources/init_pg.sql @@ -32,7 +32,7 @@ drop sequence if exists nrfc.submission_submitter_seq; create schema if not exists nrfc; create table if not exists nrfc.client_type_code ( - client_type_code varchar(1) not null, + client_type_code varchar(5) not null, description varchar(100) not null, effective_date date not null, expiry_date date default to_date('99991231','YYYYMMDD') not null, @@ -54,7 +54,7 @@ comment on column nrfc.client_type_code.create_user is 'The user or proxy accoun comment on column nrfc.client_type_code.update_user is 'The user or proxy account that created or last updated the record.'; create table if not exists nrfc.submission_status_code ( - submission_status_code varchar(5) not null, + submission_status_code varchar(5) not null, description varchar(100) not null, effective_date date not null, expiry_date date default to_date('99991231','YYYYMMDD') not null, @@ -192,13 +192,13 @@ comment on column nrfc.business_type_code.create_user is 'The user or proxy acco comment on column nrfc.business_type_code.update_user is 'The user or proxy account that created or last updated the record.'; create table if not exists nrfc.submission( - submission_id integer not null, - submission_status_code varchar(5) null, - submission_type_code varchar(5) null, + submission_id integer not null, + submission_status_code varchar(5) null, + submission_type_code varchar(5) null, submission_date timestamp null, update_timestamp timestamp default current_timestamp, create_user varchar(60) not null, - update_user varchar(60) null, + update_user varchar(60) null, constraint submission_pk primary key (submission_id), constraint submission_submission_status_code_fk foreign key (submission_status_code) references nrfc.submission_status_code(submission_status_code), constraint submission_submission_type_code_fk foreign key (submission_type_code) references nrfc.submission_type_code(submission_type_code) @@ -220,8 +220,9 @@ create table if not exists nrfc.submission_detail ( business_type_code varchar(1) not null, incorporation_number varchar(50) null, organization_name varchar(100) null, - client_type_code varchar(1) not null, + client_type_code varchar(5) not null, good_standing_ind varchar(1) null, + birthdate date null, constraint submission_detail_id_pk primary key (submission_detail_id), constraint submission_id_fk foreign key (submission_id) references nrfc.submission(submission_id), constraint submission_detail_business_type_code_fk foreign key (business_type_code) references nrfc.business_type_code(business_type_code), @@ -236,6 +237,7 @@ comment on column nrfc.submission_detail.incorporation_number is 'A number provi comment on column nrfc.submission_detail.organization_name is 'The name of the client.'; comment on column nrfc.submission_detail.client_type_code is 'A code representing the type of a client.'; comment on column nrfc.submission_detail.good_standing_ind is 'An indicator that determines whether a client is in good standing with respect to their financial obligations.'; +comment on column nrfc.submission_detail.birthdate is 'The date that the BC Services Card logged in person was born.'; create table if not exists nrfc.submission_matching_detail ( submission_matching_detail_id integer not null, @@ -368,6 +370,8 @@ insert into nrfc.client_type_code (client_type_code, description, effective_date insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('S', 'Society', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('T', 'First Nation Tribal Council', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('U', 'Unregistered Company', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; +insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('USP', 'Unregistered sole proprietorship', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; +insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('RSP', 'Registered sole proprietorship', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; insert into nrfc.contact_type_code (contact_type_code, description, effective_date, create_user) values ('AP', 'Accounts Payable', current_timestamp, 'mariamar') on conflict (contact_type_code) do nothing; insert into nrfc.contact_type_code (contact_type_code, description, effective_date, create_user) values ('AR', 'Accounts Receivable', current_timestamp, 'mariamar') on conflict (contact_type_code) do nothing;