Skip to content
Merged

Main #331

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
f54ba38
Release/3.2 (#28)
dominicwest Sep 6, 2023
c74f374
Release/3.3 (#39)
dylanwrightCO Sep 28, 2023
b940900
Release/3.6 (#47)
dominicwest Oct 17, 2023
a4c6d94
Merge branch 'main' into release/4.0
GavCookCO Nov 9, 2023
c6a3198
Create DUMMY
GavCookCO Nov 9, 2023
1fb9a9a
Merge pull request #64 from cabinetoffice/release/4.0
GavCookCO Nov 9, 2023
c78a8d7
Merge branch 'main' into release/5.0
jgunnCO Nov 13, 2023
c08f05b
Merge pull request #69 from cabinetoffice/release/5.0
dominicwest Nov 15, 2023
ee6a598
Tmi2 501/revert tmi2 496 changes (#129) (#130)
a-lor-cab Dec 6, 2023
e1bfdc5
Merge pull request #122 from cabinetoffice/release/6.0
dominicwest Dec 6, 2023
3320f82
Release/6.1 (#136)
ConnorTCO Dec 12, 2023
bc90f4a
Merge branch 'main' into release/7.0
iaincooper-tco Jan 12, 2024
3dba5f3
Merge pull request #151 from cabinetoffice/release/7.0
iaincooper-tco Jan 17, 2024
fc3b4a7
Merge pull request #159 from cabinetoffice/release/8.0
dylanwrightCO Feb 7, 2024
fd69ec0
Fix promoteToProd.yml (#179)
dominicwest Feb 7, 2024
b4cc5d7
Merge branch 'main' into release/9.0
dominicwest Feb 23, 2024
4f5b3dd
Adds query to find application with no ownership check to allow lambd…
dylanwrightCO Feb 26, 2024
e297c35
Merge pull request #208 from cabinetoffice/release/9.0
dominicwest Feb 28, 2024
f5edb8b
Fix unpublish application form from lambda (#268) (#269)
dominicwest Mar 27, 2024
aa3b8c8
Merge branch 'main' of github.com:cabinetoffice/gap-find-admin-backen…
dylanwrightCO Mar 27, 2024
b3ef752
Updating release 10.0 with develop changes (#274)
dominicwest Apr 1, 2024
eb512bd
Merge branch 'develop' into release/10.0
jgunnCO Apr 2, 2024
54e1ef9
GAP-2533: Retrospectively fix BST ads in prod (#276) (#278)
dominicwest Apr 2, 2024
9c6405a
Merge pull request #267 from cabinetoffice/release/10.0
dylanwrightCO Apr 2, 2024
535af9a
Improves logs around updating and deleting open search (#280)
dylanwrightCO Apr 9, 2024
7dcfc0b
Merge pull request #281 from cabinetoffice/release/10.1
dylanwrightCO Apr 9, 2024
f72f649
Improving logging (#283)
GavCookCO Apr 10, 2024
24d4b57
Improving logging II
GavCookCO Apr 10, 2024
51b9e44
Merge pull request #284 from cabinetoffice/release/10.2
GavCookCO Apr 10, 2024
f275c32
Merge branch 'main' into release/11.0
jgunnCO Apr 29, 2024
f057aa7
Merge branch 'develop' into release/11.0
jgunnCO Apr 30, 2024
29d600e
Merge branch 'develop' into release/11.0
jgunnCO Apr 30, 2024
16b0599
amend schemeeditorcontroller
jgunnCO May 1, 2024
f1c7f32
Merge branch 'feature/GAP-2619-3' into release/11.0
jgunnCO May 1, 2024
49c2ae2
Merge pull request #299 from cabinetoffice/release/11.0
ConorFayleTCO May 1, 2024
54a13de
Release/12.0 (#308)
arul-fourseals Aug 12, 2024
35616fa
Release/12.3 (#310)
arul-fourseals Jan 9, 2025
e8ec48f
release/12.4 (#313)
BenExile Jun 3, 2025
a4be0bc
Release/test deployments main (#317)
rmohammed-goaco Nov 19, 2025
ff9697d
Implement idempotency check for spotlight submissions in SpotlightBat…
rmohammed-goaco Jan 8, 2026
3778b9b
Release/12.5 > main (#325)
rmohammed-goaco Jan 9, 2026
380706e
Release/13.0 - Multiple Submissions (#322)
rmohammed-goaco Jan 14, 2026
7984fe5
Release/13.0 (#330)
rmohammed-goaco Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ jobs:
--enableRetired
--disableOssIndex true
--disableRetireJS true
--disableCentral true

- name: Upload Test results
uses: actions/upload-artifact@master
uses: actions/upload-artifact@v4
with:
name: DependencyCheck report
path: ${{github.workspace}}/reports
7 changes: 4 additions & 3 deletions .github/workflows/pushImage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ jobs:
args: >
--enableRetired
--disableOssIndex true
--disableCentral true
- name: Upload Test results
uses: actions/upload-artifact@master
uses: actions/upload-artifact@v4
with:
name: DependencyCheck report
path: ${{github.workspace}}/reports
Expand Down Expand Up @@ -110,7 +111,7 @@ jobs:
run: docker save --output ${{ steps.docker-image-name.outputs.name }}.tar $ECR_REGISTRY/gap-apply-admin-backend:${{ env.BUILD_VERSION }}

- name: Upload Docker image
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ steps.docker-image-name.outputs.name }}
path: ${{ steps.docker-image-name.outputs.name }}.tar
Expand Down Expand Up @@ -148,7 +149,7 @@ jobs:
echo BUILD_VERSION is ${{ env.BUILD_VERSION }}

- name: Download Docker image
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ needs.build.outputs.docker-image-name }}

Expand Down
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,97 @@ mvn Test -Dtest=className
mvn Test -Dtest=className#methodName
```

## Troubleshooting

### MapStruct Mapper Implementation Not Found

If you encounter an error like:

```
Parameter X of constructor in ...Service required a bean of type '...Mapper' that could not be found.
```

This typically means MapStruct hasn't generated the mapper implementation classes. MapStruct generates these during compilation.

**Solution:**

1. **Clean and rebuild the project:**
```bash
mvn clean compile
```

Or in your IDE:
- **IntelliJ IDEA**: Build → Rebuild Project
- **Eclipse**: Project → Clean → Clean all projects

2. **Verify annotation processing is enabled:**
- **IntelliJ IDEA**: Settings → Build, Execution, Deployment → Compiler → Annotation Processors → Enable annotation processing
- **Eclipse**: Project Properties → Java Compiler → Annotation Processing → Enable annotation processing

3. **Check for compilation errors:** If there are any compilation errors in mapper interfaces or related DTOs, MapStruct won't generate the implementation classes. Check the compiler output for errors.

After a successful build, you should see generated mapper implementations in:
```
target/generated-sources/annotations/gov/cabinetoffice/gap/adminbackend/mappers/
```

### Flyway Migration Checksum Mismatch

If you encounter an error during deployment like:

```
Migration checksum mismatch for migration version X.XX
-> Applied to database : -336000449
-> Resolved locally : -252574045
Either revert the changes to the migration, or run repair to update the schema history.
```

This occurs when a migration file has been modified after it was already applied to the database. Flyway validates checksums to ensure migration files haven't been changed.

**Important:** Never modify migration files after they've been deployed to any environment. If you need to change something, create a new migration file instead.

#### Solution Options

**Option 1: Use Flyway Repair (Recommended)**

Enable Flyway repair mode temporarily in your deployment configuration:

```properties
spring.flyway.repair=true
```

Then restart the application. Flyway will repair all checksums to match the current migration files. After the repair completes, remove this property.

**Option 2: Manually Update Checksum in Database**

If you have direct database access, you can manually update the checksum in the `flyway_schema_history` table:

```sql
-- Connect to your database and run:
UPDATE flyway_schema_history
SET checksum = <new_checksum> -- Use the checksum from the error message
WHERE version = '<version>';
```

**⚠️ Warning:** Only update the checksum if you're certain the migration file changes don't affect the database schema. If the migration was modified to change the schema, you may need to create a new migration to apply those changes instead.

**Option 3: Use Flyway CLI Repair Command**

If you have access to run commands on your deployment environment:

```bash
flyway repair -url=jdbc:postgresql://<your-db-host>:5432/<database-name> \
-user=<username> -password=<password> \
-locations=filesystem:src/main/resources/db/migration
```

#### Prevention

- **Never modify migration files** after they've been applied to any environment
- If you need to change something, **create a new migration file** instead
- Use version control to track migration history
- Consider using `spring.flyway.validate-on-migrate=true` (already enabled) to catch these issues early

## System Context Diagram
```mermaid
flowchart TD
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ public ResponseEntity<GenericErrorDTO> updateApplicationForm(HttpServletRequest

this.applicationFormService.patchApplicationForm(applicationId, applicationFormPatchDTO, false);

EventType eventType = applicationFormPatchDTO.getApplicationStatus().equals(ApplicationStatusEnum.PUBLISHED)
EventType eventType = applicationFormPatchDTO.getApplicationStatus() != null
&& applicationFormPatchDTO.getApplicationStatus().equals(ApplicationStatusEnum.PUBLISHED)
? EventType.APPLICATION_PUBLISHED : EventType.APPLICATION_UPDATED;

logApplicationEvent(eventType, request.getRequestedSessionId(), applicationId.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected ResponseEntity<Object> handleConflict(ObjectOptimisticLockingFailureEx
/**
* This overridden method handles controller methods being called with invalid
* arguments It'll spit back a json representation of a FieldErrorsDTO or a
* ClassErrorsDTO
* ClassErrorsDTO.
*/
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
Expand All @@ -140,7 +140,7 @@ protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotV

/**
* This overridden method handles controller methods which cause bind exceptions It'll
* spit back a json representation of a FieldErrorsDTO or a ClassErrorsDTO
* spit back a json representation of a FieldErrorsDTO or a ClassErrorsDTO.
*/
@Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ public ResponseEntity<String> getHealthCheck() {
return ResponseEntity.ok("Service up");
}

@GetMapping("/check")
public ResponseEntity<String> getHealthCheck2() {
return ResponseEntity.ok("Admin API Service up");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public ResponseEntity<List<SchemeEditorsDTO>> getSchemeEditors(@PathVariable fin
@PostMapping
public ResponseEntity<String> addEditorToScheme(@PathVariable final Integer schemeId, @RequestBody @Valid final SchemeEditorPostDTO newEditorDto, final HttpServletRequest request) {
final String jwt = HelperUtils.getJwtFromCookies(request, userServiceConfig.getCookieName());
schemeEditorService.addEditorToScheme(schemeId, newEditorDto.getEditorEmailAddress(), jwt);
schemeEditorService.addEditorToScheme(schemeId, newEditorDto.getEditorEmailAddress().toLowerCase(), jwt);
return ResponseEntity.ok("Editor added to scheme successfully");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
package gov.cabinetoffice.gap.adminbackend.dtos.application;

import javax.validation.constraints.NotNull;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import gov.cabinetoffice.gap.adminbackend.enums.ApplicationStatusEnum;
import lombok.AllArgsConstructor;
import gov.cabinetoffice.gap.adminbackend.validation.AtLeastOneFieldNotNull;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = false)
@AtLeastOneFieldNotNull
public class ApplicationFormPatchDTO {

@NotNull
private ApplicationStatusEnum applicationStatus;

private Boolean allowsMultipleSubmissions;

public ApplicationFormPatchDTO(ApplicationStatusEnum applicationStatus) {
this.applicationStatus = applicationStatus;
}

public ApplicationFormPatchDTO(ApplicationStatusEnum applicationStatus, Boolean allowsMultipleSubmissions) {
this.applicationStatus = applicationStatus;
this.allowsMultipleSubmissions = allowsMultipleSubmissions;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ public class ExportedSubmissionsDto {
private UUID submissionId;
private GrantExportStatus status;
private ZonedDateTime submittedDate;
private String submissionName;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class LambdaSubmissionDefinition {

private String gapId;

private String submissionName;

private ZonedDateTime submittedDate;

private List<SubmissionSection> sections;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class ApplicationFormEntity extends BaseEntity {
@Enumerated(EnumType.STRING)
private ApplicationStatusEnum applicationStatus;

@Column(name = "allows_multiple_submissions")
private Boolean allowsMultipleSubmissions;

@Column(name = "definition", nullable = false, columnDefinition = "json")
@Type(type = "json")
private ApplicationDefinitionDTO definition;
Expand All @@ -70,6 +73,7 @@ public ApplicationFormEntity(Integer grantSchemeId, String applicationName, Inte
this.lastUpdateBy = lastUpdateBy;
this.lastPublished = null;
this.applicationName = applicationName;
this.allowsMultipleSubmissions = false;
this.definition = definition;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public class Submission extends BaseEntity {
@Column
private String applicationName;

@Column(name = "submission_name")
private String submissionName;

@Column
@Enumerated(EnumType.STRING)
private SubmissionStatus status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import gov.cabinetoffice.gap.adminbackend.dtos.application.questions.QuestionOptionsPatchDTO;
import gov.cabinetoffice.gap.adminbackend.dtos.application.questions.QuestionOptionsPostDTO;
import gov.cabinetoffice.gap.adminbackend.entities.ApplicationFormEntity;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValuePropertyMappingStrategy;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -52,6 +54,7 @@ public interface ApplicationFormMapper {

List<ApplicationFormsFoundDTO> applicationFormFoundViewToDTO(List<ApplicationFormsFoundView> foundView);

@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void updateApplicationEntityFromPatchDto(ApplicationFormPatchDTO patchDTO,
@MappingTarget ApplicationFormEntity applicationFormEntity);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public ExportedSubmissionsDto grantExportEntityToExportedSubmissions(GrantExport
exportedSubmissionsDto.status(grantExportEntity.getStatus());
exportedSubmissionsDto.name(mapExportedSubmissionName(grantExportEntity));
exportedSubmissionsDto.submittedDate(mapExportedSubmissionSubmittedDate(grantExportEntity));
exportedSubmissionsDto.submissionName(mapExportedSubmissionSubmissionName(grantExportEntity));

return exportedSubmissionsDto.build();
}
Expand Down Expand Up @@ -96,6 +97,20 @@ public ZonedDateTime mapExportedSubmissionSubmittedDate(GrantExportEntity grantE

return submission.get().getSubmittedDate();
}

@Override
public String mapExportedSubmissionSubmissionName(GrantExportEntity grantExportEntity) {
log.info("Getting submission name from grant export {} and submission {}", grantExportEntity.getId(),
grantExportEntity.getId().getSubmissionId());
final UUID submissionId = grantExportEntity.getId().getSubmissionId();
final Optional<Submission> submission = submissionRepository.findById(submissionId);
if (submission.isEmpty()) {
log.error("Submission not found for id: {}", submissionId);
return null;
}
return submission.get().getSubmissionName();
}

private UUID grantExportEntityIdSubmissionId(GrantExportEntity grantExportEntity) {
if (grantExportEntity == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public interface GrantExportMapper {
@Mapping(target = "name", expression = "java(mapExportedSubmissionName(grantExportEntity))")
@Mapping(target ="status", source = "status")
@Mapping(target = "submittedDate", expression = "java(mapExportedSubmissionSubmittedDate(grantExportEntity))")
@Mapping(target = "submissionName", expression = "java(mapExportedSubmissionSubmissionName(grantExportEntity))")
ExportedSubmissionsDto grantExportEntityToExportedSubmissions(GrantExportEntity grantExportEntity);

default String mapExportedSubmissionName(GrantExportEntity grantExportEntity) {
Expand All @@ -31,4 +32,8 @@ default ZonedDateTime mapExportedSubmissionSubmittedDate(GrantExportEntity grant
return null;
}

default String mapExportedSubmissionSubmissionName(GrantExportEntity grantExportEntity) {
return "";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ Optional<SpotlightBatch> findByStatusAndSpotlightSubmissionsSizeLessThan(

Optional<List<SpotlightBatch>> findByStatus(@Param("status") SpotlightBatchStatus status);

@Query("select s from SpotlightBatch s inner join s.spotlightSubmissions spotlightSubmissions where s.status =:status and spotlightSubmissions.mandatoryQuestions.gapId = :gapId")
Optional<SpotlightBatch> findByStatusAndSpotlightSubmissions_MandatoryQuestions_GapId(
@Query("select s from SpotlightBatch s inner join s.spotlightSubmissions spotlightSubmissions where s.status =:status and spotlightSubmissions.mandatoryQuestions.gapId = :gapId ORDER BY s.created DESC")
List<SpotlightBatch> findByStatusAndSpotlightSubmissions_MandatoryQuestions_GapId(
@Param("status") SpotlightBatchStatus status, @Param("gapId") String gapId);

}
Loading
Loading