From 42c954f00549fb58086d1031aa1d49010d2a0a7c Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Fri, 5 Jan 2024 16:10:47 -0800 Subject: [PATCH 1/5] fix(FSADT1-1052): fixing list of client types --- .../gov/app/controller/client/ClientController.java | 2 +- .../repository/client/ContactTypeCodeRepository.java | 11 ++++++++++- .../ca/bc/gov/app/service/client/ClientService.java | 4 ++-- .../client/ClientControllerIntegrationTest.java | 7 ++----- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/ca/bc/gov/app/controller/client/ClientController.java b/backend/src/main/java/ca/bc/gov/app/controller/client/ClientController.java index a0d52c9fe2..663b764f90 100644 --- a/backend/src/main/java/ca/bc/gov/app/controller/client/ClientController.java +++ b/backend/src/main/java/ca/bc/gov/app/controller/client/ClientController.java @@ -81,7 +81,7 @@ public Flux listClientContactTypeCodes( Integer size ) { return clientService - .listClientContactTypeCodes(page, size); + .listClientContactTypeCodes(LocalDate.now(),page, size); } /** diff --git a/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java b/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java index 1cde60a7b8..ac46a506ab 100644 --- a/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java +++ b/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java @@ -1,7 +1,9 @@ package ca.bc.gov.app.repository.client; import ca.bc.gov.app.entity.client.ContactTypeCodeEntity; +import java.time.LocalDate; import org.springframework.data.domain.Pageable; +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; @@ -13,7 +15,14 @@ public interface ContactTypeCodeRepository extends ReactiveCrudRepository, ReactiveSortingRepository { - Flux findBy(Pageable pageable); + @Query(""" + SELECT * FROM + nrfc.contact_type_code + WHERE + (expiry_date is null or expiry_date > :activeDate) + and effective_date <= :activeDate + ORDER BY description""") + Flux findActiveAt(LocalDate activeDate, Pageable pageable); Mono findByOrDescription(String id, String description); } diff --git a/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java b/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java index 8c68fe075c..1ffb1c354b 100644 --- a/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java +++ b/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java @@ -149,9 +149,9 @@ public Flux listProvinces(String countryCode, int page, int size) { * @param size The amount of entries per page. * @return A list of {@link CodeNameDto} entries. */ - public Flux listClientContactTypeCodes(int page, int size) { + public Flux listClientContactTypeCodes(LocalDate activeDate,int page, int size) { return contactTypeCodeRepository - .findBy(PageRequest.of(page, size)) + .findActiveAt(activeDate, PageRequest.of(page, size)) .map(entity -> new CodeNameDto( entity.getContactTypeCode(), entity.getDescription())); diff --git a/backend/src/test/java/ca/bc/gov/app/controller/client/ClientControllerIntegrationTest.java b/backend/src/test/java/ca/bc/gov/app/controller/client/ClientControllerIntegrationTest.java index 0371e41649..11f8a08d13 100644 --- a/backend/src/test/java/ca/bc/gov/app/controller/client/ClientControllerIntegrationTest.java +++ b/backend/src/test/java/ca/bc/gov/app/controller/client/ClientControllerIntegrationTest.java @@ -560,11 +560,8 @@ private static Stream provinceCode() { private static Stream contactTypeCodes() { return Stream.of( - Arguments.of(null, null, "AP", "Accounts Payable"), - Arguments.of(0, 1, "AP", "Accounts Payable"), - Arguments.of(1, 1, "AR", "Accounts Receivable"), - Arguments.of(11, 1, "GP", "General Partner"), - Arguments.of(22, 1, "TP", "EDI Trading Partner") + Arguments.of(null, null, "TC", "BCTS Contractor"), + Arguments.of(0, 1, "TC", "BCTS Contractor") ); } From 589765d5e83b4ef86bd630419baf6622ae44123b Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Fri, 5 Jan 2024 16:21:44 -0800 Subject: [PATCH 2/5] fix(FSADT1-1101|FSADT1-1103|FSADT1-1106): fixing review email --- .../service/client/ClientSubmissionMailService.java | 12 ------------ .../client/ClientSubmissionMailServiceTest.java | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionMailService.java b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionMailService.java index 7388ba75db..c742038283 100644 --- a/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionMailService.java +++ b/processor/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionMailService.java @@ -22,18 +22,6 @@ public class ClientSubmissionMailService { @ServiceActivator(inputChannel = ApplicationConstant.SUBMISSION_MAIL_CHANNEL) public void sendMail(Message mailMessage) { - if ( - Objects.equals(mailMessage.getHeaders().get( - ApplicationConstant.SUBMISSION_TYPE, SubmissionTypeCodeEnum.class - ), SubmissionTypeCodeEnum.RNC) - ) { - log.info("Receiving a review notification, mail not sent {} {} -> {}", - mailMessage.getPayload().email(), - mailMessage.getPayload().subject(), - mailMessage.getPayload().variables() - ); - return; - } log.info("Sending email to {} {} -> {}", mailMessage.getPayload().email(), mailMessage.getPayload().subject(), 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 cb164232e5..a5ffe5b0d8 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 @@ -81,7 +81,7 @@ void shouldPreventReviewMails() { .untilAsserted(() -> { wireMockExtension .verify( - 0, + 1, postRequestedFor(urlEqualTo("/ches/email")) .withHeader("Content-Type", containing(MediaType.APPLICATION_JSON_VALUE)) .withRequestBody(equalToJson(TestConstants.EMAIL_REQUEST_JSON) From f8830fd98d9f53f55f985392134ed43268230fbe Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Fri, 5 Jan 2024 16:54:34 -0800 Subject: [PATCH 3/5] fix(FSADT1-1106): adding environment and mail list - added a mail list as a parameter instead of hardcoded - setting environment name on email subject when not in prod --- .github/workflows/merge-main.yml | 2 + .github/workflows/pr-open.yml | 1 + .../ForestClientConfiguration.java | 1 + .../bc/gov/app/service/ches/ChesService.java | 196 ++++++++++-------- backend/src/main/resources/application.yml | 1 + .../db/migration/V3__email_log_size.sql | 1 + .../test/resources/application-default.yml | 1 + 7 files changed, 113 insertions(+), 90 deletions(-) create mode 100644 backend/src/main/resources/db/migration/V3__email_log_size.sql diff --git a/.github/workflows/merge-main.yml b/.github/workflows/merge-main.yml index 3fecd5f031..3a2f169e39 100644 --- a/.github/workflows/merge-main.yml +++ b/.github/workflows/merge-main.yml @@ -195,6 +195,7 @@ jobs: -p COGNITO_ENVIRONMENT=TEST -p COGNITO_REDIRECT_URI=https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }}/dashboard -p COGNITO_LOGOUT_URI=https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }} + -p CHES_MAIL_COPY=${{ secrets.CHES_MAIL_COPY }} - name: Conventional Changelog Update uses: TriPSs/conventional-changelog-action@v5 @@ -370,6 +371,7 @@ jobs: -p COGNITO_ENVIRONMENT=PROD -p COGNITO_REDIRECT_URI=https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }}/dashboard -p COGNITO_LOGOUT_URI=https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }} + -p CHES_MAIL_COPY=${{ secrets.CHES_MAIL_COPY }} prod-deploy: name: PROD Deployment diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index c9b3224aa2..032c9403df 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -182,6 +182,7 @@ jobs: -p COGNITO_ENVIRONMENT=DEV -p COGNITO_REDIRECT_URI=https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }}/dashboard -p COGNITO_LOGOUT_URI=https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }} + -p CHES_MAIL_COPY=${{ secrets.CHES_MAIL_COPY }} - name: Deploy Database uses: bcgov-nr/action-deployer-openshift@v2.0.0 diff --git a/backend/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java b/backend/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java index 321ffdfd9d..b818fabb5c 100644 --- a/backend/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java +++ b/backend/src/main/java/ca/bc/gov/app/configuration/ForestClientConfiguration.java @@ -52,6 +52,7 @@ public static class ChesConfiguration { private String clientId; private String clientSecret; private String scope; + private List copyEmail; } /** diff --git a/backend/src/main/java/ca/bc/gov/app/service/ches/ChesService.java b/backend/src/main/java/ca/bc/gov/app/service/ches/ChesService.java index 0230661b63..6117b2b831 100644 --- a/backend/src/main/java/ca/bc/gov/app/service/ches/ChesService.java +++ b/backend/src/main/java/ca/bc/gov/app/service/ches/ChesService.java @@ -11,13 +11,27 @@ import ca.bc.gov.app.dto.ches.CommonExposureJwtDto; import ca.bc.gov.app.dto.client.EmailLogDto; import ca.bc.gov.app.entity.client.EmailLogEntity; -import ca.bc.gov.app.exception.*; +import ca.bc.gov.app.exception.BadRequestException; +import ca.bc.gov.app.exception.InvalidAccessTokenException; +import ca.bc.gov.app.exception.InvalidRequestObjectException; +import ca.bc.gov.app.exception.InvalidRoleException; +import ca.bc.gov.app.exception.UnableToProcessRequestException; +import ca.bc.gov.app.exception.UnexpectedErrorException; import ca.bc.gov.app.repository.client.EmailLogRepository; import com.fasterxml.jackson.core.JsonProcessingException; -import io.r2dbc.postgresql.codec.Json; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; +import io.r2dbc.postgresql.codec.Json; +import java.io.IOException; +import java.io.StringWriter; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpHeaders; @@ -29,14 +43,6 @@ import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; -import java.io.IOException; -import java.io.StringWriter; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; @Service @Slf4j @@ -48,9 +54,9 @@ public class ChesService { private final WebClient authApi; private final Configuration freeMarkerConfiguration; - + private final EmailLogRepository emailLogRepository; - + private final Jackson2ObjectMapperBuilder builder; public ChesService( @@ -71,93 +77,103 @@ public ChesService( } public Mono sendEmail(String templateName, - String emailAddress, - String subject, - Map variables, - Integer emailLogId) { + String emailAddress, + String subject, + Map variables, + Integer emailLogId) { + + List emails = new ArrayList<>(); + emails.add(emailAddress); + emails.addAll(configuration.getChes().getCopyEmail()); + + String processedSubject = + configuration.getCognito().getEnvironment().equalsIgnoreCase("prod") + ? subject + : String.format("[%s] %s", configuration.getCognito().getEnvironment(), subject); + return this .buildTemplate(templateName, variables) - .flatMap(body -> { - ChesRequestDto chesRequestDto = new ChesRequestDto(List.of(emailAddress, - "paulo.cruz@gov.bc.ca", - "ziad.bhunnoo@gov.bc.ca", - "maria.martinez@gov.bc.ca"), - body); - - return this.sendEmail(chesRequestDto, subject).flatMap(emailId -> { - log.info("Mail sent, transaction ID is {}", emailId); - - EmailLogDto emailLogDto = new EmailLogDto( - emailLogId, - templateName, - emailAddress, - subject, - "Y", - emailId, - "", - variables); - return saveEmailLog(emailLogDto, "Email sent successfully. Transaction ID: " + emailId); - }) - .onErrorResume(throwable -> { - log.error("Error occurred while building/sending the email: {}", throwable.getMessage()); - - EmailLogDto emailLogDto = new EmailLogDto( - emailLogId, - templateName, - emailAddress, - subject, - "N", - "", - throwable.getMessage(), - variables); - return saveEmailLog(emailLogDto, "Error sending email"); - }); - }); + .map(body -> new ChesRequestDto(emails, body)) + .flatMap(chesRequestDto -> + this + .sendEmail(chesRequestDto, processedSubject) + .doOnNext(emailId -> log.info("Mail sent, transaction ID is {}", emailId)) + .flatMap(emailId -> + saveEmailLog( + new EmailLogDto( + emailLogId, + templateName, + emailAddress, + processedSubject, + "Y", + emailId, + "", + variables), + "Email sent successfully. Transaction ID: " + emailId + ) + ) + ) + .doOnError(throwable -> log.error("Error occurred while building/sending the email: {}", + throwable.getMessage())) + .onErrorResume(throwable -> + saveEmailLog( + new EmailLogDto( + emailLogId, + templateName, + emailAddress, + processedSubject, + "N", + "", + throwable.getMessage(), + variables), + "Error sending email" + ) + ); } - + private Mono saveEmailLog(EmailLogDto emailLogDto, String transactionMsg) { if (emailLogDto.emailLogId() != null) { - return emailLogRepository.findById(emailLogDto.emailLogId()) - .flatMap(existingLogEntity -> updateExistingLogEntity( - existingLogEntity, - emailLogDto, - transactionMsg)); + return emailLogRepository.findById(emailLogDto.emailLogId()) + .flatMap(existingLogEntity -> updateExistingLogEntity( + existingLogEntity, + emailLogDto, + transactionMsg)); } else { - EmailLogEntity logEntity = createNewLogEntity(emailLogDto); - return emailLogRepository.save(logEntity) - .thenReturn(transactionMsg); + EmailLogEntity logEntity = createNewLogEntity(emailLogDto); + return emailLogRepository.save(logEntity) + .thenReturn(transactionMsg); } } private Mono updateExistingLogEntity( - EmailLogEntity existingLogEntity, - EmailLogDto emailLogDto, - String transactionMsg) { - - String exceptionMessage = "Y".equals(existingLogEntity.getEmailSentInd()) - ? "" - : emailLogDto.exceptionMessage(); - existingLogEntity.setEmailSentInd(emailLogDto.emailSentInd()); - existingLogEntity.setExceptionMessage(exceptionMessage); - existingLogEntity.setUpdateDate(LocalDateTime.now()); - - return emailLogRepository - .save(existingLogEntity) - .thenReturn(transactionMsg); + EmailLogEntity existingLogEntity, + EmailLogDto emailLogDto, + String transactionMsg) { + + String exceptionMessage = "Y".equals(existingLogEntity.getEmailSentInd()) + ? "" + : emailLogDto.exceptionMessage(); + existingLogEntity.setEmailSentInd(emailLogDto.emailSentInd()); + existingLogEntity.setExceptionMessage(exceptionMessage); + existingLogEntity.setUpdateDate(LocalDateTime.now()); + + return emailLogRepository + .save(existingLogEntity) + .thenReturn(transactionMsg); } - + private EmailLogEntity createNewLogEntity(EmailLogDto emailLogDto) { - EmailLogEntity logEntity = new EmailLogEntity(); - logEntity.setCreateDate(LocalDateTime.now()); - logEntity.setTemplateName(emailLogDto.templateName()); - logEntity.setEmailAddress(emailLogDto.emailAddress()); - logEntity.setEmailSubject(emailLogDto.subject()); - logEntity.setEmailSentInd(emailLogDto.emailSentInd()); - logEntity.setEmailId(emailLogDto.emailId()); - logEntity.setExceptionMessage(emailLogDto.exceptionMessage()); - logEntity.setEmailVariables(convertTo(emailLogDto.variables())); - - return logEntity; + EmailLogEntity logEntity = new EmailLogEntity(); + logEntity.setCreateDate(LocalDateTime.now()); + logEntity.setTemplateName(emailLogDto.templateName()); + logEntity.setEmailAddress(emailLogDto.emailAddress()); + logEntity.setEmailSubject(emailLogDto.subject()); + logEntity.setEmailSentInd(emailLogDto.emailSentInd()); + logEntity.setEmailId(emailLogDto.emailId()); + logEntity.setExceptionMessage(emailLogDto.exceptionMessage()); + logEntity.setEmailVariables(convertTo(emailLogDto.variables())); + + return logEntity; } private Json convertTo(Map variables) { @@ -165,8 +181,8 @@ private Json convertTo(Map variables) { try { json = builder - .build() - .writeValueAsString(variables); + .build() + .writeValueAsString(variables); } catch (JsonProcessingException e) { log.error("Error while converting matchers to json", e); } @@ -247,7 +263,7 @@ public Mono sendEmail(ChesRequestDto requestContent, String subject) { * @param variables a map of variable names and their corresponding values to be used when * processing the template * @return a Mono that emits the String representation of the processed template, or an error if - * an exception occurs during template processing + * an exception occurs during template processing */ public Mono buildTemplate(String templateName, Map variables) { StringWriter writer = new StringWriter(); diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 7b6b640a58..878d5131d4 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -55,6 +55,7 @@ ca: clientId: ${CHES_CLIENT_ID:clientId} clientSecret: ${CHES_CLIENT_SECRET:secret} scope: scope + copyEmail: ${CHES_COPY_EMAIL:email@email.ca} bcregistry: uri: ${BCREGISTRY_URI:https://bcregistry-sandbox.apigee.net} apiKey: ${BCREGISTRY_KEY:123456} diff --git a/backend/src/main/resources/db/migration/V3__email_log_size.sql b/backend/src/main/resources/db/migration/V3__email_log_size.sql new file mode 100644 index 0000000000..1b31728acf --- /dev/null +++ b/backend/src/main/resources/db/migration/V3__email_log_size.sql @@ -0,0 +1 @@ +ALTER TABLE nrfc.email_log ALTER COLUMN email_subject TYPE varchar(50) USING email_subject::varchar(50); diff --git a/backend/src/test/resources/application-default.yml b/backend/src/test/resources/application-default.yml index 520de5b2ba..77d369596c 100644 --- a/backend/src/test/resources/application-default.yml +++ b/backend/src/test/resources/application-default.yml @@ -13,6 +13,7 @@ ca: clientId: clientId clientSecret: secret scope: scope + copyEmail: sample@email.ca,mail@mail.ca,nobody@mail.ca bcregistry: uri: 'http://127.0.0.1:10040' apiKey: abc1234 From f9af1578a8a1466ac8633448ae0c33a55ecb84b4 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Fri, 5 Jan 2024 16:56:40 -0800 Subject: [PATCH 4/5] fix(FSADT1-1104): removing limit to active clients only --- .../ca/bc/gov/app/repository/ForestClientRepository.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java b/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java index c89cfeb405..616a2017a3 100644 --- a/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java +++ b/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java @@ -19,8 +19,7 @@ public interface ForestClientRepository extends ReactiveCrudRepository findClientByIncorporationOrName( @Param("incorporationNumber") String incorporationNumber, @Param("companyName") String companyName @@ -33,7 +32,6 @@ Flux findClientByIncorporationOrName( 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); @@ -43,7 +41,6 @@ AND UPPER(CLIENT_NAME) = UPPER(:lastName) FROM THE.FOREST_CLIENT WHERE UTL_MATCH.JARO_WINKLER_SIMILARITY(UPPER(CLIENT_NAME),UPPER(:companyName)) >= 95 - AND CLIENT_STATUS_CODE = 'ACT' ORDER BY CLIENT_NUMBER""") Flux matchBy(String companyName); From b90f1dc940ba6967cd5390a67f21aaeb139cb7b2 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Mon, 8 Jan 2024 10:13:50 -0800 Subject: [PATCH 5/5] chore: updating deployment file --- common/openshift.init.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/openshift.init.yml b/common/openshift.init.yml index 14db16f63a..5a5c5d7d6e 100644 --- a/common/openshift.init.yml +++ b/common/openshift.init.yml @@ -86,6 +86,9 @@ parameters: - name: COGNITO_LOGOUT_URI description: Cognito redirect url once logged out required: true + - name: CHES_MAIL_COPY + description: Email address to copy all CHES emails to + required: true objects: - apiVersion: v1 kind: Secret