From 5d49b68232aa80a5ce8f794ad27322f0d5828c50 Mon Sep 17 00:00:00 2001 From: GavCookCO <99668051+GavCookCO@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:47:20 +0000 Subject: [PATCH 01/19] TMI2-396: Add submissions to SQS for processing in Spotlight (#51) * first pass at adding Spotlight submission + batch + queue * first pass at adding Spotlight submission + batch + queue * Adding functionality to send Spotlight Submissions to SQS * refactoring a property name * PR feedback --- pom.xml | 5 +- .../gap/applybackend/config/SQSConfig.java | 17 ++++ .../SpotlightQueueConfigProperties.java | 21 +++++ .../enums/SpotlightBatchStatus.java | 7 ++ .../enums/SpotlightSubmissionStatus.java | 9 ++ .../applybackend/model/SpotlightBatch.java | 49 ++++++++++ .../model/SpotlightSubmission.java | 55 +++++++++++ .../SpotlightSubmissionRepository.java | 10 ++ .../GrantMandatoryQuestionService.java | 6 +- .../service/SpotlightService.java | 54 +++++++++++ .../GrantMandatoryQuestionsController.java | 2 +- .../web/SubmissionController.java | 9 ++ src/main/resources/application.properties | 4 +- .../GrantMandatoryQuestionServiceTest.java | 13 +-- .../service/SpotlightServiceTest.java | 94 +++++++++++++++++++ .../web/SubmissionControllerTest.java | 69 ++++++++++---- 16 files changed, 396 insertions(+), 28 deletions(-) create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/config/SQSConfig.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/config/SpotlightQueueConfigProperties.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightBatchStatus.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightSubmissionStatus.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightBatch.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightSubmission.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/repository/SpotlightSubmissionRepository.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/service/SpotlightService.java create mode 100644 src/test/java/gov/cabinetoffice/gap/applybackend/service/SpotlightServiceTest.java diff --git a/pom.xml b/pom.xml index 3859ab7f..ac3ecd89 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,10 @@ commons-lang3 3.13.0 - + + com.amazonaws + aws-java-sdk-sqs + diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/config/SQSConfig.java b/src/main/java/gov/cabinetoffice/gap/applybackend/config/SQSConfig.java new file mode 100644 index 00000000..4b91c913 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/config/SQSConfig.java @@ -0,0 +1,17 @@ +package gov.cabinetoffice.gap.applybackend.config; + +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.AmazonSQSClientBuilder; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class SQSConfig { + + @Bean + AmazonSQS amazonSQS() { + return AmazonSQSClientBuilder.defaultClient(); + } +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/config/SpotlightQueueConfigProperties.java b/src/main/java/gov/cabinetoffice/gap/applybackend/config/SpotlightQueueConfigProperties.java new file mode 100644 index 00000000..1f0ccca2 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/config/SpotlightQueueConfigProperties.java @@ -0,0 +1,21 @@ +package gov.cabinetoffice.gap.applybackend.config; + +import lombok.*; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import javax.validation.constraints.NotNull; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Configuration +@ConfigurationProperties(prefix = "spotlight-queue") +public class SpotlightQueueConfigProperties { + + @NotNull + private String queueUrl; + +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightBatchStatus.java b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightBatchStatus.java new file mode 100644 index 00000000..01470938 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightBatchStatus.java @@ -0,0 +1,7 @@ +package gov.cabinetoffice.gap.applybackend.enums; + +public enum SpotlightBatchStatus { + QUEUED, + SUCCESS, + FAILURE +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightSubmissionStatus.java b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightSubmissionStatus.java new file mode 100644 index 00000000..bbb68a60 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/SpotlightSubmissionStatus.java @@ -0,0 +1,9 @@ +package gov.cabinetoffice.gap.applybackend.enums; + +public enum SpotlightSubmissionStatus { + QUEUED, + SENT, + SEND_ERROR, + GGIS_ERROR, + VALIDATION_ERROR +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightBatch.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightBatch.java new file mode 100644 index 00000000..472caf6f --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightBatch.java @@ -0,0 +1,49 @@ +package gov.cabinetoffice.gap.applybackend.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +@EntityListeners(AuditingEntityListener.class) +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Entity +@Table(name= "spotlight_batch") +public class SpotlightBatch { + + @Id + @GeneratedValue + private UUID id; + + @Column + private String status; + + @Column + private Instant lastSendAttempt; + + @Column + private int version; + + @Column(name = "created", nullable = false) + @Builder.Default + private Instant created = Instant.now(); + + @Column(name = "last_updated") + private Instant lastUpdated; + + @ManyToMany + @JoinTable( + name = "spotlight_batch_submission", + joinColumns = @JoinColumn(name = "spotlight_batch_id"), + inverseJoinColumns = @JoinColumn(name = "spotlight_submission_id")) + private List spotlightSubmissions; +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightSubmission.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightSubmission.java new file mode 100644 index 00000000..bd6dc30d --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/SpotlightSubmission.java @@ -0,0 +1,55 @@ +package gov.cabinetoffice.gap.applybackend.model; + +import gov.cabinetoffice.gap.applybackend.enums.SpotlightSubmissionStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +@EntityListeners(AuditingEntityListener.class) +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Entity +@Table(name= "spotlight_submission") +public class SpotlightSubmission { + + @Id + @GeneratedValue + private UUID id; + + @OneToOne + @JoinColumn(name = "grant_mandatory_questions_id") + private GrantMandatoryQuestions mandatoryQuestions; + + @ManyToOne + @JoinColumn(name = "grant_scheme") + private GrantScheme grantScheme; + + @Builder.Default + @Column + private String status = SpotlightSubmissionStatus.QUEUED.toString(); + + @Column(name = "last_send_attempt") + private Instant lastSendAttempt; + + @Column + private int version; + + @Column(name = "created", nullable = false) + @Builder.Default + private Instant created = Instant.now(); + + @Column(name = "last_updated", nullable = false) + private Instant lastUpdated; + + @ManyToMany + private List batches; +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/repository/SpotlightSubmissionRepository.java b/src/main/java/gov/cabinetoffice/gap/applybackend/repository/SpotlightSubmissionRepository.java new file mode 100644 index 00000000..dcf2fcc6 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/repository/SpotlightSubmissionRepository.java @@ -0,0 +1,10 @@ +package gov.cabinetoffice.gap.applybackend.repository; + +import gov.cabinetoffice.gap.applybackend.model.SpotlightSubmission; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface SpotlightSubmissionRepository extends JpaRepository { + +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java index ae78909f..6fb7e1bb 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java @@ -38,7 +38,11 @@ public GrantMandatoryQuestions getGrantMandatoryQuestionById(UUID id, String app return grantMandatoryQuestion.get(); } - public GrantMandatoryQuestions getGrantMandatoryQuestionBySubmissionId(UUID submissionId, String applicantSub) { + /* + TODO I think we should decouple access control and CRUD functionality. + These methods should not require an applicant ID to be passed in to confirm ownership. + */ + public GrantMandatoryQuestions getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(UUID submissionId, String applicantSub) { final Optional grantMandatoryQuestion = ofNullable(grantMandatoryQuestionRepository.findBySubmissionId(submissionId) .orElseThrow(() -> new NotFoundException(String.format("No Mandatory Question with submission id %s was found", submissionId)))); diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/SpotlightService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/SpotlightService.java new file mode 100644 index 00000000..2239749a --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/SpotlightService.java @@ -0,0 +1,54 @@ +package gov.cabinetoffice.gap.applybackend.service; + +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.model.MessageAttributeValue; +import com.amazonaws.services.sqs.model.SendMessageRequest; +import gov.cabinetoffice.gap.applybackend.config.SpotlightQueueConfigProperties; +import gov.cabinetoffice.gap.applybackend.model.GrantMandatoryQuestions; +import gov.cabinetoffice.gap.applybackend.model.GrantScheme; +import gov.cabinetoffice.gap.applybackend.model.SpotlightSubmission; +import gov.cabinetoffice.gap.applybackend.repository.SpotlightSubmissionRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.Clock; +import java.time.Instant; +import java.util.UUID; + +@Slf4j +@RequiredArgsConstructor +@Service +public class SpotlightService { + + public static final String SPOTLIGHT_SUBMISSION_ID = "spotlightSubmissionId"; + public static final int SPOTLIGHT_SUBSCRIPTION_VERSION = 1; + private final SpotlightSubmissionRepository spotlightSubmissionRepository; + private final AmazonSQS amazonSqs; + private final SpotlightQueueConfigProperties spotlightQueueProperties; + private final Clock clock; + + public void createSpotlightCheck(GrantMandatoryQuestions mandatoryQuestions, GrantScheme scheme) { + + // save a spotlight submission object to the database + final SpotlightSubmission spotlightSubmission = SpotlightSubmission.builder() + .mandatoryQuestions(mandatoryQuestions) + .grantScheme(scheme) + .version(SPOTLIGHT_SUBSCRIPTION_VERSION) + .lastUpdated(Instant.now(clock)) + .build(); + + final SpotlightSubmission savedSpotlightSubmission = spotlightSubmissionRepository.save(spotlightSubmission); + + // send that object to SQS for processing + final UUID messageId = UUID.randomUUID(); + + final SendMessageRequest messageRequest = new SendMessageRequest() + .withQueueUrl(spotlightQueueProperties.getQueueUrl()) + .withMessageGroupId(messageId.toString()) + .withMessageBody(savedSpotlightSubmission.getId().toString()) + .withMessageDeduplicationId(messageId.toString()); + + amazonSqs.sendMessage(messageRequest); + } +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java index 5945a9c2..51ffe383 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java @@ -81,7 +81,7 @@ public ResponseEntity getGrantMandatoryQuestionsBy }) public ResponseEntity getGrantMandatoryQuestionsBySubmissionId(@PathVariable final UUID submissionId) { final JwtPayload jwtPayload = (JwtPayload) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - final GrantMandatoryQuestions grantMandatoryQuestions = grantMandatoryQuestionService.getGrantMandatoryQuestionBySubmissionId(submissionId, jwtPayload.getSub()); + final GrantMandatoryQuestions grantMandatoryQuestions = grantMandatoryQuestionService.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submissionId, jwtPayload.getSub()); log.info("Mandatory question with ID {} has been retrieved.", grantMandatoryQuestions.getId()); final GetGrantMandatoryQuestionDto getGrantMandatoryQuestionBySubmissionDto = grantMandatoryQuestionMapper.mapGrantMandatoryQuestionToGetGrantMandatoryQuestionDTO(grantMandatoryQuestions); diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java b/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java index 2373cde3..0ad4e70c 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java @@ -36,6 +36,8 @@ public class SubmissionController { private final GrantApplicantService grantApplicantService; private final GrantAttachmentService grantAttachmentService; private final GrantApplicationService grantApplicationService; + private final SpotlightService spotlightService; + private final GrantMandatoryQuestionService mandatoryQuestionService; private final SecretAuthService secretAuthService; private final AttachmentService attachmentService; @@ -179,8 +181,15 @@ public ResponseEntity submitApplication(@RequestBody SubmitApplicationDt final JwtPayload jwtPayload = (JwtPayload) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); final GrantApplicant grantApplicant = grantApplicantService.getApplicantFromPrincipal(); final Submission submission = submissionService.getSubmissionFromDatabaseBySubmissionId(grantApplicant.getUserId(), applicationSubmission.getSubmissionId()); + final GrantScheme scheme = submission.getScheme(); + submissionService.submit(submission, grantApplicant, jwtPayload.getEmail()); + if (scheme.getVersion() > 1) { + final GrantMandatoryQuestions mandatoryQuestions = mandatoryQuestionService.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submission.getId(), grantApplicant.getUserId()); + spotlightService.createSpotlightCheck(mandatoryQuestions, scheme); + } + return ResponseEntity.ok("Submitted"); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index cf558eb3..4d9540e0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -43,4 +43,6 @@ feature.onelogin.enabled=false contentful.spaceId=a-space-id contentful.accessToken=an-access-token contentful.deliveryAPIAccessToken=an-access-token -contentful.environmentId=an-environment \ No newline at end of file +contentful.environmentId=an-environment + +spotlight-queue.queueUrl=a-sqs-queue-url \ No newline at end of file diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java index 13ac1966..8d34c078 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java @@ -124,7 +124,7 @@ void getGrantMandatoryQuestionBySubmissionId_ThrowsNotFoundException() { when(grantMandatoryQuestionRepository.findBySubmissionId(submissionId)) .thenThrow(NotFoundException.class); - assertThrows(NotFoundException.class, () -> serviceUnderTest.getGrantMandatoryQuestionBySubmissionId(submissionId, applicantUserId)); + assertThrows(NotFoundException.class, () -> serviceUnderTest.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submissionId, applicantUserId)); } @Test @@ -146,7 +146,7 @@ void getGrantMandatoryQuestionBySubmissionId_ThrowsForbiddenException() { when(grantMandatoryQuestionRepository.findBySubmissionId(submissionId)) .thenReturn(mandatoryQuestions); - assertThrows(ForbiddenException.class, () -> serviceUnderTest.getGrantMandatoryQuestionBySubmissionId(submissionId, invalidUserId)); + assertThrows(ForbiddenException.class, () -> serviceUnderTest.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submissionId, invalidUserId)); } @Test @@ -167,7 +167,7 @@ void getGrantMandatoryQuestionBySubmissionId_ReturnsExpectedMandatoryQuestions() when(grantMandatoryQuestionRepository.findBySubmissionId(submissionId)) .thenReturn(mandatoryQuestions); - final GrantMandatoryQuestions methodResponse = serviceUnderTest.getGrantMandatoryQuestionBySubmissionId(submissionId, applicantUserId); + final GrantMandatoryQuestions methodResponse = serviceUnderTest.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submissionId, applicantUserId); assertThat(methodResponse).isEqualTo(mandatoryQuestions.get()); } @@ -335,8 +335,9 @@ void createMandatoryQuestion_NullsCCandCH_ifTooLong() { verify(organisationProfileMapper).mapOrgProfileToGrantMandatoryQuestion(orgProfileWithLongCCandCH); verify(grantMandatoryQuestionRepository).save(any()); - assertThat(methodResponse.getCharityCommissionNumber()).isEqualTo(null); - assertThat(methodResponse.getCompaniesHouseNumber()).isEqualTo(null); + + assertThat(methodResponse.getCharityCommissionNumber()).isNull(); + assertThat(methodResponse.getCompaniesHouseNumber()).isNull(); } } @@ -438,7 +439,7 @@ void testGenerateNextPageUrl() { } @Test - public void testGenerateNextPageUrl_UrlNotInMapper() { + void testGenerateNextPageUrl_UrlNotInMapper() { final String url = "/any/url/thatIsNotInMapper"; final String expectedNextPageUrl = ""; diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/SpotlightServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/SpotlightServiceTest.java new file mode 100644 index 00000000..c8b167ce --- /dev/null +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/SpotlightServiceTest.java @@ -0,0 +1,94 @@ +package gov.cabinetoffice.gap.applybackend.service; + +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.model.SendMessageRequest; +import gov.cabinetoffice.gap.applybackend.config.SpotlightQueueConfigProperties; +import gov.cabinetoffice.gap.applybackend.enums.SpotlightSubmissionStatus; +import gov.cabinetoffice.gap.applybackend.model.GrantMandatoryQuestions; +import gov.cabinetoffice.gap.applybackend.model.GrantScheme; +import gov.cabinetoffice.gap.applybackend.model.SpotlightSubmission; +import gov.cabinetoffice.gap.applybackend.repository.SpotlightSubmissionRepository; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.util.UUID; + +@ExtendWith(MockitoExtension.class) +class SpotlightServiceTest { + + @Mock + private SpotlightSubmissionRepository spotlightSubmissionRepository; + + @Mock + private AmazonSQS amazonSqs; + + private SpotlightQueueConfigProperties spotlightQueueProperties; + private final String SPOOKY_DAY_OOOOO = "2023-10-31T12:00:00.00z"; + private final Clock clock = Clock.fixed(Instant.parse(SPOOKY_DAY_OOOOO), ZoneId.of("UTC")); + + private SpotlightService serviceUnderTest; + + @BeforeEach + void setup() { + spotlightQueueProperties = SpotlightQueueConfigProperties.builder() + .queueUrl("a-queue-url") + .build(); + + serviceUnderTest = new SpotlightService(spotlightSubmissionRepository, amazonSqs, spotlightQueueProperties, clock); + } + + @Test + void createSpotlightCheck_CreatesDatabaseEntry_AndAddsToQueue() { + + final GrantMandatoryQuestions mqs = GrantMandatoryQuestions.builder().build(); + final GrantScheme scheme = GrantScheme.builder() + .version(2) + .build(); + + final ArgumentCaptor spotlightSubmissionCaptor = ArgumentCaptor.forClass(SpotlightSubmission.class); + + final SpotlightSubmission submissionAfterSave = SpotlightSubmission.builder() + .id(UUID.randomUUID()) + .build(); + + when(spotlightSubmissionRepository.save(Mockito.any())) + .thenReturn(submissionAfterSave); + + final ArgumentCaptor sqsRequestCaptor = ArgumentCaptor.forClass(SendMessageRequest.class); + + serviceUnderTest.createSpotlightCheck(mqs, scheme); + + // Test that the spotlight submission has been saved to the relational database + verify(spotlightSubmissionRepository).save(spotlightSubmissionCaptor.capture()); + + final SpotlightSubmission spotlightSubmission = spotlightSubmissionCaptor.getValue(); + + assertThat(spotlightSubmission.getMandatoryQuestions()).isEqualTo(mqs); + assertThat(spotlightSubmission.getGrantScheme()).isEqualTo(scheme); + assertThat(spotlightSubmission.getVersion()).isEqualTo(SpotlightService.SPOTLIGHT_SUBSCRIPTION_VERSION); + assertThat(spotlightSubmission.getLastUpdated()).isEqualTo(Instant.now(clock)); + assertThat(spotlightSubmission.getStatus()).isEqualTo(SpotlightSubmissionStatus.QUEUED.toString()); + + + // Test that the spotlight submission has been sent to SQS + verify(amazonSqs).sendMessage(sqsRequestCaptor.capture()); + + final SendMessageRequest sqsRequest = sqsRequestCaptor.getValue(); + + assertThat(sqsRequest.getMessageBody()).isNotNull(); + assertThat(sqsRequest.getQueueUrl()).isEqualTo(spotlightQueueProperties.getQueueUrl()); + assertThat(sqsRequest.getMessageBody()).isEqualTo(submissionAfterSave.getId().toString()); + } +} \ No newline at end of file diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java index a5df3e37..356d5313 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java @@ -22,23 +22,8 @@ import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; import gov.cabinetoffice.gap.applybackend.exception.SubmissionAlreadyCreatedException; import gov.cabinetoffice.gap.applybackend.exception.UnauthorizedException; -import gov.cabinetoffice.gap.applybackend.model.ApplicationDefinition; -import gov.cabinetoffice.gap.applybackend.model.GrantApplicant; -import gov.cabinetoffice.gap.applybackend.model.GrantApplicantOrganisationProfile; -import gov.cabinetoffice.gap.applybackend.model.GrantApplication; -import gov.cabinetoffice.gap.applybackend.model.GrantAttachment; -import gov.cabinetoffice.gap.applybackend.model.GrantScheme; -import gov.cabinetoffice.gap.applybackend.model.Submission; -import gov.cabinetoffice.gap.applybackend.model.SubmissionDefinition; -import gov.cabinetoffice.gap.applybackend.model.SubmissionQuestion; -import gov.cabinetoffice.gap.applybackend.model.SubmissionQuestionValidation; -import gov.cabinetoffice.gap.applybackend.model.SubmissionSection; -import gov.cabinetoffice.gap.applybackend.service.AttachmentService; -import gov.cabinetoffice.gap.applybackend.service.GrantApplicantService; -import gov.cabinetoffice.gap.applybackend.service.GrantApplicationService; -import gov.cabinetoffice.gap.applybackend.service.GrantAttachmentService; -import gov.cabinetoffice.gap.applybackend.service.SecretAuthService; -import gov.cabinetoffice.gap.applybackend.service.SubmissionService; +import gov.cabinetoffice.gap.applybackend.model.*; +import gov.cabinetoffice.gap.applybackend.service.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -287,6 +272,12 @@ class SubmissionControllerTest { @Mock private AttachmentService attachmentService; + @Mock + private SpotlightService spotlightService; + + @Mock + private GrantMandatoryQuestionService mandatoryQuestionService; + final String CHRISTMAS_2022_MIDDAY = "2022-12-25T12:00:00.00z"; final Clock clock = Clock.fixed(Instant.parse(CHRISTMAS_2022_MIDDAY), ZoneId.of("UTC")); @@ -294,7 +285,8 @@ class SubmissionControllerTest { @BeforeEach void setup() { - controllerUnderTest = new SubmissionController(submissionService, grantApplicantService, grantAttachmentService, grantApplicationService, secretAuthService, attachmentService, clock); + // TODO holy argument list batman, this class needs broken down!§§§ + controllerUnderTest = new SubmissionController(submissionService, grantApplicantService, grantAttachmentService, grantApplicationService, spotlightService, mandatoryQuestionService, secretAuthService, attachmentService, clock); when(securityContext.getAuthentication()).thenReturn(authentication); SecurityContextHolder.setContext(securityContext); JwtPayload jwtPayload = JwtPayload.builder().sub(String.valueOf(APPLICANT_USER_ID)).build(); @@ -464,6 +456,47 @@ void submitApplication_isSuccessfulAndReturnsExpectedResponse() { assertThat(response.getBody()).isEqualTo("Submitted"); } + @Test + void submitApplication_isSuccessfulAndReturnsExpectedResponse_ForV2Schemes() { + + // set up the V2 scheme + submission.getScheme().setVersion(2); + + final String emailAddress = "test@email.com"; + final GrantApplicant grantApplicant = GrantApplicant.builder().userId(APPLICANT_USER_ID).id(1).build(); + final SubmitApplicationDto submitApplication = SubmitApplicationDto.builder() + .submissionId(SUBMISSION_ID) + .build(); + final JwtPayload jwtPayload = JwtPayload.builder().sub(APPLICANT_USER_ID).email(emailAddress).build(); + + final GrantMandatoryQuestions mandatoryQuestions = GrantMandatoryQuestions.builder() + .build(); + + when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()) + .thenReturn(jwtPayload); + + when(submissionService.getSubmissionFromDatabaseBySubmissionId(APPLICANT_USER_ID, SUBMISSION_ID)) + .thenReturn(submission); + + when(grantApplicantService.getApplicantFromPrincipal()) + .thenReturn(grantApplicant); + + when(mandatoryQuestionService.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submission.getId(), grantApplicant.getUserId())) + .thenReturn(mandatoryQuestions); + + + final ResponseEntity response = controllerUnderTest.submitApplication(submitApplication); + + + verify(mandatoryQuestionService).getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submission.getId(), grantApplicant.getUserId()); + verify(spotlightService).createSpotlightCheck(mandatoryQuestions, submission.getScheme()); + verify(submissionService).getSubmissionFromDatabaseBySubmissionId(APPLICANT_USER_ID, SUBMISSION_ID); + verify(submissionService).submit(submission, grantApplicant, emailAddress); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo("Submitted"); + } + @Test void submitApplication_ThrowsNotFoundException_IfSubmissionNotFound() { From 1206a3f8262cd397238c1fc337046e07e7efaffd Mon Sep 17 00:00:00 2001 From: Conor Fayle <141320269+ConorFayleTCO@users.noreply.github.com> Date: Fri, 17 Nov 2023 11:32:31 +0000 Subject: [PATCH 02/19] GAP-2251: MQ Conditional Logic - Choose organisation type (#56) * GAP-2251 | change order of MQs, add conditional logic for non-limited company and individual * GAP-2251 | remove public for test func * GAP-2251 | fix test --------- Co-authored-by: conor --- .../model/GrantMandatoryQuestions.java | 10 +-- .../GrantMandatoryQuestionService.java | 17 +++-- .../GrantMandatoryQuestionsController.java | 2 +- src/main/resources/banner.txt | 8 ++ .../GrantMandatoryQuestionServiceTest.java | 76 ++++++++++++++++++- ...GrantMandatoryQuestionsControllerTest.java | 7 +- 6 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 src/main/resources/banner.txt diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantMandatoryQuestions.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantMandatoryQuestions.java index 58d0a553..8b62ee01 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantMandatoryQuestions.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantMandatoryQuestions.java @@ -51,6 +51,11 @@ public class GrantMandatoryQuestions extends BaseEntity { @JoinColumn(name = "submission_id", referencedColumnName = "id") private Submission submission; + @Column(name = "org_type") + @Enumerated(EnumType.STRING) + @ColumnTransformer(write = "?::grant_mandatory_question_type") + private GrantMandatoryQuestionOrgType orgType; + @Column(name = "name") private String name; @@ -69,11 +74,6 @@ public class GrantMandatoryQuestions extends BaseEntity { @Column(name = "postcode") private String postcode; - @Column(name = "org_type") - @Enumerated(EnumType.STRING) - @ColumnTransformer(write = "?::grant_mandatory_question_type") - private GrantMandatoryQuestionOrgType orgType; - @Column(name = "companies_house_number") private String companiesHouseNumber; diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java index 6fb7e1bb..1b98e914 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java @@ -2,6 +2,7 @@ import gov.cabinetoffice.gap.applybackend.config.properties.EnvironmentProperties; import gov.cabinetoffice.gap.applybackend.constants.MandatoryQuestionConstants; +import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionOrgType; import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionStatus; import gov.cabinetoffice.gap.applybackend.enums.SubmissionQuestionResponseType; import gov.cabinetoffice.gap.applybackend.enums.SubmissionSectionStatus; @@ -105,14 +106,20 @@ public GrantMandatoryQuestions updateMandatoryQuestion(GrantMandatoryQuestions g .orElseThrow(() -> new NotFoundException(String.format("No Mandatory Question with id %s was found", grantMandatoryQuestions.getId()))); } - public String generateNextPageUrl(String url, UUID mandatoryQuestionId) { + public String generateNextPageUrl(String url, UUID mandatoryQuestionId, String applicantSub) { + final GrantMandatoryQuestions mqs = getGrantMandatoryQuestionById(mandatoryQuestionId, applicantSub); final Map mapper = new HashMap<>(); String mandatoryQuestionsUrlStart = "/mandatory-questions/" + mandatoryQuestionId; + mapper.put("organisation-type", mandatoryQuestionsUrlStart + "/organisation-name"); mapper.put("organisation-name", mandatoryQuestionsUrlStart + "/organisation-address"); - mapper.put("organisation-address", mandatoryQuestionsUrlStart + "/organisation-type"); - mapper.put("organisation-type", mandatoryQuestionsUrlStart + "/organisation-companies-house-number"); - mapper.put("organisation-companies-house-number", mandatoryQuestionsUrlStart + "/organisation-charity-commission-number"); - mapper.put("organisation-charity-commission-number", mandatoryQuestionsUrlStart + "/organisation-funding-amount"); + if (mqs.getOrgType() == GrantMandatoryQuestionOrgType.NON_LIMITED_COMPANY + || mqs.getOrgType() == GrantMandatoryQuestionOrgType.INDIVIDUAL) { + mapper.put("organisation-address", mandatoryQuestionsUrlStart + "/organisation-funding-amount"); + } else { + mapper.put("organisation-address", mandatoryQuestionsUrlStart + "/organisation-companies-house-number"); + mapper.put("organisation-companies-house-number", mandatoryQuestionsUrlStart + "/organisation-charity-commission-number"); + mapper.put("organisation-charity-commission-number", mandatoryQuestionsUrlStart + "/organisation-funding-amount"); + } mapper.put("organisation-funding-amount", mandatoryQuestionsUrlStart + "/organisation-funding-location"); mapper.put("organisation-funding-location", mandatoryQuestionsUrlStart + "/organisation-summary"); diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java index 51ffe383..07fa77b6 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsController.java @@ -154,6 +154,6 @@ public ResponseEntity updateMandatoryQuestion(@PathVariable final UUID m log.info("Mandatory question with ID {} has been updated.", grantMandatoryQuestions.getId()); - return ResponseEntity.ok(grantMandatoryQuestionService.generateNextPageUrl(url, mandatoryQuestionId)); + return ResponseEntity.ok(grantMandatoryQuestionService.generateNextPageUrl(url, mandatoryQuestionId, jwtPayload.getSub())); } } diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 00000000..8fccb5a9 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,8 @@ +,------.,--. ,--. ,----. ,--. +| .---'`--',--,--, ,-| | ,--,--. ' .-./ ,--.--.,--,--.,--,--, ,-' '-. +| `--, ,--.| \' .-. | ' ,-. | | | .---.| .--' ,-. || \'-. .-' +| |` | || || |\ `-' | \ '-' | ' '--' || | \ '-' || || | | | +`--' `--'`--''--' `---' `--`--' `------' `--' `--`--'`--''--' `--' + +${info.app.name} (${info.app.version}) +Powered by Spring Boot ${spring-boot.version} diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java index 8d34c078..73362024 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java @@ -430,20 +430,90 @@ void updateMandatoryQuestion_UpdatesExpectedMandatoryQuestionsAndSetsGapIdBySubm class generateNextPageUrl { @Test void testGenerateNextPageUrl() { + final GrantApplicant applicant = GrantApplicant + .builder() + .userId(applicantUserId) + .build(); + final GrantMandatoryQuestions grantMandatoryQuestions = GrantMandatoryQuestions + .builder() + .id(MANDATORY_QUESTION_ID) + .createdBy(applicant) + .build(); + when(grantMandatoryQuestionRepository.findById(MANDATORY_QUESTION_ID)) + .thenReturn(Optional.of(grantMandatoryQuestions)); + + final String url = "/any/url/organisation-type?some-param=some-value"; + final String expectedNextPageUrl = "/mandatory-questions/" + MANDATORY_QUESTION_ID + "/organisation-name"; + + final String nextPageUrl = serviceUnderTest.generateNextPageUrl(url, MANDATORY_QUESTION_ID, applicantUserId); + + assertThat(nextPageUrl).isEqualTo(expectedNextPageUrl); + } + + @Test + void testGenerateNextPageUrlForSkippingCompaniesHouseAndCharityCommission() { + final GrantApplicant applicant = GrantApplicant + .builder() + .userId(applicantUserId) + .build(); + final GrantMandatoryQuestions grantMandatoryQuestions = GrantMandatoryQuestions + .builder() + .id(MANDATORY_QUESTION_ID) + .createdBy(applicant) + .orgType(GrantMandatoryQuestionOrgType.INDIVIDUAL) + .build(); + when(grantMandatoryQuestionRepository.findById(MANDATORY_QUESTION_ID)) + .thenReturn(Optional.of(grantMandatoryQuestions)); + final String url = "/any/url/organisation-address?some-param=some-value"; - final String expectedNextPageUrl = "/mandatory-questions/" + MANDATORY_QUESTION_ID + "/organisation-type"; + final String expectedNextPageUrl = "/mandatory-questions/" + MANDATORY_QUESTION_ID + "/organisation-funding-amount"; - final String nextPageUrl = serviceUnderTest.generateNextPageUrl(url, MANDATORY_QUESTION_ID); + final String nextPageUrl = serviceUnderTest.generateNextPageUrl(url, MANDATORY_QUESTION_ID, applicantUserId); + + assertThat(nextPageUrl).isEqualTo(expectedNextPageUrl); + } + + @Test + void testGenerateNextPageUrlForNotSkippingCompaniesHouseAndCharityCommission() { + final GrantApplicant applicant = GrantApplicant + .builder() + .userId(applicantUserId) + .build(); + final GrantMandatoryQuestions grantMandatoryQuestions = GrantMandatoryQuestions + .builder() + .id(MANDATORY_QUESTION_ID) + .createdBy(applicant) + .orgType(GrantMandatoryQuestionOrgType.CHARITY) + .build(); + when(grantMandatoryQuestionRepository.findById(MANDATORY_QUESTION_ID)) + .thenReturn(Optional.of(grantMandatoryQuestions)); + + final String url = "/any/url/organisation-address?some-param=some-value"; + final String expectedNextPageUrl = "/mandatory-questions/" + MANDATORY_QUESTION_ID + "/organisation-companies-house-number"; + + final String nextPageUrl = serviceUnderTest.generateNextPageUrl(url, MANDATORY_QUESTION_ID, applicantUserId); assertThat(nextPageUrl).isEqualTo(expectedNextPageUrl); } @Test void testGenerateNextPageUrl_UrlNotInMapper() { + final GrantApplicant applicant = GrantApplicant + .builder() + .userId(applicantUserId) + .build(); + final GrantMandatoryQuestions grantMandatoryQuestions = GrantMandatoryQuestions + .builder() + .id(MANDATORY_QUESTION_ID) + .createdBy(applicant) + .build(); + when(grantMandatoryQuestionRepository.findById(MANDATORY_QUESTION_ID)) + .thenReturn(Optional.of(grantMandatoryQuestions)); + final String url = "/any/url/thatIsNotInMapper"; final String expectedNextPageUrl = ""; - final String nextPageUrl = serviceUnderTest.generateNextPageUrl(url, MANDATORY_QUESTION_ID); + final String nextPageUrl = serviceUnderTest.generateNextPageUrl(url, MANDATORY_QUESTION_ID, applicantUserId); assertThat(nextPageUrl).isEqualTo(expectedNextPageUrl); } diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsControllerTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsControllerTest.java index 145dd2af..2cc6e8f8 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsControllerTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantMandatoryQuestionsControllerTest.java @@ -194,7 +194,7 @@ void updateMandatoryQuestion_UpdatesExpectedFields_AndSavesChanges() { when(grantMandatoryQuestionService.updateMandatoryQuestion(mandatoryQuestions, applicant)) .thenReturn(mandatoryQuestions); - when(grantMandatoryQuestionService.generateNextPageUrl("url", MANDATORY_QUESTION_ID)) + when(grantMandatoryQuestionService.generateNextPageUrl("url", MANDATORY_QUESTION_ID, jwtPayload.getSub())) .thenReturn("nextPageUrl"); @@ -235,7 +235,7 @@ void updateMandatoryQuestion_AddsSubmissionToMandatoryQuestions_IfSubmissionIdPr when(grantMandatoryQuestionService.updateMandatoryQuestion(mandatoryQuestions, applicant)) .thenReturn(mandatoryQuestions); - when(grantMandatoryQuestionService.generateNextPageUrl("url", MANDATORY_QUESTION_ID)) + when(grantMandatoryQuestionService.generateNextPageUrl("url", MANDATORY_QUESTION_ID, jwtPayload.getSub())) .thenReturn("nextPageUrl"); when(submissionService.getSubmissionFromDatabaseBySubmissionId(jwtPayload.getSub(), submissionId)) @@ -279,10 +279,9 @@ void updateMandatoryQuestion_SetsStatusToComplete() { when(grantMandatoryQuestionService.updateMandatoryQuestion(mandatoryQuestions, applicant)) .thenReturn(mandatoryQuestions); - when(grantMandatoryQuestionService.generateNextPageUrl("url", MANDATORY_QUESTION_ID)) + when(grantMandatoryQuestionService.generateNextPageUrl("url", MANDATORY_QUESTION_ID, jwtPayload.getSub())) .thenReturn("nextPageUrl"); - final ResponseEntity methodResponse = controllerUnderTest.updateMandatoryQuestion(MANDATORY_QUESTION_ID, updateDto, "url"); verify(grantMandatoryQuestionService).addMandatoryQuestionsToSubmissionObject(mandatoryQuestions); From 4a207fabeebaef1b0f0aa6ad8b9dbae51fb3afc9 Mon Sep 17 00:00:00 2001 From: GavCookCO <99668051+GavCookCO@users.noreply.github.com> Date: Fri, 17 Nov 2023 12:50:17 +0000 Subject: [PATCH 03/19] Adding an org type check for sending to spotlight (#57) --- .../web/SubmissionController.java | 12 +++- .../web/SubmissionControllerTest.java | 57 ++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java b/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java index 0ad4e70c..5afa0101 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/web/SubmissionController.java @@ -4,6 +4,7 @@ import gov.cabinetoffice.gap.applybackend.constants.APIConstants; import gov.cabinetoffice.gap.applybackend.dto.api.*; import gov.cabinetoffice.gap.applybackend.enums.GrantAttachmentStatus; +import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionOrgType; import gov.cabinetoffice.gap.applybackend.enums.SubmissionSectionStatus; import gov.cabinetoffice.gap.applybackend.exception.AttachmentException; import gov.cabinetoffice.gap.applybackend.exception.GrantApplicationNotPublishedException; @@ -187,12 +188,21 @@ public ResponseEntity submitApplication(@RequestBody SubmitApplicationDt if (scheme.getVersion() > 1) { final GrantMandatoryQuestions mandatoryQuestions = mandatoryQuestionService.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submission.getId(), grantApplicant.getUserId()); - spotlightService.createSpotlightCheck(mandatoryQuestions, scheme); + final boolean shouldSendToSpotlight = !isOrganisationIndividualOrOther(mandatoryQuestions); + + if (shouldSendToSpotlight) { + spotlightService.createSpotlightCheck(mandatoryQuestions, scheme); + } } return ResponseEntity.ok("Submitted"); } + private boolean isOrganisationIndividualOrOther(GrantMandatoryQuestions mandatoryQuestions) { + return mandatoryQuestions.getOrgType().equals(GrantMandatoryQuestionOrgType.INDIVIDUAL) || + mandatoryQuestions.getOrgType().equals(GrantMandatoryQuestionOrgType.OTHER); + } + @PostMapping("/createSubmission/{applicationId}") public ResponseEntity createApplication(@PathVariable final int applicationId) throws JsonProcessingException { final String applicantId = getUserIdFromSecurityContext(); diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java index 356d5313..7d940d47 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/web/SubmissionControllerTest.java @@ -13,10 +13,7 @@ import gov.cabinetoffice.gap.applybackend.dto.api.SubmissionReviewBodyDto; import gov.cabinetoffice.gap.applybackend.dto.api.SubmitApplicationDto; import gov.cabinetoffice.gap.applybackend.dto.api.UpdateAttachmentDto; -import gov.cabinetoffice.gap.applybackend.enums.GrantAttachmentStatus; -import gov.cabinetoffice.gap.applybackend.enums.SubmissionQuestionResponseType; -import gov.cabinetoffice.gap.applybackend.enums.SubmissionSectionStatus; -import gov.cabinetoffice.gap.applybackend.enums.SubmissionStatus; +import gov.cabinetoffice.gap.applybackend.enums.*; import gov.cabinetoffice.gap.applybackend.exception.AttachmentException; import gov.cabinetoffice.gap.applybackend.exception.GrantApplicationNotPublishedException; import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; @@ -29,9 +26,12 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Mockito; +import static org.mockito.Mockito.*; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -56,11 +56,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class SubmissionControllerTest { @@ -470,6 +465,7 @@ void submitApplication_isSuccessfulAndReturnsExpectedResponse_ForV2Schemes() { final JwtPayload jwtPayload = JwtPayload.builder().sub(APPLICANT_USER_ID).email(emailAddress).build(); final GrantMandatoryQuestions mandatoryQuestions = GrantMandatoryQuestions.builder() + .orgType(GrantMandatoryQuestionOrgType.LIMITED_COMPANY) .build(); when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()) @@ -497,6 +493,49 @@ void submitApplication_isSuccessfulAndReturnsExpectedResponse_ForV2Schemes() { assertThat(response.getBody()).isEqualTo("Submitted"); } + @ParameterizedTest + @EnumSource(value = GrantMandatoryQuestionOrgType.class, names = {"INDIVIDUAL", "OTHER"}) + void submitApplication_DoesNotCreateSpotlightCheck_ForIndividualsOrOther_ForV2Schemes(GrantMandatoryQuestionOrgType orgType) { + + // set up the V2 scheme + submission.getScheme().setVersion(2); + + final String emailAddress = "test@email.com"; + final GrantApplicant grantApplicant = GrantApplicant.builder().userId(APPLICANT_USER_ID).id(1).build(); + final SubmitApplicationDto submitApplication = SubmitApplicationDto.builder() + .submissionId(SUBMISSION_ID) + .build(); + final JwtPayload jwtPayload = JwtPayload.builder().sub(APPLICANT_USER_ID).email(emailAddress).build(); + + final GrantMandatoryQuestions mandatoryQuestions = GrantMandatoryQuestions.builder() + .orgType(orgType) + .build(); + + when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()) + .thenReturn(jwtPayload); + + when(submissionService.getSubmissionFromDatabaseBySubmissionId(APPLICANT_USER_ID, SUBMISSION_ID)) + .thenReturn(submission); + + when(grantApplicantService.getApplicantFromPrincipal()) + .thenReturn(grantApplicant); + + when(mandatoryQuestionService.getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submission.getId(), grantApplicant.getUserId())) + .thenReturn(mandatoryQuestions); + + + final ResponseEntity response = controllerUnderTest.submitApplication(submitApplication); + + + verify(mandatoryQuestionService).getGrantMandatoryQuestionBySubmissionIdAndApplicantSub(submission.getId(), grantApplicant.getUserId()); + verify(spotlightService, never()).createSpotlightCheck(Mockito.any(), Mockito.any()); + verify(submissionService).getSubmissionFromDatabaseBySubmissionId(APPLICANT_USER_ID, SUBMISSION_ID); + verify(submissionService).submit(submission, grantApplicant, emailAddress); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo("Submitted"); + } + @Test void submitApplication_ThrowsNotFoundException_IfSubmissionNotFound() { From f0a195986d1e50c301dac4da5d9b4ce64776fc04 Mon Sep 17 00:00:00 2001 From: Conor Fayle <141320269+ConorFayleTCO@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:59:56 +0000 Subject: [PATCH 04/19] GAP-2249: Conditional Logic - Changes to submission for applicant types (#58) * GAP-2249 | reorder submission questions, conditionally remove questions if individual or non-limited company, conditionally change title if individual * GAP-2249 | use org types from constants --------- Co-authored-by: conor --- .../constants/MandatoryQuestionConstants.java | 31 ++-- .../enums/GrantApplicantOrganisationType.java | 2 +- .../enums/GrantMandatoryQuestionOrgType.java | 2 +- .../GrantMandatoryQuestionService.java | 59 +++++--- .../GrantMandatoryQuestionServiceTest.java | 133 +++++++++++++++++- 5 files changed, 195 insertions(+), 32 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/constants/MandatoryQuestionConstants.java b/src/main/java/gov/cabinetoffice/gap/applybackend/constants/MandatoryQuestionConstants.java index 8f2bbdc8..f47d31e6 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/constants/MandatoryQuestionConstants.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/constants/MandatoryQuestionConstants.java @@ -1,14 +1,18 @@ package gov.cabinetoffice.gap.applybackend.constants; +import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionOrgType; + public class MandatoryQuestionConstants { public static final String ORGANISATION_DETAILS_SECTION_ID = "ORGANISATION_DETAILS"; - public static final String ORGANISATION_DETAILS_SECTION_TITLE = "Your Organisation"; + public static final String ORGANISATION_DETAILS_SECTION_TITLE = "Your organisation"; + public static final String ORGANISATION_INDIVIDUAL_DETAILS_SECTION_TITLE = "Your details"; public static final String FUNDING_DETAILS_SECTION_ID = "FUNDING_DETAILS"; public static final String FUNDING_DETAILS_SECTION_TITLE = "Funding"; public static final String APPLICANT_ORG_NAME_TITLE = "Enter the name of your organisation"; + public static final String APPLICANT_SUBMISSION_ORG_NAME_TITLE = "Name"; public static final String APPLICANT_ORG_NAME_PROFILE_FIELD = "ORG_NAME"; public static final String APPLICANT_ORG_NAME_HINT = "This is the official name of your organisation. It could be the name that is registered with Companies House or the Charities Commission"; public static final String APPLICANT_ORG_NAME_ADMIN_SUMMARY = "organisation legal name"; @@ -16,35 +20,38 @@ public class MandatoryQuestionConstants { public static final int APPLICANT_ORG_NAME_MAX_LENGTH = 250; public static final String APPLICANT_TYPE_TITLE = "Choose your organisation type"; + public static final String APPLICANT_SUBMISSION_TYPE_TITLE = "Type of organisation"; + public static final String APPLICANT_INDIVIDUAL_SUBMISSION_TYPE_TITLE = "Type of application"; public static final String APPLICANT_TYPE_PROFILE_FIELD = "ORG_TYPE"; public static final String APPLICANT_TYPE_HINT_TEXT = "Choose the option that best describes your organisation"; public static final String APPLICANT_TYPE_ADMIN_SUMMARY = "organisation type (e.g. limited company)"; public static final String[] APPLICANT_TYPE_OPTIONS = new String[] { - "Limited company", - "Non-limited company", - "Registered charity", - "Unregistered charity", - "Other", - "Charity", - "I am applying as an Individual", + GrantMandatoryQuestionOrgType.LIMITED_COMPANY.toString(), + GrantMandatoryQuestionOrgType.NON_LIMITED_COMPANY.toString(), + GrantMandatoryQuestionOrgType.REGISTERED_CHARITY.toString(), + GrantMandatoryQuestionOrgType.UNREGISTERED_CHARITY.toString(), + GrantMandatoryQuestionOrgType.OTHER.toString(), + GrantMandatoryQuestionOrgType.CHARITY.toString(), + GrantMandatoryQuestionOrgType.INDIVIDUAL.toString(), }; public static final String ORGANISATION_ADDRESS_TITLE = "Enter your organisations address"; + public static final String ORGANISATION_SUBMISSION_ADDRESS_TITLE = "Address"; public static final String ORGANISATION_ADDRESS_PROFILE_FIELD = "ORG_ADDRESS"; public static final String ORGANISATION_ADDRESS_ADMIN_SUMMARY = "Enter your organisations address"; - public static final String CHARITY_COMMISSION_NUMBER_TITLE = "Enter your Charity Commission number (if you have one)"; + public static final String CHARITY_COMMISSION_NUMBER_TITLE = "Enter your Charity Commission number"; public static final String CHARITY_COMMISSION_NUMBER_PROFILE_FIELD = "ORG_CHARITY_NUMBER"; public static final String CHARITY_COMMISSION_NUMBER_HINT_TEXT = "Funding organisation might use this to identify your organisation when you apply for a grant. It might also be used to check your organisation is legitimate."; - public static final String CHARITY_COMMISSION_NUMBER_ADMIN_SUMMARY = "Charity Commission number (if applicable)"; + public static final String CHARITY_COMMISSION_NUMBER_ADMIN_SUMMARY = "Charity Commission number"; public static final int CHARITY_COMMISSION_NUMBER_MIN_LENGTH = 2; public static final int CHARITY_COMMISSION_NUMBER_MAX_LENGTH = 15; public static final String CHARITY_COMMISSION_NUMBER_VALID_INPUT = "alphanumeric-nospace"; - public static final String COMPANIES_HOUSE_NUMBER_TITLE = "Enter your Companies House number (if you have one)"; + public static final String COMPANIES_HOUSE_NUMBER_TITLE = "Enter your Companies House number"; public static final String COMPANIES_HOUSE_NUMBER_PROFILE_FIELD = "ORG_COMPANIES_HOUSE"; public static final String COMPANIES_HOUSE_NUMBER_HINT_TEXT = "Funding organisation might use this to identify your organisation when you apply for a grant. It might also be used to check your organisation is legitimate."; - public static final String COMPANIES_HOUSE_NUMBER_ADMIN_SUMMARY = "Companies House number (if applicable)"; + public static final String COMPANIES_HOUSE_NUMBER_ADMIN_SUMMARY = "Companies House number"; public static final int COMPANIES_HOUSE_NUMBER_MIN_LENGTH = 2; public static final int COMPANIES_HOUSE_NUMBER_MAX_LENGTH = 8; public static final String COMPANIES_HOUSE_NUMBER_VALID_INPUT = "alphanumeric-nospace"; diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantApplicantOrganisationType.java b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantApplicantOrganisationType.java index c0789e2a..1b5d26eb 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantApplicantOrganisationType.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantApplicantOrganisationType.java @@ -9,7 +9,7 @@ public enum GrantApplicantOrganisationType { REGISTERED_CHARITY("Registered charity"), UNREGISTERED_CHARITY("Unregistered charity"), CHARITY("Charity"), - INDIVIDUAL("I am applying as an Individual"), + INDIVIDUAL("I am applying as an individual"), OTHER("Other"); private String name; diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionOrgType.java b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionOrgType.java index ee7a4c8e..173c9101 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionOrgType.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionOrgType.java @@ -8,7 +8,7 @@ public enum GrantMandatoryQuestionOrgType { REGISTERED_CHARITY("Registered charity"), UNREGISTERED_CHARITY("Unregistered charity"), CHARITY("Charity"), - INDIVIDUAL("I am applying as an Individual"), + INDIVIDUAL("I am applying as an individual"), OTHER("Other"); private String name; diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java index 1b98e914..a7510f4c 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java @@ -161,22 +161,45 @@ public void addMandatoryQuestionsToSubmissionObject(final GrantMandatoryQuestion } public SubmissionSection buildOrganisationDetailsSubmissionSection(final GrantMandatoryQuestions mandatoryQuestions, final SubmissionSectionStatus sectionStatus) { - final SubmissionQuestion organisationName = mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_NAME.toString(), mandatoryQuestions); - final SubmissionQuestion applicantType = mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_TYPE.toString(), mandatoryQuestions); - final SubmissionQuestion organisationAddress = mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_ADDRESS.toString(), mandatoryQuestions); - final SubmissionQuestion charityCommissionNumber = mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_CHARITY_NUMBER.toString(), mandatoryQuestions); - final SubmissionQuestion companiesHouseNumber = mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_COMPANIES_HOUSE.toString(), mandatoryQuestions); + final boolean isNonLimitedCompany = Objects.equals(mandatoryQuestions.getOrgType().toString(), GrantMandatoryQuestionOrgType.NON_LIMITED_COMPANY.toString()); + final boolean isIndividual = Objects.equals(mandatoryQuestions.getOrgType().toString(), GrantMandatoryQuestionOrgType.INDIVIDUAL.toString()); + final String sectionTitle = isIndividual + ? MandatoryQuestionConstants.ORGANISATION_INDIVIDUAL_DETAILS_SECTION_TITLE + : MandatoryQuestionConstants.ORGANISATION_DETAILS_SECTION_TITLE; + final SubmissionQuestion organisationName = mandatoryQuestionToSubmissionQuestion( + MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_NAME.toString(), + mandatoryQuestions + ); + final SubmissionQuestion applicantType = mandatoryQuestionToSubmissionQuestion( + MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_TYPE.toString(), + mandatoryQuestions + ); + final SubmissionQuestion organisationAddress = mandatoryQuestionToSubmissionQuestion( + MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_ADDRESS.toString(), + mandatoryQuestions + ); + + List questions = new ArrayList<>(); + questions.add(applicantType); + questions.add(organisationName); + questions.add(organisationAddress); + if (!isIndividual && !isNonLimitedCompany) { + final SubmissionQuestion charityCommissionNumber = mandatoryQuestionToSubmissionQuestion( + MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_CHARITY_NUMBER.toString(), + mandatoryQuestions + ); + final SubmissionQuestion companiesHouseNumber = mandatoryQuestionToSubmissionQuestion( + MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_COMPANIES_HOUSE.toString(), + mandatoryQuestions + ); + questions.add(charityCommissionNumber); + questions.add(companiesHouseNumber); + } return SubmissionSection.builder() .sectionId(MandatoryQuestionConstants.ORGANISATION_DETAILS_SECTION_ID) - .sectionTitle(MandatoryQuestionConstants.ORGANISATION_DETAILS_SECTION_TITLE) - .questions(List.of( - organisationName, - applicantType, - organisationAddress, - charityCommissionNumber, - companiesHouseNumber - )) + .sectionTitle(sectionTitle) + .questions(questions) .sectionStatus(sectionStatus) .build(); } @@ -230,7 +253,7 @@ private SubmissionQuestion buildOrganisationNameQuestion(final GrantMandatoryQue return SubmissionQuestion.builder() .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_NAME.toString()) - .fieldTitle(MandatoryQuestionConstants.APPLICANT_ORG_NAME_TITLE) + .fieldTitle(MandatoryQuestionConstants.APPLICANT_SUBMISSION_ORG_NAME_TITLE) .profileField(MandatoryQuestionConstants.APPLICANT_ORG_NAME_PROFILE_FIELD) .hintText(MandatoryQuestionConstants.APPLICANT_ORG_NAME_HINT) .adminSummary(MandatoryQuestionConstants.APPLICANT_ORG_NAME_ADMIN_SUMMARY) @@ -245,9 +268,13 @@ private SubmissionQuestion buildApplicationTypeQuestion(final GrantMandatoryQues .mandatory(true) .build(); + final String title = Objects.equals(mandatoryQuestions.getOrgType().toString(), GrantMandatoryQuestionOrgType.INDIVIDUAL.toString()) + ? MandatoryQuestionConstants.APPLICANT_INDIVIDUAL_SUBMISSION_TYPE_TITLE + : MandatoryQuestionConstants.APPLICANT_SUBMISSION_TYPE_TITLE; + return SubmissionQuestion.builder() .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_TYPE.toString()) - .fieldTitle(MandatoryQuestionConstants.APPLICANT_TYPE_TITLE) + .fieldTitle(title) .profileField(MandatoryQuestionConstants.APPLICANT_TYPE_PROFILE_FIELD) .hintText(MandatoryQuestionConstants.APPLICANT_ORG_NAME_HINT) .adminSummary(MandatoryQuestionConstants.APPLICANT_TYPE_ADMIN_SUMMARY) @@ -353,7 +380,7 @@ private SubmissionQuestion buildOrganisationAddressQuestion(final GrantMandatory return SubmissionQuestion.builder() .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_ADDRESS.toString()) - .fieldTitle(MandatoryQuestionConstants.ORGANISATION_ADDRESS_TITLE) + .fieldTitle(MandatoryQuestionConstants.ORGANISATION_SUBMISSION_ADDRESS_TITLE) .profileField(MandatoryQuestionConstants.ORGANISATION_ADDRESS_PROFILE_FIELD) .adminSummary(MandatoryQuestionConstants.ORGANISATION_ADDRESS_ADMIN_SUMMARY) .responseType(SubmissionQuestionResponseType.AddressInput) diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java index 73362024..c2e44e06 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java @@ -652,7 +652,7 @@ void addMandatoryQuestions() { class buildOrganisationDetailsSubmissionSection { @Test - void addsCorrectDetails() { + void addsCorrectDetailsForLimitedCompany() { final SubmissionSectionStatus status = SubmissionSectionStatus.IN_PROGRESS; @@ -677,6 +677,7 @@ void addsCorrectDetails() { .build(); final GrantMandatoryQuestions mandatoryQuestions = GrantMandatoryQuestions.builder() + .orgType(GrantMandatoryQuestionOrgType.LIMITED_COMPANY) .submission(submission) .build(); @@ -723,13 +724,141 @@ void addsCorrectDetails() { assertThat(orgDetailsSection.getSectionTitle()).isEqualTo(MandatoryQuestionConstants.ORGANISATION_DETAILS_SECTION_TITLE); assertThat(orgDetailsSection.getSectionStatus()).isEqualTo(status); assertThat(orgDetailsSection.getQuestions()).containsExactlyElementsOf(List.of( - orgName, applicantType, + orgName, orgAddress, charityNumber, companiesHouse )); } + + @Test + void addsCorrectDetailsForNonLimitedCompany() { + + final SubmissionSectionStatus status = SubmissionSectionStatus.IN_PROGRESS; + + final SubmissionSection eligibility = SubmissionSection.builder() + .sectionId("ELIGIBILITY") + .build(); + + final SubmissionSection organisationDetails = SubmissionSection.builder() + .sectionId("ORGANISATION_DETAILS") + .build(); + + final SubmissionSection fundingDetails = SubmissionSection.builder() + .sectionId("FUNDING_DETAILS") + .build(); + + final SubmissionDefinition definition = SubmissionDefinition.builder() + .sections(new ArrayList(List.of(eligibility, organisationDetails, fundingDetails))) + .build(); + + final Submission submission = Submission.builder() + .definition(definition) + .build(); + + final GrantMandatoryQuestions mandatoryQuestions = GrantMandatoryQuestions.builder() + .orgType(GrantMandatoryQuestionOrgType.NON_LIMITED_COMPANY) + .submission(submission) + .build(); + + final SubmissionQuestion orgName = SubmissionQuestion.builder() + .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_NAME.toString()) + .build(); + + final SubmissionQuestion applicantType = SubmissionQuestion.builder() + .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_TYPE.toString()) + .build(); + + final SubmissionQuestion orgAddress = SubmissionQuestion.builder() + .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_ADDRESS.toString()) + .build(); + + doReturn(orgName) + .when(serviceUnderTest).mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_NAME.toString(), mandatoryQuestions); + + doReturn(applicantType) + .when(serviceUnderTest).mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_TYPE.toString(), mandatoryQuestions); + + doReturn(orgAddress) + .when(serviceUnderTest).mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_ADDRESS.toString(), mandatoryQuestions); + + final SubmissionSection orgDetailsSection = serviceUnderTest.buildOrganisationDetailsSubmissionSection(mandatoryQuestions, status); + + + assertThat(orgDetailsSection.getSectionId()).isEqualTo(MandatoryQuestionConstants.ORGANISATION_DETAILS_SECTION_ID); + assertThat(orgDetailsSection.getSectionTitle()).isEqualTo(MandatoryQuestionConstants.ORGANISATION_DETAILS_SECTION_TITLE); + assertThat(orgDetailsSection.getSectionStatus()).isEqualTo(status); + assertThat(orgDetailsSection.getQuestions()).containsExactlyElementsOf(List.of( + applicantType, + orgName, + orgAddress + )); + } + + @Test + void addsCorrectDetailsForIndividual() { + + final SubmissionSectionStatus status = SubmissionSectionStatus.IN_PROGRESS; + + final SubmissionSection eligibility = SubmissionSection.builder() + .sectionId("ELIGIBILITY") + .build(); + + final SubmissionSection organisationDetails = SubmissionSection.builder() + .sectionId("ORGANISATION_DETAILS") + .build(); + + final SubmissionSection fundingDetails = SubmissionSection.builder() + .sectionId("FUNDING_DETAILS") + .build(); + + final SubmissionDefinition definition = SubmissionDefinition.builder() + .sections(new ArrayList(List.of(eligibility, organisationDetails, fundingDetails))) + .build(); + + final Submission submission = Submission.builder() + .definition(definition) + .build(); + + final GrantMandatoryQuestions mandatoryQuestions = GrantMandatoryQuestions.builder() + .orgType(GrantMandatoryQuestionOrgType.INDIVIDUAL) + .submission(submission) + .build(); + + final SubmissionQuestion orgName = SubmissionQuestion.builder() + .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_NAME.toString()) + .build(); + + final SubmissionQuestion applicantType = SubmissionQuestion.builder() + .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_TYPE.toString()) + .build(); + + final SubmissionQuestion orgAddress = SubmissionQuestion.builder() + .questionId(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_ADDRESS.toString()) + .build(); + + doReturn(orgName) + .when(serviceUnderTest).mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_NAME.toString(), mandatoryQuestions); + + doReturn(applicantType) + .when(serviceUnderTest).mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_TYPE.toString(), mandatoryQuestions); + + doReturn(orgAddress) + .when(serviceUnderTest).mandatoryQuestionToSubmissionQuestion(MandatoryQuestionConstants.SUBMISSION_QUESTION_IDS.APPLICANT_ORG_ADDRESS.toString(), mandatoryQuestions); + + final SubmissionSection orgDetailsSection = serviceUnderTest.buildOrganisationDetailsSubmissionSection(mandatoryQuestions, status); + + + assertThat(orgDetailsSection.getSectionId()).isEqualTo(MandatoryQuestionConstants.ORGANISATION_DETAILS_SECTION_ID); + assertThat(orgDetailsSection.getSectionTitle()).isEqualTo(MandatoryQuestionConstants.ORGANISATION_INDIVIDUAL_DETAILS_SECTION_TITLE); + assertThat(orgDetailsSection.getSectionStatus()).isEqualTo(status); + assertThat(orgDetailsSection.getQuestions()).containsExactlyElementsOf(List.of( + applicantType, + orgName, + orgAddress + )); + } } @Nested From 3e95d745f33ed2b1d2c4a3a945e4147494000a5d Mon Sep 17 00:00:00 2001 From: Conor Fayle <141320269+ConorFayleTCO@users.noreply.github.com> Date: Tue, 21 Nov 2023 11:51:49 +0000 Subject: [PATCH 05/19] GAP-2249: fix save submission (#59) * GAP-2249 | fix submitting application * GAP-2249 | remove comment on line --------- Co-authored-by: conor --- .../service/SubmissionService.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java index 5fe13988..a4a1ab31 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java @@ -272,36 +272,32 @@ private void submitApplication(final Submission submission) { } private void createDiligenceCheckFromSubmission(final Submission submission) { - final String organisationName = getQuestionResponseByQuestionId(submission, APPLICANT_ORG_NAME); + final Optional organisationName = getQuestionResponseByQuestionId(submission, APPLICANT_ORG_NAME); final String[] organisationAddress = getQuestionMultiResponseByQuestionId(submission, APPLICANT_ORG_ADDRESS); - final String applicationAmount = getQuestionResponseByQuestionId(submission, APPLICANT_AMOUNT); - final String companiesHouseNumber = getQuestionResponseByQuestionId(submission, APPLICANT_ORG_COMPANIES_HOUSE); - final String charitiesCommissionNumber = getQuestionResponseByQuestionId(submission, APPLICANT_ORG_CHARITY_NUMBER); + final Optional applicationAmount = getQuestionResponseByQuestionId(submission, APPLICANT_AMOUNT); + final Optional companiesHouseNumber = getQuestionResponseByQuestionId(submission, APPLICANT_ORG_COMPANIES_HOUSE); + final Optional charitiesCommissionNumber = getQuestionResponseByQuestionId(submission, APPLICANT_ORG_CHARITY_NUMBER); diligenceCheckRepository.save(DiligenceCheck.builder() .submissionId(submission.getId()) .applicationNumber(submission.getGapId()) - .organisationName(organisationName) + .organisationName(organisationName.map(SubmissionQuestion::getResponse).orElse(null)) .addressStreet(organisationAddress[0]) .addressTown(organisationAddress[2]) .addressCounty(organisationAddress[3]) .addressPostcode(organisationAddress[4]) - .applicationAmount(applicationAmount) - .companiesHouseNumber(companiesHouseNumber) - .charityNumber(charitiesCommissionNumber) + .applicationAmount(applicationAmount.map(SubmissionQuestion::getResponse).orElse(null)) + .companiesHouseNumber(companiesHouseNumber.map(SubmissionQuestion::getResponse).orElse(null)) + .charityNumber(charitiesCommissionNumber.map(SubmissionQuestion::getResponse).orElse(null)) .build()); } - private String getQuestionResponseByQuestionId(final Submission submission, final String questionId) { + private Optional getQuestionResponseByQuestionId(final Submission submission, final String questionId) { return submission.getDefinition().getSections() .stream() .flatMap(s -> s.getQuestions().stream()) .filter(question -> question.getQuestionId().equals(questionId)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException( - String.format("submission %s does not contain a question with an ID of %s", - submission.getId(), questionId))) - .getResponse(); + .findAny(); } private String[] getQuestionMultiResponseByQuestionId(final Submission submission, final String questionId) { From 1f802c8e1b2a2d96c9c954585ba8b2afa1f338a4 Mon Sep 17 00:00:00 2001 From: ryan-tco <135323857+ryan-tco@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:50:38 +0000 Subject: [PATCH 06/19] GAP-2257 Disallow Special Chars in Answer Boxes (#60) --- .../validators/QuestionResponseValidator.java | 9 +++++++++ .../annotations/QuestionResponseValidatorTest.java | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java index 9cff4df6..65f12434 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java @@ -139,6 +139,11 @@ private ValidationResult validate(CreateQuestionResponseDto submittedQuestion, S } } + if(containsSpecialCharacters(submittedQuestion.getResponse())) { + result.addError(ValidationConstants.SINGLE_RESPONSE_FIELD, "Answer must only include letters, numbers, and special characters such as hyphens, spaces and apostrophes"); + return result; + } + result.setValid(Boolean.TRUE); return result; } @@ -210,6 +215,10 @@ private int getNumberOfWords(String response) { return Strings.isEmpty(response) ? 0 : response.split("\\s").length; } + private boolean containsSpecialCharacters(String response) { + return !response.matches("^(?![\\s\\S])|^[a-zA-Z0-9à-üÀ-Ü\\s',!@£$%^&*()_+=\\[\\];./?><:\"{}|`~-]+$"); + } + private ValidationResult validateDate(final String[] dateComponents, final boolean isMandatory) { ValidationResult dateValidationResult = ValidationResult.builder().build(); diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/validation/annotations/QuestionResponseValidatorTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/validation/annotations/QuestionResponseValidatorTest.java index d95e5539..b3d9b62c 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/validation/annotations/QuestionResponseValidatorTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/validation/annotations/QuestionResponseValidatorTest.java @@ -155,6 +155,17 @@ private static Stream provideTestData() { , "Answer must only include numeric values/ decimal", SubmissionQuestionValidation.builder() .greaterThanZero(true) + .build()), + + // Special characters check should fail + Arguments.of( + CreateQuestionResponseDto.builder() + .questionId(questionId) + .submissionId(submissionId) + .response("\uDBFF\uDC05") + .build() + , "Answer must only include letters, numbers, and special characters such as hyphens, spaces and apostrophes", + SubmissionQuestionValidation.builder() .build()) ); From d4888c83ab70c3e23574eb104d38d662169da490 Mon Sep 17 00:00:00 2001 From: dominicwest <101722961+dominicwest@users.noreply.github.com> Date: Thu, 23 Nov 2023 15:49:07 +0000 Subject: [PATCH 07/19] GAP-2303: Eagerly fetching scheme ads & application (#61) GAP-2303: Eagerly fetching scheme ads & application (#61) --- pull_request_template.md | 36 +++++++++++++++ .../dto/api/GetGrantApplicationDto.java | 20 ++++++++ .../dto/api/GetGrantSchemeDto.java | 43 +++++++++-------- ...tGrantSchemeWithApplicationAndAdverts.java | 14 ++++++ .../mapper/GrantSchemeMapper.java | 18 ++++++++ .../gap/applybackend/model/GrantScheme.java | 28 +++++------ .../repository/GrantSchemeRepository.java | 8 ++++ .../service/GrantAdvertService.java | 25 +++++++++- .../service/GrantSchemeService.java | 2 +- .../web/GrantSchemeController.java | 31 +++++++++++-- .../service/GrantSchemeServiceTest.java | 8 ++-- .../web/GrantSchemeControllerTest.java | 46 +++++++++++++++++-- 12 files changed, 231 insertions(+), 48 deletions(-) create mode 100644 pull_request_template.md create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantApplicationDto.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeWithApplicationAndAdverts.java create mode 100644 src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantSchemeMapper.java diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 00000000..bfe3eb4f --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,36 @@ +## Description + +Ticket # and link + +Summary of the changes and the related issue. List any dependencies that are required for this change: + +## Type of change + +Please check the relevant options. + +- [ ] Bug fix (non-breaking change which fixes an issue). +- [ ] New feature (non-breaking change which adds functionality). +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected). +- [ ] This change requires a documentation update. + +## How Has This Been Tested? + +Please describe the tests that you ran to verify your changes: + +- [ ] Unit Test + +- [ ] Integration Test (if applicable) + +- [ ] End to End Test (if applicable) + +## Screenshots (if appropriate): + +Please attach screenshots of the change if it is a UI change: + +# Checklist: + +- [ ] If I have listed dependencies above, I have ensured that they are present in the target branch. +- [ ] I have performed a self-review of my code. +- [ ] I have commented my code in hard-to-understand areas. +- [ ] I have made corresponding changes to the documentation where applicable. +- [ ] I have ran cypress tests and they all pass locally. diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantApplicationDto.java b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantApplicationDto.java new file mode 100644 index 00000000..8d513c6a --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantApplicationDto.java @@ -0,0 +1,20 @@ +package gov.cabinetoffice.gap.applybackend.dto.api; + +import gov.cabinetoffice.gap.applybackend.enums.GrantApplicantStatus; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import java.time.Instant; + +@Data +@RequiredArgsConstructor +public class GetGrantApplicationDto { + private Integer id; + private Integer grantSchemeId; + private Integer version; + private Instant created; + private Instant lastUpdated; + private Integer lastUpdatedBy; + private String applicationName; + private GrantApplicantStatus applicationStatus; +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeDto.java b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeDto.java index 90ba3376..75a462c6 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeDto.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeDto.java @@ -1,26 +1,33 @@ package gov.cabinetoffice.gap.applybackend.dto.api; -import lombok.AllArgsConstructor; -import lombok.Builder; +import gov.cabinetoffice.gap.applybackend.model.GrantScheme; import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; -import java.time.ZonedDateTime; +import java.time.Instant; -@NoArgsConstructor -@AllArgsConstructor -@Builder @Data +@RequiredArgsConstructor public class GetGrantSchemeDto { - private long id; - private long funderId; - private int version; - private ZonedDateTime lastUpdated; - private long lastUpdatedBy; + private Integer id; + private Integer funderId; + private Integer version; + private Instant createdDate; + private Instant lastUpdated; + private Integer lastUpdatedBy; private String ggisIdentifier; - private String schemeName; - private String schemeContact; - private String sectionId; - private String sectionTitle; - private String sectionStatus; -} + private String name; + private String email; + + public GetGrantSchemeDto(final GrantScheme grantScheme) { + this.id = grantScheme.getId(); + this.funderId = grantScheme.getFunderId(); + this.version = grantScheme.getVersion(); + this.createdDate = grantScheme.getCreatedDate(); + this.lastUpdated = grantScheme.getLastUpdated(); + this.lastUpdatedBy = grantScheme.getLastUpdatedBy(); + this.ggisIdentifier = grantScheme.getGgisIdentifier(); + this.name = grantScheme.getName(); + this.email = grantScheme.getEmail(); + } +} \ No newline at end of file diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeWithApplicationAndAdverts.java b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeWithApplicationAndAdverts.java new file mode 100644 index 00000000..5b73cb85 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantSchemeWithApplicationAndAdverts.java @@ -0,0 +1,14 @@ +package gov.cabinetoffice.gap.applybackend.dto.api; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class GetGrantSchemeWithApplicationAndAdverts { + private GetGrantSchemeDto grantScheme; + private GetGrantApplicationDto grantApplication; + private List grantAdverts; +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantSchemeMapper.java b/src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantSchemeMapper.java new file mode 100644 index 00000000..90040fb1 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantSchemeMapper.java @@ -0,0 +1,18 @@ +package gov.cabinetoffice.gap.applybackend.mapper; + +import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantApplicationDto; +import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantSchemeDto; +import gov.cabinetoffice.gap.applybackend.model.GrantScheme; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.NullValuePropertyMappingStrategy; + + +@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) +public interface GrantSchemeMapper { + GetGrantSchemeDto grantSchemeToGetGrantSchemeDto(GrantScheme grantScheme); + + @Mapping(source = "grantApplication", target = ".") + GetGrantApplicationDto grantSchemeToGetGrantApplicationDto(GrantScheme grantScheme); +} diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java index 798b51fa..ef154b30 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java @@ -1,19 +1,11 @@ package gov.cabinetoffice.gap.applybackend.model; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; +import lombok.*; + +import javax.persistence.*; import java.time.Instant; +import java.util.ArrayList; +import java.util.List; @Entity @Table(name = "grant_scheme") @@ -26,7 +18,7 @@ public class GrantScheme { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "grant_scheme_id") + @Column(name = "grant_scheme_id", nullable = false) private Integer id; @Column(name = "funder_id", nullable = false) @@ -54,4 +46,12 @@ public class GrantScheme { @Column(name = "scheme_contact") private String email; + + @ToString.Exclude + @OneToMany(mappedBy = "scheme", orphanRemoval = true, cascade = CascadeType.ALL) + private List grantAdverts = new ArrayList<>(); + + @ToString.Exclude + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "grantScheme") + private GrantApplication grantApplication; } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantSchemeRepository.java b/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantSchemeRepository.java index 6bbb2738..c55ab363 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantSchemeRepository.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantSchemeRepository.java @@ -1,7 +1,15 @@ package gov.cabinetoffice.gap.applybackend.repository; +import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantSchemeDto; import gov.cabinetoffice.gap.applybackend.model.GrantScheme; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Optional; public interface GrantSchemeRepository extends JpaRepository { + @Query("select g from GrantScheme g where g.id = ?1") + @EntityGraph(attributePaths = {"grantApplication", "grantAdverts"}) + Optional findByIdWithApplicationAndAdverts(Integer integer); } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java index 18dff640..3aa4d868 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java @@ -8,11 +8,15 @@ import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantAdvertDto; import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantMandatoryQuestionDto; import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; +import gov.cabinetoffice.gap.applybackend.mapper.GrantMandatoryQuestionMapper; import gov.cabinetoffice.gap.applybackend.model.GrantAdvert; import gov.cabinetoffice.gap.applybackend.model.GrantAdvertQuestionResponse; +import gov.cabinetoffice.gap.applybackend.model.GrantApplicant; +import gov.cabinetoffice.gap.applybackend.model.GrantMandatoryQuestions; import gov.cabinetoffice.gap.applybackend.repository.GrantAdvertRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @Service @@ -21,9 +25,11 @@ public class GrantAdvertService { private final GrantAdvertRepository grantAdvertRepository; - + private final GrantMandatoryQuestionMapper grantMandatoryQuestionMapper; private final GrantApplicationService grantApplicationService; private final CDAClient contentfulDeliveryClient; + private final GrantMandatoryQuestionService grantMandatoryQuestionService; + private final GrantApplicantService grantApplicantService; protected static String getExternalSubmissionUrl(GrantAdvert advert) { return advert.getResponse().getSections().stream() @@ -84,4 +90,21 @@ public GrantAdvert getAdvertBySchemeId(String schemeId) { log.debug("Advert with schemeId {} found", schemeId); return grantAdvert; } + + public GetGrantAdvertDto grantAdvertToDto( + final GrantAdvert grantAdvert, + final String sub, + final Integer schemeId + ) { + final GrantApplicant grantApplicant = grantApplicantService.getApplicantById(sub); + GetGrantMandatoryQuestionDto mandatoryQuestionsDto = null; + + if (grantMandatoryQuestionService.existsBySchemeIdAndApplicantId(schemeId, grantApplicant.getId())) { + final GrantMandatoryQuestions grantMandatoryQuestions = grantMandatoryQuestionService + .getMandatoryQuestionBySchemeId(schemeId, sub); + mandatoryQuestionsDto = grantMandatoryQuestionMapper.mapGrantMandatoryQuestionToGetGrantMandatoryQuestionDTO(grantMandatoryQuestions); + } + + return generateGetGrantAdvertDto(grantAdvert, mandatoryQuestionsDto); + } } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java index cc613f81..f4c67c83 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java @@ -14,7 +14,7 @@ public class GrantSchemeService { public GrantScheme getSchemeById(Integer schemeId) { return grantSchemeRepository - .findById(schemeId) + .findByIdWithApplicationAndAdverts(schemeId) .orElseThrow(() -> new NotFoundException( String.format("No Grant Scheme with ID %s was found", schemeId))); } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java index debc0704..dabe75f0 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java @@ -1,7 +1,10 @@ package gov.cabinetoffice.gap.applybackend.web; -import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantSchemeDto; +import gov.cabinetoffice.gap.applybackend.dto.api.*; +import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; +import gov.cabinetoffice.gap.applybackend.mapper.GrantSchemeMapper; import gov.cabinetoffice.gap.applybackend.model.GrantScheme; +import gov.cabinetoffice.gap.applybackend.service.GrantAdvertService; import gov.cabinetoffice.gap.applybackend.service.GrantSchemeService; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,11 +12,14 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RequiredArgsConstructor @RestController @@ -21,14 +27,29 @@ public class GrantSchemeController { private final GrantSchemeService grantSchemeService; + private final GrantSchemeMapper grantSchemeMapper; + private final GrantAdvertService grantAdvertService; @GetMapping("/{grantSchemeId}") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Grant Scheme Found", content = @Content(mediaType = "application/json", schema = @Schema(implementation = GetGrantSchemeDto.class))), + @ApiResponse(responseCode = "200", description = "Grant Scheme Found", content = @Content(mediaType = "application/json", schema = @Schema(implementation = GetGrantSchemeWithApplicationAndAdverts.class))), @ApiResponse(responseCode = "404", description = "No Grant Scheme found", content = @Content(mediaType = "application/json")), }) - public ResponseEntity getGrantSchemeById(@PathVariable Integer grantSchemeId) { - GrantScheme scheme = grantSchemeService.getSchemeById(grantSchemeId); - return ResponseEntity.ok(scheme); + public ResponseEntity getGrantSchemeById(@PathVariable Integer grantSchemeId) { + final JwtPayload jwtPayload = (JwtPayload) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + + final GrantScheme grantScheme = grantSchemeService.getSchemeById(grantSchemeId); + final GetGrantSchemeDto grantSchemeDto = new GetGrantSchemeDto(grantScheme); + final GetGrantApplicationDto grantApplicationDto = grantSchemeMapper.grantSchemeToGetGrantApplicationDto(grantScheme); + final List grantAdvertDtos = grantScheme.getGrantAdverts().stream() + .map(grantAdvert -> grantAdvertService.grantAdvertToDto(grantAdvert, jwtPayload.getSub(), grantSchemeId)) + .toList(); + + final GetGrantSchemeWithApplicationAndAdverts getGrantSchemeWithApplicationAndAdverts = GetGrantSchemeWithApplicationAndAdverts.builder() + .grantScheme(grantSchemeDto) + .grantApplication(grantApplicationDto) + .grantAdverts(grantAdvertDtos) + .build(); + return ResponseEntity.ok(getGrantSchemeWithApplicationAndAdverts); } } diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java index 823d2d15..030dfc18 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java @@ -41,20 +41,20 @@ void getSchemeById_Success() { .email("contact@contact.com") .build(); - when(grantSchemeRepository.findById(SCHEME_ID)).thenReturn(Optional.of(scheme)); + when(grantSchemeRepository.findByIdWithApplicationAndAdverts(SCHEME_ID)).thenReturn(Optional.of(scheme)); GrantScheme methodResponse = serviceUnderTest.getSchemeById(SCHEME_ID); - verify(grantSchemeRepository).findById(SCHEME_ID); + verify(grantSchemeRepository).findByIdWithApplicationAndAdverts(SCHEME_ID); assertEquals(methodResponse, scheme); } @Test void getGrantSchemeById_OrgNotFound() { - when(grantSchemeRepository.findById(SCHEME_ID)).thenReturn(Optional.empty()); + when(grantSchemeRepository.findByIdWithApplicationAndAdverts(SCHEME_ID)).thenReturn(Optional.empty()); Exception result = assertThrows(NotFoundException.class, () -> serviceUnderTest.getSchemeById(SCHEME_ID)); - verify(grantSchemeRepository).findById(SCHEME_ID); + verify(grantSchemeRepository).findByIdWithApplicationAndAdverts(SCHEME_ID); assertTrue(result.getMessage().contains("No Grant Scheme with ID "+ SCHEME_ID + " was found")); } diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java index 5afe9ec2..e9c8b7e5 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java @@ -1,19 +1,34 @@ package gov.cabinetoffice.gap.applybackend.web; +import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantSchemeDto; +import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantSchemeWithApplicationAndAdverts; +import gov.cabinetoffice.gap.applybackend.dto.api.JwtPayload; +import gov.cabinetoffice.gap.applybackend.mapper.GrantSchemeMapper; +import gov.cabinetoffice.gap.applybackend.model.GrantApplication; import gov.cabinetoffice.gap.applybackend.model.GrantScheme; import gov.cabinetoffice.gap.applybackend.service.GrantSchemeService; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.modelmapper.ModelMapper; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import java.time.Instant; +import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -23,9 +38,24 @@ class GrantSchemeControllerTest { @Mock private GrantSchemeService grantSchemeService; @Mock - private ModelMapper modelMapper; + private GrantSchemeMapper grantSchemeMapper; @InjectMocks private GrantSchemeController controllerUnderTest; + @MockBean + private AuthenticationManager authenticationManager; + @Mock + private Authentication authentication; + @Mock + private SecurityContext securityContext; + private JwtPayload jwtPayload; + + @BeforeEach + void setup() { + when(securityContext.getAuthentication()).thenReturn(authentication); + SecurityContextHolder.setContext(securityContext); + jwtPayload = JwtPayload.builder().sub("sub").build(); + when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()).thenReturn(jwtPayload); + } @Test void getGrantSchemeById_ReturnsTheCorrectGrantScheme() { @@ -38,15 +68,21 @@ void getGrantSchemeById_ReturnsTheCorrectGrantScheme() { .ggisIdentifier("SCH-000003589") .name("scheme_name") .email("contact@contact.com") + .grantAdverts(Collections.emptyList()) + .grantApplication(GrantApplication.builder().build()) .build(); + final GetGrantSchemeDto getGrantSchemeDto = new GetGrantSchemeDto(grantScheme); - when(grantSchemeService.getSchemeById(SCHEME_ID)) - .thenReturn(grantScheme); + when(grantSchemeService.getSchemeById(SCHEME_ID)).thenReturn(grantScheme); - ResponseEntity response = controllerUnderTest.getGrantSchemeById(SCHEME_ID); + ResponseEntity response = controllerUnderTest.getGrantSchemeById(SCHEME_ID); verify(grantSchemeService).getSchemeById(SCHEME_ID); assertEquals(HttpStatus.OK, response.getStatusCode()); - assertEquals(response.getBody(), grantScheme); + assertEquals(GetGrantSchemeWithApplicationAndAdverts.builder() + .grantScheme(getGrantSchemeDto) + .grantAdverts(Collections.emptyList()) + .grantApplication(null) + .build(), response.getBody()); } } From 9ae5fb8bc719f3ca4de4b54a1ca0fa215b9b5fb4 Mon Sep 17 00:00:00 2001 From: dominicwest <101722961+dominicwest@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:08:21 +0000 Subject: [PATCH 08/19] GAP-2251: Is org profile complete endpoint (#62) GAP-2251: Is org profile complete endpoint (#62) --- .../GrantApplicantOrganisationProfile.java | 14 ++ ...ntApplicantOrganisationProfileService.java | 6 + ...pplicantOrganisationProfileController.java | 6 + ...plicantOrganisationProfileServiceTest.java | 182 +++++++++++++++++- ...cantOrganisationProfileControllerTest.java | 34 ++++ 5 files changed, 239 insertions(+), 3 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplicantOrganisationProfile.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplicantOrganisationProfile.java index 4f815a31..d19354e9 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplicantOrganisationProfile.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplicantOrganisationProfile.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -60,4 +61,17 @@ public class GrantApplicantOrganisationProfile { @Column private String companiesHouseNumber; + + public Boolean isComplete() { + if (StringUtils.isEmpty(legalName)) return false; + if (StringUtils.isEmpty(addressLine1)) return false; + if (StringUtils.isEmpty(town)) return false; + if (StringUtils.isEmpty(postcode)) return false; + if (type == null) return false; + if (type != GrantApplicantOrganisationType.INDIVIDUAL && type != GrantApplicantOrganisationType.NON_LIMITED_COMPANY) { + if (StringUtils.isEmpty(getCompaniesHouseNumber())) return false; + return !StringUtils.isEmpty(getCharityCommissionNumber()); + } + return true; + } } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileService.java index bc837c04..f8fd0ed7 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileService.java @@ -37,4 +37,10 @@ public GrantApplicantOrganisationProfile createOrganisation(String applicantId, grantApplicantService.saveApplicant(applicant); return savedProfile; } + + public Boolean isOrganisationComplete(final String sub) { + final GrantApplicant applicant = grantApplicantService.getApplicantById(sub); + final GrantApplicantOrganisationProfile profile = applicant.getOrganisationProfile(); + return profile.isComplete(); + } } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileController.java b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileController.java index 3bbf88d4..11cc9612 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileController.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileController.java @@ -43,6 +43,12 @@ public ResponseEntity getOrganisationBy return ResponseEntity.ok(organisationDto); } + @GetMapping("/isComplete") + public ResponseEntity isOrganisationComplete() { + final JwtPayload jwtPayload = (JwtPayload) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + return ResponseEntity.ok(grantApplicantOrganisationProfileService.isOrganisationComplete(jwtPayload.getSub())); + } + @PatchMapping("/{organisationId}") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Organisation updated", content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))), diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileServiceTest.java index 34d3b954..0ebf47f6 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantApplicantOrganisationProfileServiceTest.java @@ -1,20 +1,22 @@ package gov.cabinetoffice.gap.applybackend.service; +import gov.cabinetoffice.gap.applybackend.enums.GrantApplicantOrganisationType; import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; import gov.cabinetoffice.gap.applybackend.model.GrantApplicant; import gov.cabinetoffice.gap.applybackend.model.GrantApplicantOrganisationProfile; import gov.cabinetoffice.gap.applybackend.repository.GrantApplicantOrganisationProfileRepository; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -146,4 +148,178 @@ void updateOrganisation_SuccessfullyCreateOrg() { verify(grantApplicantOrganisationProfileRepository).save(profile); assertEquals(methodResponse, profile); } + + @Nested + class IsOrganisationComplete { + @ParameterizedTest + @CsvSource(value = { + "INDIVIDUAL,Org name,9 George Square,Glasgow,G2 1QQ,,", + "NON_LIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,,", + "LIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "UNLIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "REGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "UNREGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "OTHER,Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "INDIVIDUAL,Org name,9 George Square,Glasgow,G2 1QQ,null,null", + "NON_LIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,null,null", + }, nullValues = {"null"}) + void returnsTrue( + final GrantApplicantOrganisationType type, + final String name, + final String addressLine1, + final String town, + final String postcode, + final String companiesHouseNumber, + final String charityCommissionNumber + ) { + final GrantApplicant applicant = GrantApplicant.builder() + .id(1) + .userId(APPLICANT_ID) + .build(); + final GrantApplicantOrganisationProfile profile = GrantApplicantOrganisationProfile.builder() + .charityCommissionNumber(charityCommissionNumber) + .companiesHouseNumber(companiesHouseNumber) + .addressLine1(addressLine1) + .town(town) + .postcode(postcode) + .type(type) + .legalName(name) + .build(); + applicant.setOrganisationProfile(profile); + + when(grantApplicantService.getApplicantById(APPLICANT_ID)).thenReturn(applicant); + + Boolean methodResponse = serviceUnderTest.isOrganisationComplete(APPLICANT_ID); + + verify(grantApplicantService).getApplicantById(APPLICANT_ID); + assertTrue(methodResponse); + } + + @ParameterizedTest + @CsvSource(value = { + ",Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "INDIVIDUAL,Org name,9 George Square,Glasgow,,45,000010", + "INDIVIDUAL,Org name,9 George Square,,G2 1QQ,45,000010", + "INDIVIDUAL,Org name,,Glasgow,G2 1QQ,45,000010", + "INDIVIDUAL,,9 George Square,Glasgow,G2 1QQ,45,000010", + "NON_LIMITED_COMPANY,Org name,9 George Square,Glasgow,,45,000010", + "NON_LIMITED_COMPANY,Org name,9 George Square,,G2 1QQ,45,000010", + "NON_LIMITED_COMPANY,Org name,,Glasgow,G2 1QQ,45,000010", + "NON_LIMITED_COMPANY,,9 George Square,Glasgow,G2 1QQ,45,000010", + "LIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,45,", + "LIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,,000010", + "LIMITED_COMPANY,Org name,9 George Square,Glasgow,,45,000010", + "LIMITED_COMPANY,Org name,9 George Square,,G2 1QQ,45,000010", + "LIMITED_COMPANY,Org name,,Glasgow,G2 1QQ,45,000010", + "LIMITED_COMPANY,,9 George Square,Glasgow,G2 1QQ,45,000010", + "UNLIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,45,", + "UNLIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,,000010", + "UNLIMITED_COMPANY,Org name,9 George Square,Glasgow,,45,000010", + "UNLIMITED_COMPANY,Org name,9 George Square,,G2 1QQ,45,000010", + "UNLIMITED_COMPANY,Org name,,Glasgow,G2 1QQ,45,000010", + "UNLIMITED_COMPANY,,9 George Square,Glasgow,G2 1QQ,45,000010", + "REGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,", + "REGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,,000010", + "REGISTERED_CHARITY,Org name,9 George Square,Glasgow,,45,000010", + "REGISTERED_CHARITY,Org name,9 George Square,,G2 1QQ,45,000010", + "REGISTERED_CHARITY,Org name,,Glasgow,G2 1QQ,45,000010", + "REGISTERED_CHARITY,,9 George Square,Glasgow,G2 1QQ,45,000010", + "UNREGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,", + "UNREGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,,000010", + "UNREGISTERED_CHARITY,Org name,9 George Square,Glasgow,,45,000010", + "UNREGISTERED_CHARITY,Org name,9 George Square,,G2 1QQ,45,000010", + "UNREGISTERED_CHARITY,Org name,,Glasgow,G2 1QQ,45,000010", + "UNREGISTERED_CHARITY,,9 George Square,Glasgow,G2 1QQ,45,000010", + "CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,", + "CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,,000010", + "CHARITY,Org name,9 George Square,Glasgow,,45,000010", + "CHARITY,Org name,9 George Square,,G2 1QQ,45,000010", + "CHARITY,Org name,,Glasgow,G2 1QQ,45,000010", + "CHARITY,,9 George Square,Glasgow,G2 1QQ,45,000010", + "OTHER,Org name,9 George Square,Glasgow,G2 1QQ,45,", + "OTHER,Org name,9 George Square,Glasgow,G2 1QQ,,000010", + "OTHER,Org name,9 George Square,Glasgow,,45,000010", + "OTHER,Org name,9 George Square,,G2 1QQ,45,000010", + "OTHER,Org name,,Glasgow,G2 1QQ,45,000010", + "OTHER,,9 George Square,Glasgow,G2 1QQ,45,000010", + "null,Org name,9 George Square,Glasgow,G2 1QQ,45,000010", + "INDIVIDUAL,Org name,9 George Square,Glasgow,null,45,000010", + "INDIVIDUAL,Org name,9 George Square,null,G2 1QQ,45,000010", + "INDIVIDUAL,Org name,null,Glasgow,G2 1QQ,45,000010", + "INDIVIDUAL,null,9 George Square,Glasgow,G2 1QQ,45,000010", + "NON_LIMITED_COMPANY,Org name,9 George Square,Glasgow,null,45,000010", + "NON_LIMITED_COMPANY,Org name,9 George Square,null,G2 1QQ,45,000010", + "NON_LIMITED_COMPANY,Org name,null,Glasgow,G2 1QQ,45,000010", + "NON_LIMITED_COMPANY,null,9 George Square,Glasgow,G2 1QQ,45,000010", + "LIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,45,null", + "LIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,null,000010", + "LIMITED_COMPANY,Org name,9 George Square,Glasgow,null,45,000010", + "LIMITED_COMPANY,Org name,9 George Square,null,G2 1QQ,45,000010", + "LIMITED_COMPANY,Org name,null,Glasgow,G2 1QQ,45,000010", + "LIMITED_COMPANY,null,9 George Square,Glasgow,G2 1QQ,45,000010", + "UNLIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,45,null", + "UNLIMITED_COMPANY,Org name,9 George Square,Glasgow,G2 1QQ,null,000010", + "UNLIMITED_COMPANY,Org name,9 George Square,Glasgow,null,45,000010", + "UNLIMITED_COMPANY,Org name,9 George Square,null,G2 1QQ,45,000010", + "UNLIMITED_COMPANY,Org name,null,Glasgow,G2 1QQ,45,000010", + "UNLIMITED_COMPANY,null,9 George Square,Glasgow,G2 1QQ,45,000010", + "REGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,null", + "REGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,null,000010", + "REGISTERED_CHARITY,Org name,9 George Square,Glasgow,null,45,000010", + "REGISTERED_CHARITY,Org name,9 George Square,null,G2 1QQ,45,000010", + "REGISTERED_CHARITY,Org name,null,Glasgow,G2 1QQ,45,000010", + "REGISTERED_CHARITY,null,9 George Square,Glasgow,G2 1QQ,45,000010", + "UNREGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,null", + "UNREGISTERED_CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,null,000010", + "UNREGISTERED_CHARITY,Org name,9 George Square,Glasgow,null,45,000010", + "UNREGISTERED_CHARITY,Org name,9 George Square,null,G2 1QQ,45,000010", + "UNREGISTERED_CHARITY,Org name,null,Glasgow,G2 1QQ,45,000010", + "UNREGISTERED_CHARITY,null,9 George Square,Glasgow,G2 1QQ,45,000010", + "CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,45,null", + "CHARITY,Org name,9 George Square,Glasgow,G2 1QQ,null,000010", + "CHARITY,Org name,9 George Square,Glasgow,null,45,000010", + "CHARITY,Org name,9 George Square,null,G2 1QQ,45,000010", + "CHARITY,Org name,null,Glasgow,G2 1QQ,45,000010", + "CHARITY,null,9 George Square,Glasgow,G2 1QQ,45,000010", + "OTHER,Org name,9 George Square,Glasgow,G2 1QQ,45,null", + "OTHER,Org name,9 George Square,Glasgow,G2 1QQ,null,000010", + "OTHER,Org name,9 George Square,Glasgow,null,45,000010", + "OTHER,Org name,9 George Square,null,G2 1QQ,45,000010", + "OTHER,Org name,null,Glasgow,G2 1QQ,45,000010", + "OTHER,null,9 George Square,Glasgow,G2 1QQ,45,000010", + }, nullValues = {"null"}) + void returnsFalse( + final GrantApplicantOrganisationType type, + final String name, + final String addressLine1, + final String town, + final String postcode, + final String companiesHouseNumber, + final String charityCommissionNumber + ) { + final GrantApplicant applicant = GrantApplicant.builder() + .id(1) + .userId(APPLICANT_ID) + .build(); + final GrantApplicantOrganisationProfile profile = GrantApplicantOrganisationProfile.builder() + .charityCommissionNumber(charityCommissionNumber) + .companiesHouseNumber(companiesHouseNumber) + .addressLine1(addressLine1) + .town(town) + .postcode(postcode) + .type(type) + .legalName(name) + .build(); + applicant.setOrganisationProfile(profile); + + when(grantApplicantService.getApplicantById(APPLICANT_ID)).thenReturn(applicant); + + Boolean methodResponse = serviceUnderTest.isOrganisationComplete(APPLICANT_ID); + + verify(grantApplicantService).getApplicantById(APPLICANT_ID); + assertFalse(methodResponse); + } + + } } \ No newline at end of file diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileControllerTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileControllerTest.java index b426604f..d26eb7c5 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileControllerTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantApplicantOrganisationProfileControllerTest.java @@ -6,6 +6,7 @@ import gov.cabinetoffice.gap.applybackend.dto.api.UpdateGrantApplicantOrganisationProfileDto; import gov.cabinetoffice.gap.applybackend.model.GrantApplicantOrganisationProfile; import gov.cabinetoffice.gap.applybackend.service.GrantApplicantOrganisationProfileService; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -152,4 +153,37 @@ void updateOrganisation_UpdatesTheCorrectOrg() { assertThat(methodResponse.getBody()) .isEqualTo(String.format("Organisation with ID %s has been updated.", grantApplicantOrganisationProfile.getId())); } + + @Nested + class IsOrganisationComplete { + @Test + void isOrganisationComplete_ReturnsTrue() { + final JwtPayload jwtPayload = JwtPayload.builder().sub(APPLICANT_USER_ID).build(); + when(securityContext.getAuthentication()).thenReturn(authentication); + SecurityContextHolder.setContext(securityContext); + when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()).thenReturn(jwtPayload); + when(grantApplicantOrganisationProfileService.isOrganisationComplete(APPLICANT_USER_ID)).thenReturn(true); + + ResponseEntity response = controllerUnderTest.isOrganisationComplete(); + + verify(grantApplicantOrganisationProfileService).isOrganisationComplete(APPLICANT_USER_ID); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(response.getBody(), true); + } + + @Test + void isOrganisationComplete_ReturnsFalse() { + final JwtPayload jwtPayload = JwtPayload.builder().sub(APPLICANT_USER_ID).build(); + when(securityContext.getAuthentication()).thenReturn(authentication); + SecurityContextHolder.setContext(securityContext); + when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()).thenReturn(jwtPayload); + when(grantApplicantOrganisationProfileService.isOrganisationComplete(APPLICANT_USER_ID)).thenReturn(false); + + ResponseEntity response = controllerUnderTest.isOrganisationComplete(); + + verify(grantApplicantOrganisationProfileService).isOrganisationComplete(APPLICANT_USER_ID); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(response.getBody(), false); + } + } } \ No newline at end of file From 533d481ddb416d50cc8bba6de9ece6dd3b3bf002 Mon Sep 17 00:00:00 2001 From: ryan-tco <135323857+ryan-tco@users.noreply.github.com> Date: Fri, 24 Nov 2023 09:37:47 +0000 Subject: [PATCH 09/19] GAP-2257 Additional whitelisted chars (#63) --- .../validation/validators/QuestionResponseValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java index 65f12434..1ab6b844 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java @@ -216,7 +216,7 @@ private int getNumberOfWords(String response) { } private boolean containsSpecialCharacters(String response) { - return !response.matches("^(?![\\s\\S])|^[a-zA-Z0-9à-üÀ-Ü\\s',!@£$%^&*()_+=\\[\\];./?><:\"{}|`~-]+$"); + return !response.matches("^(?![\\s\\S])|^[a-zA-Z0-9à-üÀ-Ü\\s',!@£$%^&*()_+=\\[\\];./?><:\"{}|`~߀•–¥¢…µ-]+$"); } private ValidationResult validateDate(final String[] dateComponents, final boolean isMandatory) { From 7f92e2f92c9c487f4267cee2bfaae380d96549f0 Mon Sep 17 00:00:00 2001 From: dominicwest <101722961+dominicwest@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:14:33 +0000 Subject: [PATCH 10/19] Fixing infinite recursion issue (#64) --- .../gap/applybackend/model/GrantAdvert.java | 2 ++ .../gap/applybackend/model/GrantApplication.java | 13 ++----------- .../gap/applybackend/model/GrantScheme.java | 7 ++++++- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java index 68066b2e..59606fa5 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java @@ -1,6 +1,7 @@ package gov.cabinetoffice.gap.applybackend.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonManagedReference; import gov.cabinetoffice.gap.applybackend.enums.GrantAdvertStatus; import lombok.AllArgsConstructor; import lombok.Builder; @@ -50,6 +51,7 @@ public class GrantAdvert extends BaseEntity { @JoinColumn(name = "scheme_id", nullable = false) @ToString.Exclude @JsonIgnoreProperties({"hibernateLazyInitializer"}) + @JsonManagedReference private GrantScheme scheme; @Column(name = "version", nullable = false) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java index 6343c2cd..c939ac8c 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java @@ -9,16 +9,7 @@ import lombok.ToString; import org.hibernate.annotations.Type; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.OneToOne; -import javax.persistence.Table; +import javax.persistence.*; import java.time.Instant; @Entity @@ -36,7 +27,7 @@ public class GrantApplication extends BaseEntity { @Column(name = "grant_application_id") private Integer id; - @OneToOne + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "grant_scheme_id") private GrantScheme grantScheme; diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java index ef154b30..a1e83a7b 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java @@ -1,5 +1,7 @@ package gov.cabinetoffice.gap.applybackend.model; +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.*; import javax.persistence.*; @@ -47,8 +49,11 @@ public class GrantScheme { @Column(name = "scheme_contact") private String email; - @ToString.Exclude @OneToMany(mappedBy = "scheme", orphanRemoval = true, cascade = CascadeType.ALL) + @ToString.Exclude + @JsonIgnoreProperties({ "hibernateLazyInitializer" }) + @JsonBackReference + @Builder.Default private List grantAdverts = new ArrayList<>(); @ToString.Exclude From 3ff8b0e8458f855e08bd104927bdca0c929070e5 Mon Sep 17 00:00:00 2001 From: dominicwest <101722961+dominicwest@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:03:13 +0000 Subject: [PATCH 11/19] Fix one to one relationship (#65) --- .../gap/applybackend/model/GrantAdvert.java | 1 - .../gap/applybackend/model/GrantApplication.java | 11 +++-------- .../gap/applybackend/model/GrantScheme.java | 10 +++------- .../gap/applybackend/service/GrantSchemeService.java | 9 ++++++++- .../gap/applybackend/web/GrantSchemeController.java | 3 +-- .../applybackend/service/GrantSchemeServiceTest.java | 8 ++++---- .../applybackend/web/GrantSchemeControllerTest.java | 8 ++------ 7 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java index 59606fa5..208ae257 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantAdvert.java @@ -34,7 +34,6 @@ @EntityListeners(AuditingEntityListener.class) @Getter @Setter -@ToString @NoArgsConstructor @AllArgsConstructor @Builder diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java index c939ac8c..de110472 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java @@ -1,12 +1,7 @@ package gov.cabinetoffice.gap.applybackend.model; import gov.cabinetoffice.gap.applybackend.enums.GrantApplicantStatus; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; +import lombok.*; import org.hibernate.annotations.Type; import javax.persistence.*; @@ -16,7 +11,6 @@ @Table(name = "grant_application") @Getter @Setter -@ToString @AllArgsConstructor @NoArgsConstructor @Builder @@ -27,7 +21,8 @@ public class GrantApplication extends BaseEntity { @Column(name = "grant_application_id") private Integer id; - @OneToOne(fetch = FetchType.LAZY) + @ToString.Exclude + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "grant_scheme_id") private GrantScheme grantScheme; diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java index a1e83a7b..bafd8c76 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java @@ -1,7 +1,6 @@ package gov.cabinetoffice.gap.applybackend.model; -import com.fasterxml.jackson.annotation.JsonBackReference; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.*; import javax.persistence.*; @@ -13,7 +12,6 @@ @Table(name = "grant_scheme") @Getter @Setter -@ToString @AllArgsConstructor @NoArgsConstructor @Builder @@ -49,14 +47,12 @@ public class GrantScheme { @Column(name = "scheme_contact") private String email; - @OneToMany(mappedBy = "scheme", orphanRemoval = true, cascade = CascadeType.ALL) @ToString.Exclude - @JsonIgnoreProperties({ "hibernateLazyInitializer" }) - @JsonBackReference - @Builder.Default + @OneToMany(mappedBy = "scheme", orphanRemoval = true, cascade = CascadeType.ALL) private List grantAdverts = new ArrayList<>(); @ToString.Exclude @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "grantScheme") + @JsonIgnore private GrantApplication grantApplication; } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java index f4c67c83..62d8fa6e 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeService.java @@ -12,10 +12,17 @@ public class GrantSchemeService { private final GrantSchemeRepository grantSchemeRepository; - public GrantScheme getSchemeById(Integer schemeId) { + public GrantScheme getSchemeByIdWithApplicationAndAdverts(Integer schemeId) { return grantSchemeRepository .findByIdWithApplicationAndAdverts(schemeId) .orElseThrow(() -> new NotFoundException( String.format("No Grant Scheme with ID %s was found", schemeId))); } + + public GrantScheme getSchemeById(Integer schemeId) { + return grantSchemeRepository + .findById(schemeId) + .orElseThrow(() -> new NotFoundException( + String.format("No Grant Scheme with ID %s was found", schemeId))); + } } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java index dabe75f0..c9265674 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeController.java @@ -1,7 +1,6 @@ package gov.cabinetoffice.gap.applybackend.web; import gov.cabinetoffice.gap.applybackend.dto.api.*; -import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; import gov.cabinetoffice.gap.applybackend.mapper.GrantSchemeMapper; import gov.cabinetoffice.gap.applybackend.model.GrantScheme; import gov.cabinetoffice.gap.applybackend.service.GrantAdvertService; @@ -38,7 +37,7 @@ public class GrantSchemeController { public ResponseEntity getGrantSchemeById(@PathVariable Integer grantSchemeId) { final JwtPayload jwtPayload = (JwtPayload) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - final GrantScheme grantScheme = grantSchemeService.getSchemeById(grantSchemeId); + final GrantScheme grantScheme = grantSchemeService.getSchemeByIdWithApplicationAndAdverts(grantSchemeId); final GetGrantSchemeDto grantSchemeDto = new GetGrantSchemeDto(grantScheme); final GetGrantApplicationDto grantApplicationDto = grantSchemeMapper.grantSchemeToGetGrantApplicationDto(grantScheme); final List grantAdvertDtos = grantScheme.getGrantAdverts().stream() diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java index 030dfc18..823d2d15 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantSchemeServiceTest.java @@ -41,20 +41,20 @@ void getSchemeById_Success() { .email("contact@contact.com") .build(); - when(grantSchemeRepository.findByIdWithApplicationAndAdverts(SCHEME_ID)).thenReturn(Optional.of(scheme)); + when(grantSchemeRepository.findById(SCHEME_ID)).thenReturn(Optional.of(scheme)); GrantScheme methodResponse = serviceUnderTest.getSchemeById(SCHEME_ID); - verify(grantSchemeRepository).findByIdWithApplicationAndAdverts(SCHEME_ID); + verify(grantSchemeRepository).findById(SCHEME_ID); assertEquals(methodResponse, scheme); } @Test void getGrantSchemeById_OrgNotFound() { - when(grantSchemeRepository.findByIdWithApplicationAndAdverts(SCHEME_ID)).thenReturn(Optional.empty()); + when(grantSchemeRepository.findById(SCHEME_ID)).thenReturn(Optional.empty()); Exception result = assertThrows(NotFoundException.class, () -> serviceUnderTest.getSchemeById(SCHEME_ID)); - verify(grantSchemeRepository).findByIdWithApplicationAndAdverts(SCHEME_ID); + verify(grantSchemeRepository).findById(SCHEME_ID); assertTrue(result.getMessage().contains("No Grant Scheme with ID "+ SCHEME_ID + " was found")); } diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java index e9c8b7e5..275c1d6b 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/web/GrantSchemeControllerTest.java @@ -12,14 +12,11 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import org.modelmapper.ModelMapper; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -28,7 +25,6 @@ import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -73,11 +69,11 @@ void getGrantSchemeById_ReturnsTheCorrectGrantScheme() { .build(); final GetGrantSchemeDto getGrantSchemeDto = new GetGrantSchemeDto(grantScheme); - when(grantSchemeService.getSchemeById(SCHEME_ID)).thenReturn(grantScheme); + when(grantSchemeService.getSchemeByIdWithApplicationAndAdverts(SCHEME_ID)).thenReturn(grantScheme); ResponseEntity response = controllerUnderTest.getGrantSchemeById(SCHEME_ID); - verify(grantSchemeService).getSchemeById(SCHEME_ID); + verify(grantSchemeService).getSchemeByIdWithApplicationAndAdverts(SCHEME_ID); assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(GetGrantSchemeWithApplicationAndAdverts.builder() .grantScheme(getGrantSchemeDto) From 19487635cd3a5bb916939c712906c9fddbb9f227 Mon Sep 17 00:00:00 2001 From: dominicwest <101722961+dominicwest@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:10:58 +0000 Subject: [PATCH 12/19] GAP-2309: Fixing v1 schemes from blowing up (#66) --- .../validation/validators/QuestionResponseValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java index 1ab6b844..0b843bda 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java @@ -139,7 +139,7 @@ private ValidationResult validate(CreateQuestionResponseDto submittedQuestion, S } } - if(containsSpecialCharacters(submittedQuestion.getResponse())) { + if (!singleResponseIsEmpty && containsSpecialCharacters(submittedQuestion.getResponse())) { result.addError(ValidationConstants.SINGLE_RESPONSE_FIELD, "Answer must only include letters, numbers, and special characters such as hyphens, spaces and apostrophes"); return result; } From a297e61ec44afe9ba3f0c59dfe10325b46bd88db Mon Sep 17 00:00:00 2001 From: dominicwest <101722961+dominicwest@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:07:21 +0000 Subject: [PATCH 13/19] GAP-2311: Fixing v1 applications (#67) --- .../gap/applybackend/model/GrantApplication.java | 4 +++- .../gap/applybackend/model/GrantScheme.java | 5 +++-- .../repository/GrantApplicationRepository.java | 10 ++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java index de110472..ae9a31ef 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantApplication.java @@ -1,5 +1,6 @@ package gov.cabinetoffice.gap.applybackend.model; +import com.fasterxml.jackson.annotation.JsonManagedReference; import gov.cabinetoffice.gap.applybackend.enums.GrantApplicantStatus; import lombok.*; import org.hibernate.annotations.Type; @@ -22,8 +23,9 @@ public class GrantApplication extends BaseEntity { private Integer id; @ToString.Exclude - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) @JoinColumn(name = "grant_scheme_id") + @JsonManagedReference private GrantScheme grantScheme; @Column(name = "version") diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java index bafd8c76..4d6cf04b 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/model/GrantScheme.java @@ -1,6 +1,6 @@ package gov.cabinetoffice.gap.applybackend.model; -import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonBackReference; import lombok.*; import javax.persistence.*; @@ -49,10 +49,11 @@ public class GrantScheme { @ToString.Exclude @OneToMany(mappedBy = "scheme", orphanRemoval = true, cascade = CascadeType.ALL) + @JsonBackReference private List grantAdverts = new ArrayList<>(); @ToString.Exclude @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "grantScheme") - @JsonIgnore + @JsonBackReference private GrantApplication grantApplication; } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantApplicationRepository.java b/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantApplicationRepository.java index 1feaeb50..09378a60 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantApplicationRepository.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/repository/GrantApplicationRepository.java @@ -3,7 +3,10 @@ import gov.cabinetoffice.gap.applybackend.model.GrantApplication; import gov.cabinetoffice.gap.applybackend.model.GrantScheme; +import org.jetbrains.annotations.NotNull; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.Optional; @@ -11,5 +14,12 @@ public interface GrantApplicationRepository extends JpaRepository getGrantApplicationByGrantSchemeId(int schemeId); Optional findByGrantScheme(GrantScheme grantScheme); + + + @NotNull + @EntityGraph(attributePaths = {"grantScheme"}) + @Query("select g from GrantApplication g where g.id = ?1") + @Override + Optional findById(@NotNull Integer integer); } From 536cb0a8ad1e728c686fd3b92f69dadacc223ab8 Mon Sep 17 00:00:00 2001 From: dominicwest <101722961+dominicwest@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:53:19 +0000 Subject: [PATCH 14/19] TMI-466: Fix enum mistmatch (#68) --- .../enums/GrantMandatoryQuestionFundingLocation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionFundingLocation.java b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionFundingLocation.java index 5c2749f0..4dc3787b 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionFundingLocation.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/enums/GrantMandatoryQuestionFundingLocation.java @@ -8,7 +8,7 @@ public enum GrantMandatoryQuestionFundingLocation { YORKSHIRE_AND_THE_HUMBER("Yorkshire and the Humber"), EAST_MIDLANDS_ENGLAND("East Midlands (England)"), WEST_MIDLANDS("West Midlands (England)"), - EAST_ENGLAND("East England"), + EAST_ENGLAND("East (England)"), LONDON("London"), SOUTH_EAST_ENGLAND("South East (England)"), SOUTH_WEST_ENGLAND("South West (England)"), From b2e2b243954c2ffc0bfb7fdbc35b80ad0ae65787 Mon Sep 17 00:00:00 2001 From: ryan-tco <135323857+ryan-tco@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:55:44 +0000 Subject: [PATCH 15/19] GAP-2257: Additional Whitelisted Chars (#69) --- .../validation/validators/QuestionResponseValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java index 0b843bda..17d3fb76 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/validation/validators/QuestionResponseValidator.java @@ -216,7 +216,7 @@ private int getNumberOfWords(String response) { } private boolean containsSpecialCharacters(String response) { - return !response.matches("^(?![\\s\\S])|^[a-zA-Z0-9à-üÀ-Ü\\s',!@£$%^&*()_+=\\[\\];./?><:\"{}|`~߀•–¥¢…µ-]+$"); + return !response.matches("^(?![\\s\\S])|^[a-zA-Z0-9à-üÀ-Ü\\s',!@£$%^&*()_+=\\[\\];./?><:\"{}|`~߀•–¥¢…µèéêëěẽýŷÿùúûüǔũūűìíîïǐĩiòóôöǒàáâäśğźžżćçčċñńņň-]+$"); } private ValidationResult validateDate(final String[] dateComponents, final boolean isMandatory) { From 18908ca3847961010e0d855adc1d0b122a8c5ce7 Mon Sep 17 00:00:00 2001 From: a-lor-cab <107372333+a-lor-cab@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:09:01 +0000 Subject: [PATCH 16/19] add status to the dto, and modify the mapper to accomodate it (#70) --- .../applybackend/dto/api/GetGrantMandatoryQuestionDto.java | 2 ++ .../applybackend/mapper/GrantMandatoryQuestionMapper.java | 7 +++++++ .../mapper/GrantMandatoryQuestionMapperTest.java | 3 +++ 3 files changed, 12 insertions(+) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantMandatoryQuestionDto.java b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantMandatoryQuestionDto.java index c890d34f..980d481a 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantMandatoryQuestionDto.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantMandatoryQuestionDto.java @@ -40,4 +40,6 @@ public class GetGrantMandatoryQuestionDto { private String fundingAmount; private List fundingLocation; + + private String status; } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapper.java b/src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapper.java index 13dc185f..3a279af1 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapper.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapper.java @@ -4,6 +4,7 @@ import gov.cabinetoffice.gap.applybackend.dto.api.UpdateGrantMandatoryQuestionDto; import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionFundingLocation; import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionOrgType; +import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionStatus; import gov.cabinetoffice.gap.applybackend.model.GrantMandatoryQuestions; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -27,8 +28,14 @@ public interface GrantMandatoryQuestionMapper { @Mapping(source = "fundingAmount", target = "fundingAmount", qualifiedByName = "mapEntityFundingAmountToDtoFundingAmount") @Mapping(source = "fundingLocation", target = "fundingLocation", qualifiedByName = "mapEntityFundingLocationToDtoFundingLocation") @Mapping(source = "submission.id", target = "submissionId") + @Mapping(source = "status", target = "status", qualifiedByName = "mapEntityStatusToDtoStatus") GetGrantMandatoryQuestionDto mapGrantMandatoryQuestionToGetGrantMandatoryQuestionDTO(GrantMandatoryQuestions source); + @Named("mapEntityStatusToDtoStatus") + default String mapEntityStatusToDtoStatus(GrantMandatoryQuestionStatus status) { + return status.toString(); + } + @Named("mapEntityOrgTypeToDtoOrgType") default String mapEntityOrgTypeToDtoOrgType(GrantMandatoryQuestionOrgType type) { return type.toString(); diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapperTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapperTest.java index 10566093..43c3fbb5 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapperTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/mapper/GrantMandatoryQuestionMapperTest.java @@ -4,6 +4,7 @@ import gov.cabinetoffice.gap.applybackend.dto.api.UpdateGrantMandatoryQuestionDto; import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionFundingLocation; import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionOrgType; +import gov.cabinetoffice.gap.applybackend.enums.GrantMandatoryQuestionStatus; import gov.cabinetoffice.gap.applybackend.model.GrantMandatoryQuestions; import gov.cabinetoffice.gap.applybackend.model.Submission; import org.junit.jupiter.api.Nested; @@ -33,6 +34,7 @@ void grantMandatoryQuestionIsFilled() { .submission(submission) .addressLine1("addressLine1") .addressLine2("addressLine2") + .status(GrantMandatoryQuestionStatus.IN_PROGRESS) .city("city") .county("county") .postcode("postcode") @@ -57,6 +59,7 @@ void grantMandatoryQuestionIsFilled() { assertThat(result.getFundingAmount()).isEqualTo(grantMandatoryQuestions.getFundingAmount().toString()); assertThat(result.getFundingLocation()).isEqualTo(List.of(grantMandatoryQuestions.getFundingLocation()[0].getName())); assertThat(result.getSubmissionId()).isEqualTo(uuid); + assertThat(result.getStatus()).isEqualTo(grantMandatoryQuestions.getStatus().toString()); } @Test From 63eed52676df604545a8b82bf15b32837eb14918 Mon Sep 17 00:00:00 2001 From: iaincooper-tco <99728291+iaincooper-tco@users.noreply.github.com> Date: Sun, 3 Dec 2023 21:58:24 +0000 Subject: [PATCH 17/19] TMI2-484: Fix GAP ID Generation (#73) --- .../GrantMandatoryQuestionService.java | 4 +- .../service/SubmissionService.java | 22 ++++++---- .../applybackend/utils/GapIdGenerator.java | 6 +-- .../GrantMandatoryQuestionServiceTest.java | 34 +++++---------- .../service/SubmissionServiceTest.java | 42 +++++++++++++++++-- 5 files changed, 66 insertions(+), 42 deletions(-) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java index a7510f4c..69482d76 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionService.java @@ -95,9 +95,7 @@ public GrantMandatoryQuestions createMandatoryQuestion(GrantScheme scheme, Grant public GrantMandatoryQuestions updateMandatoryQuestion(GrantMandatoryQuestions grantMandatoryQuestions, GrantApplicant grantApplicant) { if (grantMandatoryQuestions.getStatus().equals(GrantMandatoryQuestionStatus.COMPLETED)) { - final Submission submission = grantMandatoryQuestions.getSubmission(); - final String gapId = submission == null ? GapIdGenerator - .generateGapId(grantApplicant.getId(), envProperties.getEnvironmentName(), grantMandatoryQuestionRepository.count(), true) : submission.getGapId(); + final String gapId = GapIdGenerator.generateGapId(grantApplicant.getId(), envProperties.getEnvironmentName(), grantMandatoryQuestionRepository.count(), 2); grantMandatoryQuestions.setGapId(gapId); } return grantMandatoryQuestionRepository diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java index a4a1ab31..032431ea 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/SubmissionService.java @@ -221,17 +221,21 @@ public void submit(final Submission submission, final GrantApplicant grantApplic } final long diligenceCheckRecordsFromToday = getDiligenceCheckRecordsCountFromToday(); + final int version = submission.getScheme().getVersion(); - final String gapId = GapIdGenerator.generateGapId( - grantApplicant.getId(), - envProperties.getEnvironmentName(), - diligenceCheckRecordsFromToday + 1, - false - ); - submission.setGapId(gapId); - if (submission.getScheme().getVersion() > 1) { - setMandatoryQuestionsGapId(submission); + final Optional grantMandatoryQuestion = grantMandatoryQuestionRepository.findBySubmissionId(submission.getId()); + final String gapId; + if (grantMandatoryQuestion.isPresent()) { + gapId = grantMandatoryQuestion.get().getGapId(); + } else { + gapId = GapIdGenerator.generateGapId( + grantApplicant.getId(), + envProperties.getEnvironmentName(), + diligenceCheckRecordsFromToday + 1, + version + ); } + submission.setGapId(gapId); submitApplication(submission); notifyClient.sendConfirmationEmail(emailAddress, submission); diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/utils/GapIdGenerator.java b/src/main/java/gov/cabinetoffice/gap/applybackend/utils/GapIdGenerator.java index 4b385d9f..d4b643c4 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/utils/GapIdGenerator.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/utils/GapIdGenerator.java @@ -5,7 +5,7 @@ public class GapIdGenerator { - public static String generateGapId(final Long userId, final String env, final long recordNumber, final boolean isMandatoryQuestion) { + public static String generateGapId(final Long userId, final String env, final long recordNumber, final int version) { final LocalDate currentDate = LocalDate.now(); final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); final String date = currentDate.format(formatter); @@ -13,10 +13,10 @@ public static String generateGapId(final Long userId, final String env, final lo return "GAP" + "-" + env + - (isMandatoryQuestion ? - "-MQ-" : "-") + + "-" + date + "-" + + version + recordNumber + "-" + userId diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java index c2e44e06..f37c9695 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantMandatoryQuestionServiceTest.java @@ -21,6 +21,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Stream; @@ -381,6 +383,11 @@ void updateMandatoryQuestion_UpdatesExpectedMandatoryQuestions() { @Test void updateMandatoryQuestion_UpdatesExpectedMandatoryQuestionsAndSetsGapId() { final UUID mandatoryQuestionsId = UUID.randomUUID(); + + final LocalDate currentDate = LocalDate.now(); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + final String date = currentDate.format(formatter); + final GrantMandatoryQuestions grantMandatoryQuestions = GrantMandatoryQuestions .builder() .id(mandatoryQuestionsId) @@ -398,31 +405,10 @@ void updateMandatoryQuestion_UpdatesExpectedMandatoryQuestionsAndSetsGapId() { verify(grantMandatoryQuestionRepository).save(grantMandatoryQuestions); assertThat(methodResponse).isEqualTo(grantMandatoryQuestions); - assertThat(methodResponse.getGapId()).contains("GAP-local-MQ-"); - assertThat(methodResponse.getGapId()).isEqualTo(grantMandatoryQuestions.getGapId()); - } - @Test - void updateMandatoryQuestion_UpdatesExpectedMandatoryQuestionsAndSetsGapIdBySubmission() { - final UUID mandatoryQuestionsId = UUID.randomUUID(); - final Submission submission = Submission.builder().gapId("GAP-ID").build(); - final GrantMandatoryQuestions grantMandatoryQuestions = GrantMandatoryQuestions - .builder() - .id(mandatoryQuestionsId) - .status(GrantMandatoryQuestionStatus.COMPLETED) - .submission(submission) - .build(); - - when(grantMandatoryQuestionRepository.findById(mandatoryQuestionsId)) - .thenReturn(Optional.of(grantMandatoryQuestions)); - when(grantMandatoryQuestionRepository.save(grantMandatoryQuestions)) - .thenReturn(grantMandatoryQuestions); - - final GrantMandatoryQuestions methodResponse = serviceUnderTest.updateMandatoryQuestion(grantMandatoryQuestions, grantApplicant); - - verify(grantMandatoryQuestionRepository).save(grantMandatoryQuestions); - assertThat(methodResponse).isEqualTo(grantMandatoryQuestions); - assertThat(methodResponse.getGapId()).isEqualTo(submission.getGapId()); + //GAP ID Should be GAP-{environment}-{date}-{version}{recordNumber}-{userId} + assertThat(methodResponse.getGapId()).isEqualTo("GAP-"+ "local" + "-" + date + "-22-1"); + assertThat(methodResponse.getGapId()).isEqualTo(grantMandatoryQuestions.getGapId()); } } diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/SubmissionServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/SubmissionServiceTest.java index c664b7c9..f0ede555 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/SubmissionServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/SubmissionServiceTest.java @@ -31,6 +31,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.time.*; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1094,8 +1095,9 @@ void submit_CreatesGrantBeneficiary() { } @Test - void submit_SubmitsTheApplicationFormAndUpdatesMandatoryQuestions() { + void submit_SubmitsTheApplicationFormAndTakesIDFromMandatoryQuestions() { final String emailAddress = "test@email.com"; + final String gapId = "GAP-local-20231201-21-1"; final ArgumentCaptor submissionCaptor = ArgumentCaptor.forClass(Submission.class); final GrantApplicant grantApplicant = GrantApplicant.builder() .userId(userId) @@ -1107,7 +1109,7 @@ void submit_SubmitsTheApplicationFormAndUpdatesMandatoryQuestions() { final GrantMandatoryQuestions grantMandatoryQuestions = GrantMandatoryQuestions.builder() .id(UUID.randomUUID()) - .gapId(null) + .gapId(gapId) .build(); submission.setScheme(scheme); @@ -1122,7 +1124,41 @@ void submit_SubmitsTheApplicationFormAndUpdatesMandatoryQuestions() { final Submission capturedSubmission = submissionCaptor.getValue(); - assertThat(grantMandatoryQuestions.getGapId()).isEqualTo(capturedSubmission.getGapId()); + assertThat(grantMandatoryQuestions.getGapId()).isEqualTo(gapId); + assertThat(capturedSubmission.getGapId()).isEqualTo(gapId); + } + + @Test + void submit_SubmitsTheApplicationFormAndGeneratesID() { + final LocalDate currentDate = LocalDate.now(); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + final String date = currentDate.format(formatter); + + final String emailAddress = "test@email.com"; + final String gapId = "GAP-LOCAL-" + date +"-12-1"; + final ArgumentCaptor submissionCaptor = ArgumentCaptor.forClass(Submission.class); + final GrantApplicant grantApplicant = GrantApplicant.builder() + .userId(userId) + .id(1) + .build(); + final GrantScheme scheme = GrantScheme.builder() + .version(1) + .build(); + + submission.setScheme(scheme); + + when(grantMandatoryQuestionRepository.findBySubmissionId(submission.getId())).thenReturn(Optional.empty()); + when(diligenceCheckRepository.countDistinctByApplicationNumberContains(any())).thenReturn(1L); + doReturn(true).when(serviceUnderTest).isSubmissionReadyToBeSubmitted(userId, SUBMISSION_ID); + + serviceUnderTest.submit(submission, grantApplicant, emailAddress); + + verify(notifyClient).sendConfirmationEmail(emailAddress, submission); + verify(submissionRepository).save(submissionCaptor.capture()); + + final Submission capturedSubmission = submissionCaptor.getValue(); + + assertThat(capturedSubmission.getGapId()).isEqualTo(gapId); } } From cacf30237d1989e7a6cf03f5669685084c386704 Mon Sep 17 00:00:00 2001 From: jimpurvisTCO <151734116+jimpurvisTCO@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:53:49 +0000 Subject: [PATCH 18/19] TMI2-479 -Adding 'isPublished' property to grant advert DTOs (#71) * TMI2-479 -Adding 'isPublished' property to grant advert DTOs --------- Co-authored-by: Jim Purvis --- .../dto/api/GetGrantAdvertDto.java | 2 + .../service/GrantAdvertService.java | 2 + .../service/GrantAdvertServiceTest.java | 96 +++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java index c00bf88f..ff6d7de4 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java @@ -31,4 +31,6 @@ public class GetGrantAdvertDto { private boolean isAdvertInDatabase; private GetGrantMandatoryQuestionDto mandatoryQuestionsDto; + + private boolean isPublished; } diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java index 3aa4d868..4130d119 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertService.java @@ -7,6 +7,7 @@ import com.contentful.java.cda.CDAResourceNotFoundException; import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantAdvertDto; import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantMandatoryQuestionDto; +import gov.cabinetoffice.gap.applybackend.enums.GrantAdvertStatus; import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; import gov.cabinetoffice.gap.applybackend.mapper.GrantMandatoryQuestionMapper; import gov.cabinetoffice.gap.applybackend.model.GrantAdvert; @@ -63,6 +64,7 @@ public GetGrantAdvertDto generateGetGrantAdvertDto(GrantAdvert advert, GetGrantM .grantSchemeId(advert.getScheme().getId()) .isAdvertInDatabase(true) .mandatoryQuestionsDto(mandatoryQuestions) + .isPublished(advert.getStatus() == GrantAdvertStatus.PUBLISHED) .build(); } diff --git a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertServiceTest.java b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertServiceTest.java index 3cd6a757..acb645a5 100644 --- a/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/applybackend/service/GrantAdvertServiceTest.java @@ -8,6 +8,7 @@ import com.contentful.java.cda.FetchQuery; import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantAdvertDto; import gov.cabinetoffice.gap.applybackend.dto.api.GetGrantMandatoryQuestionDto; +import gov.cabinetoffice.gap.applybackend.enums.GrantAdvertStatus; import gov.cabinetoffice.gap.applybackend.exception.NotFoundException; import gov.cabinetoffice.gap.applybackend.model.GrantAdvert; import gov.cabinetoffice.gap.applybackend.model.GrantAdvertPageResponse; @@ -149,6 +150,7 @@ void generateGetGrantAdvertDto_createDtoForInternalApplicationAndVersion1() { .version(1) .scheme(scheme) .response(response) + .status(GrantAdvertStatus.PUBLISHED) .build(); final GetGrantMandatoryQuestionDto mandatoryQuestionDto = GetGrantMandatoryQuestionDto.builder().build(); @@ -164,6 +166,7 @@ void generateGetGrantAdvertDto_createDtoForInternalApplicationAndVersion1() { assertThat(methodResponse.getGrantApplicationId()).isEqualTo(1); assertThat(methodResponse.getGrantSchemeId()).isEqualTo(1); assertThat(methodResponse.getMandatoryQuestionsDto()).isEqualTo(mandatoryQuestionDto); + assertThat(methodResponse.isPublished()).isTrue(); } @Test @@ -176,6 +179,7 @@ void generateGetGrantAdvertDto_createDtoForInternalApplicationAndVersion2() { .version(2) .scheme(scheme) .response(response) + .status(GrantAdvertStatus.PUBLISHED) .build(); final GetGrantMandatoryQuestionDto mandatoryQuestionDto = GetGrantMandatoryQuestionDto.builder().build(); @@ -191,6 +195,7 @@ void generateGetGrantAdvertDto_createDtoForInternalApplicationAndVersion2() { assertThat(methodResponse.getGrantApplicationId()).isEqualTo(1); assertThat(methodResponse.getGrantSchemeId()).isEqualTo(1); assertThat(methodResponse.getMandatoryQuestionsDto()).isEqualTo(mandatoryQuestionDto); + assertThat(methodResponse.isPublished()).isTrue(); } @Test @@ -203,6 +208,7 @@ void generateGetGrantAdvertDto_createDtoForExternalApplicationAndVersion2() { .version(2) .scheme(scheme) .response(response) + .status(GrantAdvertStatus.PUBLISHED) .build(); final GetGrantMandatoryQuestionDto mandatoryQuestionDto = GetGrantMandatoryQuestionDto.builder().build(); @@ -218,6 +224,94 @@ void generateGetGrantAdvertDto_createDtoForExternalApplicationAndVersion2() { assertThat(methodResponse.getGrantApplicationId()).isNull(); assertThat(methodResponse.getGrantSchemeId()).isEqualTo(1); assertThat(methodResponse.getMandatoryQuestionsDto()).isEqualTo(mandatoryQuestionDto); + assertThat(methodResponse.isPublished()).isTrue(); + } + + @Test + void generateGetGrantAdvertDto_createDtoForDraftExternalApplicationAndVersion2() { + final GrantAdvertResponse response = generateResponseWithHowToApplySection(); + final GrantScheme scheme = GrantScheme.builder().id(1).build(); + final GrantAdvert advert = GrantAdvert.builder() + .contentfulSlug("slug") + .id(ADVERT_ID) + .version(2) + .scheme(scheme) + .response(response) + .status(GrantAdvertStatus.DRAFT) + .build(); + final GetGrantMandatoryQuestionDto mandatoryQuestionDto = GetGrantMandatoryQuestionDto.builder().build(); + + when(grantApplicationService.doesSchemeHaveApplication(scheme)).thenReturn(false); + when(grantApplicationService.getGrantApplicationId(scheme)).thenReturn(null); + + final GetGrantAdvertDto methodResponse = grantAdvertService.generateGetGrantAdvertDto(advert, mandatoryQuestionDto); + + assertThat(methodResponse.getId()).isEqualTo(ADVERT_ID); + assertThat(methodResponse.getVersion()).isEqualTo(2); + assertThat(methodResponse.getExternalSubmissionUrl()).isEqualTo("responseUrl"); + assertThat(methodResponse.isInternal()).isFalse(); + assertThat(methodResponse.getGrantApplicationId()).isNull(); + assertThat(methodResponse.getGrantSchemeId()).isEqualTo(1); + assertThat(methodResponse.getMandatoryQuestionsDto()).isEqualTo(mandatoryQuestionDto); + assertThat(methodResponse.isPublished()).isFalse(); + } + + @Test + void generateGetGrantAdvertDto_createDtoForUnpublishedExternalApplicationAndVersion2() { + final GrantAdvertResponse response = generateResponseWithHowToApplySection(); + final GrantScheme scheme = GrantScheme.builder().id(1).build(); + final GrantAdvert advert = GrantAdvert.builder() + .contentfulSlug("slug") + .id(ADVERT_ID) + .version(2) + .scheme(scheme) + .response(response) + .status(GrantAdvertStatus.UNPUBLISHED) + .build(); + final GetGrantMandatoryQuestionDto mandatoryQuestionDto = GetGrantMandatoryQuestionDto.builder().build(); + + when(grantApplicationService.doesSchemeHaveApplication(scheme)).thenReturn(false); + when(grantApplicationService.getGrantApplicationId(scheme)).thenReturn(null); + + final GetGrantAdvertDto methodResponse = grantAdvertService.generateGetGrantAdvertDto(advert, mandatoryQuestionDto); + + assertThat(methodResponse.getId()).isEqualTo(ADVERT_ID); + assertThat(methodResponse.getVersion()).isEqualTo(2); + assertThat(methodResponse.getExternalSubmissionUrl()).isEqualTo("responseUrl"); + assertThat(methodResponse.isInternal()).isFalse(); + assertThat(methodResponse.getGrantApplicationId()).isNull(); + assertThat(methodResponse.getGrantSchemeId()).isEqualTo(1); + assertThat(methodResponse.getMandatoryQuestionsDto()).isEqualTo(mandatoryQuestionDto); + assertThat(methodResponse.isPublished()).isFalse(); + } + + @Test + void generateGetGrantAdvertDto_createDtoForScheduledExternalApplicationAndVersion2() { + final GrantAdvertResponse response = generateResponseWithHowToApplySection(); + final GrantScheme scheme = GrantScheme.builder().id(1).build(); + final GrantAdvert advert = GrantAdvert.builder() + .contentfulSlug("slug") + .id(ADVERT_ID) + .version(2) + .scheme(scheme) + .response(response) + .status(GrantAdvertStatus.SCHEDULED) + .build(); + final GetGrantMandatoryQuestionDto mandatoryQuestionDto = GetGrantMandatoryQuestionDto.builder().build(); + + when(grantApplicationService.doesSchemeHaveApplication(scheme)).thenReturn(false); + when(grantApplicationService.getGrantApplicationId(scheme)).thenReturn(null); + + final GetGrantAdvertDto methodResponse = grantAdvertService.generateGetGrantAdvertDto(advert, mandatoryQuestionDto); + + assertThat(methodResponse.getId()).isEqualTo(ADVERT_ID); + assertThat(methodResponse.getVersion()).isEqualTo(2); + assertThat(methodResponse.getExternalSubmissionUrl()).isEqualTo("responseUrl"); + assertThat(methodResponse.isInternal()).isFalse(); + assertThat(methodResponse.getGrantApplicationId()).isNull(); + assertThat(methodResponse.getGrantSchemeId()).isEqualTo(1); + assertThat(methodResponse.getMandatoryQuestionsDto()).isEqualTo(mandatoryQuestionDto); + assertThat(methodResponse.isPublished()).isFalse(); } @Test @@ -230,6 +324,7 @@ void generateGetGrantAdvertDto_createDtoForExternalApplicationAndVersion1() { .version(1) .scheme(scheme) .response(response) + .status(GrantAdvertStatus.PUBLISHED) .build(); final GetGrantMandatoryQuestionDto mandatoryQuestionDto = GetGrantMandatoryQuestionDto.builder().build(); @@ -245,6 +340,7 @@ void generateGetGrantAdvertDto_createDtoForExternalApplicationAndVersion1() { assertThat(methodResponse.getGrantApplicationId()).isNull(); assertThat(methodResponse.getGrantSchemeId()).isEqualTo(1); assertThat(methodResponse.getMandatoryQuestionsDto()).isEqualTo(mandatoryQuestionDto); + assertThat(methodResponse.isPublished()).isTrue(); } } From b04744125b321c6af1c1636d360f8eb208fcb5e6 Mon Sep 17 00:00:00 2001 From: iaincooper-tco <99728291+iaincooper-tco@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:34:10 +0000 Subject: [PATCH 19/19] TMI2-479: Fix isPublished having wrong key in response (#75) --- .../gap/applybackend/dto/api/GetGrantAdvertDto.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java index ff6d7de4..272560fe 100644 --- a/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java +++ b/src/main/java/gov/cabinetoffice/gap/applybackend/dto/api/GetGrantAdvertDto.java @@ -32,5 +32,6 @@ public class GetGrantAdvertDto { private GetGrantMandatoryQuestionDto mandatoryQuestionsDto; + @JsonProperty("isPublished") private boolean isPublished; }