Skip to content

Commit

Permalink
Merge pull request #151 from cabinetoffice/release/7.0
Browse files Browse the repository at this point in the history
Release/7.0
  • Loading branch information
iaincooper-tco authored Jan 17, 2024
2 parents 3320f82 + bc90f4a commit 3dba5f3
Show file tree
Hide file tree
Showing 107 changed files with 2,094 additions and 1,408 deletions.
1 change: 0 additions & 1 deletion DUMMY

This file was deleted.

12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ which communicates with a PostgreSQL database, a SQS queue, S3 buckets and Conte
## Starting the app (Dev)

The Apply for a Grant admin backend is a maven project. This means to run the app locally you must have Maven installed
and setup on your machine. For this apps other dependencies, please see the [dependencies section](#Dependencies). Once
all dependencies and Maven is setup, you can start the app locally by running a simple maven command:
and setup on your machine. For this app's other dependencies, please see the [dependencies section](#Dependencies). Once
all dependencies and Maven is set up, you can start the app locally by running a simple maven command:

```
mvn spring-boot:run
Expand All @@ -20,7 +20,7 @@ and `/login`).
Before you can make your requests via Postman or the FE, you will to authenticate via login endpoint to create a
session. Login requires a valid JWT signed by COLA that has not expired provided in an Authorization header as a Bearer
token.
For local development purposes, there are three properties in `application.properties` that you can set to login in as a
For local development purposes, there are three properties in `application.properties` that you can set to log in as a
given user without a valid JWT.

By setting `debug.ignore-jwt` equal to `true`, the app will no longer look for a JWT and will instead log you in as an
Expand Down Expand Up @@ -221,8 +221,8 @@ mvn flyway:migrate
```

This will run all scripts in the directory listed above. Migration history is automatically stored in a table called
`flyway_schema_history`. This means that whenever a new script is added to the directory and you run the Maven migration
command again, it will only run the new script. Therefore once the database is established and deployed
`flyway_schema_history`. This means that whenever a new script is added to the directory, and you run the Maven migration
command again, it will only run the new script. Therefore, once the database is established and deployed
in long-term environments, when making changes to schemas, you do not modify existing scripts that have already
been migrated - you must add new scripts. A more eloquent explanation and example is provided
[here](https://flywaydb.org/documentation/getstarted/why).
Expand Down Expand Up @@ -270,7 +270,7 @@ a commit.**
from [here](https://repo.spring.io/artifactory/release/io/spring/javaformat/io.spring.javaformat.eclipse.site/0.0.34/io.spring.javaformat.eclipse.site-0.0.34.zip)
(it can't be installed through the Eclipse Marketplace).
2. Navigate to Help > Install New Software.
3. On this window, click the Add button along the right hand side.
3. On this window, click the Add button along the right-hand side.
4. In the next window, click Archive and select the zip file you downloaded in step 1.
5. You may be asked to install an unauthorised plugin, due to it not coming directly from the Marketplace. Tick the
plugin and click accept.
Expand Down
4 changes: 2 additions & 2 deletions pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Please describe the tests that you ran to verify your changes:

- [ ] Integration Test (if applicable)

- [ ] End to End Test (if applicable)
- [ ] End-to-End Test (if applicable)

## Screenshots (if appropriate):

Expand All @@ -33,4 +33,4 @@ Please attach screenshots of the change if it is a UI change:
- [ ] 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.
- [ ] I have run cypress tests, and they all pass locally.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpotlightPublisherHeaderValidator {
public @interface LambdasHeaderValidator {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package gov.cabinetoffice.gap.adminbackend.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 = "completion-statistics-scheduler")
public class CompletionStatisticsSchedulerConfigProperties {

@NotNull
private String queue;

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ public CMAClient getContentfulManagementClient() {

@Bean
public CDAClient getContentfulDeliveryClient() {
CDAClient client = CDAClient.builder().setToken(configProperties.getDeliveryAPIAccessToken())
return CDAClient.builder().setToken(configProperties.getDeliveryAPIAccessToken())
.setSpace(configProperties.getSpaceId()).setEnvironment(configProperties.getEnvironmentId()).build();

return client;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package gov.cabinetoffice.gap.adminbackend.config;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Configuration("lambdaProperties")
@ConfigurationProperties(prefix = "lambda")
public class LambdaSecretConfigProperties {

private String secret;

private String privateKey;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package gov.cabinetoffice.gap.adminbackend.config;

import gov.cabinetoffice.gap.adminbackend.security.interceptors.AuthorizationHeaderInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@RequiredArgsConstructor
@Configuration
public class LambdasInterceptor implements WebMvcConfigurer {

private static final String UUID_REGEX_STRING = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}";

private final SpotlightPublisherConfigProperties spotlightPublisherConfigProperties;

private final LambdaSecretConfigProperties lambdaSecretConfigProperties;

@Bean(name = "spotlightPublisherLambdaInterceptor")
public AuthorizationHeaderInterceptor spotlightPublisherLambdaInterceptor() {
return new AuthorizationHeaderInterceptor(spotlightPublisherConfigProperties.getSecret(),
spotlightPublisherConfigProperties.getPrivateKey());
}

@Bean(name = "submissionExportAndScheduledPublishingLambdasInterceptor")
AuthorizationHeaderInterceptor submissionExportAndScheduledPublishingLambdasInterceptor() {
return new AuthorizationHeaderInterceptor(lambdaSecretConfigProperties.getSecret(),
lambdaSecretConfigProperties.getPrivateKey());
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(spotlightPublisherLambdaInterceptor())
.addPathPatterns("/spotlight-submissions/**", "/spotlight-batch/**").order(Ordered.HIGHEST_PRECEDENCE);

registry.addInterceptor(submissionExportAndScheduledPublishingLambdasInterceptor())
.addPathPatterns(
"/emails/sendLambdaConfirmationEmail", "/submissions/{submissionId:" + UUID_REGEX_STRING
+ "}/export-batch/{batchExportId:" + UUID_REGEX_STRING + "}/submission",
"/submissions/*/export-batch/*/status",
"/submissions/{submissionId:" + UUID_REGEX_STRING + "}/export-batch/{batchExportId:"
+ UUID_REGEX_STRING + "}/s3-object-key",
"/export-batch/{exportId:" + UUID_REGEX_STRING + "}/outstandingCount",
"/grant-advert/lambda/{grantAdvertId:" + UUID_REGEX_STRING + "}/publish",
"/grant-advert/lambda/{grantAdvertId:" + UUID_REGEX_STRING + "}/unpublish",
"/application-forms/lambda/**")
.order(Ordered.HIGHEST_PRECEDENCE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ public class SpotlightPublisherConfigProperties {

private String secret;

private String privateKey;

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gov.cabinetoffice.gap.adminbackend.constants;

public class SpotlightExports {

public static final String REQUIRED_CHECKS_FILENAME = "required_checks.zip";

public static final String SPOTLIGHT_CHECKS_FILENAME = "spotlight_checks.zip";

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@
import java.util.AbstractMap;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ValidationMaps {

public final static Map<String, Object> SHORT_ANSWER_VALIDATION = Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("minLength", 1),
new AbstractMap.SimpleEntry<String, Object>("maxLength", 250));
public final static ConcurrentMap<String, Object> SHORT_ANSWER_VALIDATION = new ConcurrentHashMap<>(
Map.ofEntries(new AbstractMap.SimpleEntry<String, Object>("minLength", 1),
new AbstractMap.SimpleEntry<String, Object>("maxLength", 250)));

public final static Map<String, Object> LONG_ANSWER_VALIDATION = Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("minLength", 2),
new AbstractMap.SimpleEntry<String, Object>("maxLength", 6000));
public final static ConcurrentMap<String, Object> LONG_ANSWER_VALIDATION = new ConcurrentHashMap<>(
Map.ofEntries(new AbstractMap.SimpleEntry<String, Object>("minLength", 2),
new AbstractMap.SimpleEntry<String, Object>("maxLength", 6000)));

public final static Map<String, Object> NUMERIC_ANSWER_VALIDATION = Map
.ofEntries(new AbstractMap.SimpleEntry<String, Object>("greaterThanZero", true));
public final static ConcurrentMap<String, Object> NUMERIC_ANSWER_VALIDATION = new ConcurrentHashMap<>(
Map.ofEntries((new AbstractMap.SimpleEntry<String, Object>("greaterThanZero", true))));

public final static Map<String, Object> SINGLE_FILE_UPLOAD_VALIDATION = Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("maxFileSizeMB", 300),
new AbstractMap.SimpleEntry<String, Object>("allowedTypes",
new String[] { "DOC", "DOCX", "ODT", "PDF", "XLS", "XLSX", "ZIP" }));
public final static ConcurrentMap<String, Object> SINGLE_FILE_UPLOAD_VALIDATION = new ConcurrentHashMap<>(
Map.ofEntries(new AbstractMap.SimpleEntry<String, Object>("maxFileSizeMB", 300),
new AbstractMap.SimpleEntry<String, Object>("allowedTypes",
new String[] { "DOC", "DOCX", "ODT", "PDF", "XLS", "XLSX", "ZIP" })));

public final static Map<String, Object> NO_VALIDATION = Collections.emptyMap();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gov.cabinetoffice.gap.adminbackend.controllers;

import gov.cabinetoffice.gap.adminbackend.annotations.LambdasHeaderValidator;
import gov.cabinetoffice.gap.adminbackend.dtos.GenericPostResponseDTO;
import gov.cabinetoffice.gap.adminbackend.dtos.application.*;
import gov.cabinetoffice.gap.adminbackend.dtos.errors.GenericErrorDTO;
Expand All @@ -22,7 +23,6 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
Expand All @@ -45,8 +45,6 @@ public class ApplicationFormController {

private final ApplicationFormService applicationFormService;

private final SecretAuthService secretAuthService;

private final GrantAdvertService grantAdvertService;

private final SchemeService schemeService;
Expand Down Expand Up @@ -157,11 +155,10 @@ public ResponseEntity<Void> deleteApplicationForm(@PathVariable @NotNull Integer
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "404", description = "Application not found with given id",
content = @Content(mediaType = "application/json")), })

public ResponseEntity<Void> removeApplicationAttachedToGrantAdvert(
@RequestHeader(HttpHeaders.AUTHORIZATION) String authHeader, @PathVariable @NotNull UUID grantAdvertId) {
@LambdasHeaderValidator
public ResponseEntity<Void> removeApplicationAttachedToGrantAdvert(@PathVariable @NotNull UUID grantAdvertId) {
try {
secretAuthService.authenticateSecret(authHeader);

Integer schemeId = grantAdvertService.getAdvertById(grantAdvertId, true).getScheme().getId();
Optional<ApplicationFormEntity> applicationForm = applicationFormService
.getOptionalApplicationFromSchemeId(schemeId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest
@ExceptionHandler(value = { ConstraintViolationException.class })
protected ResponseEntity<Object> handleConflict(ConstraintViolationException ex, WebRequest request) {
log.error(ex.getMessage(), ex);
ConstraintViolation constraintViolation = ex.getConstraintViolations().stream().findFirst().get();
ConstraintViolation<?> constraintViolation = ex.getConstraintViolations().stream().findFirst().get();

// if validation violation was raised by NotAllNullValidator, then map to
// ClassErrorsDto. Else, map to FieldErrorsDto
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package gov.cabinetoffice.gap.adminbackend.controllers;

import gov.cabinetoffice.gap.adminbackend.annotations.LambdasHeaderValidator;
import gov.cabinetoffice.gap.adminbackend.dtos.SendLambdaExportEmailDTO;
import gov.cabinetoffice.gap.adminbackend.services.GovNotifyService;
import gov.cabinetoffice.gap.adminbackend.services.SecretAuthService;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

Expand All @@ -22,19 +24,16 @@ public class GovNotifyController {

private final GovNotifyService govNotifyService;

private final SecretAuthService secretAuthService;

@PostMapping("/sendLambdaConfirmationEmail")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Email sent successfully.",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "400", description = "Bad request body",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "500", description = "Email failed to send") })
@LambdasHeaderValidator
public ResponseEntity<String> sendLambdaExportEmail(
final @RequestBody @Valid SendLambdaExportEmailDTO lambdaExportEmailDTO,
@RequestHeader(HttpHeaders.AUTHORIZATION) String authHeader) {
secretAuthService.authenticateSecret(authHeader);
final @RequestBody @Valid SendLambdaExportEmailDTO lambdaExportEmailDTO) {

if (govNotifyService.sendLambdaExportEmail(lambdaExportEmailDTO))
return ResponseEntity.ok("Email successfully sent");
Expand Down
Loading

0 comments on commit 3dba5f3

Please sign in to comment.