Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VUU70: Add resource for application layouts #78

Merged
merged 75 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
444364d
VUU25: Initialise module
cfisher-scottlogic Sep 21, 2023
3e8e738
VUU25: Basic Controller infrastructure
cfisher-scottlogic Sep 21, 2023
7061d64
VUU25: Implement Layout & Metadata models
cfisher-scottlogic Sep 21, 2023
d444cad
VUU25: Add OpenAPI docs (Swagger)
cfisher-scottlogic Sep 22, 2023
2bb9c0a
VUU25: Add basic H2 database
cfisher-scottlogic Sep 22, 2023
55aa632
VUU25: Implement DTOs
cfisher-scottlogic Sep 22, 2023
e0d6728
VUU25: Change server port and swagger URL
cfisher-scottlogic Sep 26, 2023
2cbb5e2
VUU25: Wire up Controller, Service and Repository for full implementa…
cfisher-scottlogic Sep 26, 2023
4f3f4cb
VUU25: Implement model mapper to replace manual entity<->DTO conversion
cfisher-scottlogic Sep 26, 2023
9b2f6ff
VUU25: Fix one-to-one entity persistence issue
cfisher-scottlogic Sep 28, 2023
a7ed2bd
VUU25: Pluralise layout endpoint resource
cfisher-scottlogic Oct 4, 2023
ed7f151
VUU25: Amend annotations, javadocs, and updateLayout signature in the…
cfisher-scottlogic Oct 4, 2023
7d72461
VUU25: Add javadocs to definition field in LayoutDTOs
cfisher-scottlogic Oct 4, 2023
2653ab1
VUU25: Remove no args constructor on entities
cfisher-scottlogic Oct 4, 2023
29a71ce
VUU25: Replace layoutRepository with metadataRepository in LayoutServ…
cfisher-scottlogic Oct 4, 2023
42f4863
VUU25: Add date to create layout HTTP response body
cfisher-scottlogic Oct 4, 2023
10e53ad
VUU25: Introduce MetadataService
cfisher-scottlogic Oct 4, 2023
715cbac
VUU25: Create layout controller unit tests (backend)
cfisher-scottlogic Oct 3, 2023
9895e10
VUU25: Create layout integration tests (backend)
cfisher-scottlogic Oct 5, 2023
3990408
VUU25: Add global exception handling to give appropriate HTTP responses
cfisher-scottlogic Oct 5, 2023
13142bb
VUU25: Rename LayoutResponseDTO to GetLayoutResponseDTO
cfisher-scottlogic Oct 5, 2023
681dd93
VUU25: Reformatting
cfisher-scottlogic Oct 5, 2023
7b5dc5a
VUU25: Make deleteLayout generate 404 if layout does not exist (backend)
cfisher-scottlogic Oct 5, 2023
09b9e73
VUU25: Downgrade Java 17 -> 11 and Springboot 3 -> 2
cfisher-scottlogic Oct 9, 2023
dd5c120
VUU25: Change DB persistence from file to in-memory
cfisher-scottlogic Oct 9, 2023
594e454
Merge branch 'VUU25-layout-server' into VUU25-layout-server-with-tests
cfisher-scottlogic Oct 9, 2023
07c179a
VUU25: Fix issues caused by downgrading Java & Springboot
cfisher-scottlogic Oct 9, 2023
5fcb328
VUU25: Write description for layout-server pom.xml
cfisher-scottlogic Oct 10, 2023
1c05d6b
VUU25: Implement interface for Metadata DTOs
cfisher-scottlogic Oct 10, 2023
a0b2630
Merge branch 'VUU25-layout-server' into VUU25-layout-server-with-tests
cfisher-scottlogic Oct 10, 2023
3c32c6a
VUU25: Create layout service unit tests (backend)
cfisher-scottlogic Oct 10, 2023
3d5697b
VUU25: Add test for transactional nature of LayoutService.createLayout()
cfisher-scottlogic Oct 10, 2023
3d25ff8
VUU25: Remove unnecessary LayoutController unit tests
cfisher-scottlogic Oct 10, 2023
b9ec7ca
VUU25: Mock out model mapper in LayoutController tests
cfisher-scottlogic Oct 10, 2023
bee7f43
VUU25: Remove TODO
cfisher-scottlogic Oct 11, 2023
67e5a52
Merge branch 'main' into VUU25-layout-server
cfisher-scottlogic Oct 11, 2023
6629880
VUU25: Move 'updated date' logic from DTO to service for update layou…
cfisher-scottlogic Oct 11, 2023
90545b0
VUU25: Move updateLayout logic from mapper in controller to setters i…
cfisher-scottlogic Oct 11, 2023
416028f
Merge branch 'VUU25-layout-server' into VUU25-layout-server-with-tests
cfisher-scottlogic Oct 11, 2023
6f97b5d
VUU25: Fix tests for recent changes in layout server
cfisher-scottlogic Oct 11, 2023
6116b28
VUU25: Remove unnecessary autogenerated content
cfisher-scottlogic Oct 11, 2023
4613103
VUU25: Make create layout (with a valid layout) integration test more…
cfisher-scottlogic Oct 11, 2023
6c0de9a
VUU25: Increase max length of screenshot column
cfisher-scottlogic Oct 16, 2023
ae2d2cc
VUU25: Change create layout endpoint to return whole layout object
cfisher-scottlogic Oct 16, 2023
00bb5d2
Merge branch 'VUU25-layout-server' into VUU25-layout-server-with-tests
cfisher-scottlogic Oct 16, 2023
4d46371
VUU25: Fix tests for new create layout response
cfisher-scottlogic Oct 16, 2023
8a6d60a
VUU25: Simplify LayoutControllerTest test name
cfisher-scottlogic Oct 16, 2023
d0ca229
VUU25: Fix tests for changed unidirectional Layout<->Metadata relatio…
cfisher-scottlogic Oct 16, 2023
e097743
VUU-70: Create resource for application layouts
pling-scottlogic Oct 13, 2023
b5e0476
VUU-70: Add integration tests
pling-scottlogic Oct 16, 2023
957148b
VUU-70: Add unit tests for controller
pling-scottlogic Oct 17, 2023
fe5c279
VUU-70: Fix service test
pling-scottlogic Oct 17, 2023
fc8e069
VUU-70: Extract constant for default layout file
pling-scottlogic Oct 17, 2023
108b087
VUU-70: Adjust whitespace
pling-scottlogic Oct 17, 2023
fe796e6
VUU-70: Use AttributeConverter for JsonNode
pling-scottlogic Oct 18, 2023
c231404
VUU-70: Remove DTO annotations
pling-scottlogic Oct 19, 2023
cc13be9
VUU-70: Rename method
pling-scottlogic Oct 19, 2023
306dbec
VUU-70: Refactor getApplicationLayout
pling-scottlogic Oct 20, 2023
c547efa
VUU-70: Standardise DTO casing
pling-scottlogic Oct 20, 2023
5fbc41b
VUU-70: Return 500 on failed JSON read
pling-scottlogic Oct 20, 2023
27ceb94
VUU-70: Adjust exception handling
pling-scottlogic Oct 23, 2023
8602b70
VUU-70: Implement custom error responses
pling-scottlogic Oct 23, 2023
4a8b62e
VUU-70: Update javadocs
pling-scottlogic Oct 23, 2023
3772c26
VUU-70: Revert application properties
pling-scottlogic Oct 24, 2023
3cfbe20
VUU-70: Add DefaultApplicationLayoutLoader
pling-scottlogic Oct 24, 2023
4c2b809
VUU-70: Rename controller test methods
pling-scottlogic Oct 24, 2023
d9b27da
VUU-70: Rename header key for username
pling-scottlogic Oct 26, 2023
8f15b28
VUU-70: Remove POST endpoint
pling-scottlogic Oct 26, 2023
7ab6d30
VUU-70: Remove @Validated annotation
pling-scottlogic Nov 1, 2023
5ad3a79
VUU-70: Make integration test constants private
pling-scottlogic Nov 1, 2023
6c93a6a
merge main in VUU-70
vferraro-scottlogic Nov 6, 2023
72e1370
Merge branch 'main' of https://github.com/ScottLogic/finos-vuu into V…
vferraro-scottlogic Nov 6, 2023
398a7e4
VUU-70 improve exception handling and clean-up
vferraro-scottlogic Nov 7, 2023
67c3672
VUU-70 fix comment
vferraro-scottlogic Nov 7, 2023
881afe1
VUU-70 remove helper method
vferraro-scottlogic Nov 8, 2023
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.finos.vuu.layoutserver.config;

import lombok.RequiredArgsConstructor;
import org.finos.vuu.layoutserver.dto.request.LayoutRequestDTO;
import org.finos.vuu.layoutserver.dto.response.MetadataResponseDTO;
import org.finos.vuu.layoutserver.dto.request.LayoutRequestDto;
import org.finos.vuu.layoutserver.dto.response.MetadataResponseDto;
import org.finos.vuu.layoutserver.model.Layout;
import org.finos.vuu.layoutserver.model.Metadata;
import org.finos.vuu.layoutserver.service.LayoutService;
Expand All @@ -20,14 +20,14 @@ public class MappingConfig {
public ModelMapper modelMapper() {
ModelMapper mapper = new ModelMapper();

mapper.typeMap(LayoutRequestDTO.class, Layout.class)
mapper.typeMap(LayoutRequestDto.class, Layout.class)
.addMappings(m -> m.skip(Layout::setId));

mapper.typeMap(Metadata.class, MetadataResponseDTO.class)
mapper.typeMap(Metadata.class, MetadataResponseDto.class)
.addMappings(m -> m.map(
metadata -> layoutService.getLayoutByMetadataId(metadata.getId()),
MetadataResponseDTO::setLayoutId));
MetadataResponseDto::setLayoutId));

return mapper;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.finos.vuu.layoutserver.controller;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import org.finos.vuu.layoutserver.dto.response.ApplicationLayoutDto;
import org.finos.vuu.layoutserver.service.ApplicationLayoutService;
import org.modelmapper.ModelMapper;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
@RequestMapping("/application-layouts")
public class ApplicationLayoutController {

private final ApplicationLayoutService service;
private final ModelMapper mapper;

/**
* Gets the persisted application layout for the requesting user. If the requesting user does not have an
* application layout persisted, a default layout with a null username is returned instead. No more than one
* application layout can be persisted for a given user.
*
* @return the application layout
*/
@ResponseStatus(HttpStatus.OK)
@GetMapping
public ApplicationLayoutDto getApplicationLayout(@RequestHeader("username") String username) {
return mapper.map(service.getApplicationLayout(username), ApplicationLayoutDto.class);
}

/**
* Creates or updates the unique application layout for the requesting user.
*
* @param layoutDefinition JSON representation of the application layout to be created
* @param username the user making the request
*/
@ResponseStatus(HttpStatus.CREATED)
@PutMapping
public void persistApplicationLayout(@RequestHeader("username") String username, @RequestBody JsonNode layoutDefinition) {
service.persistApplicationLayout(username, layoutDefinition);
}

/**
* Deletes the application layout for the requesting user. A 404 will be returned if there is no existing
* application layout.
*
* @param username the user making the request
*/
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping
public void deleteApplicationLayout(@RequestHeader("username") String username) {
service.deleteApplicationLayout(username);
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
package org.finos.vuu.layoutserver.controller;

import java.util.List;
import java.util.UUID;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.finos.vuu.layoutserver.dto.request.LayoutRequestDTO;
import org.finos.vuu.layoutserver.dto.response.LayoutResponseDTO;
import org.finos.vuu.layoutserver.dto.response.MetadataResponseDTO;
import org.finos.vuu.layoutserver.dto.request.LayoutRequestDto;
import org.finos.vuu.layoutserver.dto.response.LayoutResponseDto;
import org.finos.vuu.layoutserver.dto.response.MetadataResponseDto;
import org.finos.vuu.layoutserver.model.Layout;
import org.finos.vuu.layoutserver.service.LayoutService;
import org.finos.vuu.layoutserver.service.MetadataService;
import org.modelmapper.ModelMapper;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;
import java.util.UUID;

@RequiredArgsConstructor
@RestController
Expand All @@ -41,8 +34,8 @@ public class LayoutController {
*/
@ResponseStatus(HttpStatus.OK)
@GetMapping("/{id}")
public LayoutResponseDTO getLayout(@PathVariable UUID id) {
return mapper.map(layoutService.getLayout(id), LayoutResponseDTO.class);
public LayoutResponseDto getLayout(@PathVariable UUID id) {
return mapper.map(layoutService.getLayout(id), LayoutResponseDto.class);
}

/**
Expand All @@ -52,11 +45,11 @@ public LayoutResponseDTO getLayout(@PathVariable UUID id) {
*/
@ResponseStatus(HttpStatus.OK)
@GetMapping("/metadata")
public List<MetadataResponseDTO> getMetadata() {
public List<MetadataResponseDto> getMetadata() {

return metadataService.getMetadata()
.stream()
.map(metadata -> mapper.map(metadata, MetadataResponseDTO.class))
.map(metadata -> mapper.map(metadata, MetadataResponseDto.class))
.collect(java.util.stream.Collectors.toList());
}

Expand All @@ -68,12 +61,12 @@ public List<MetadataResponseDTO> getMetadata() {
*/
@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public LayoutResponseDTO createLayout(@Valid @RequestBody LayoutRequestDTO layoutToCreate) {
public LayoutResponseDto createLayout(@RequestBody @Valid LayoutRequestDto layoutToCreate) {
Layout layout = mapper.map(layoutToCreate, Layout.class);

Layout createdLayout = layoutService.getLayout(layoutService.createLayout(layout));

return mapper.map(createdLayout, LayoutResponseDTO.class);
return mapper.map(createdLayout, LayoutResponseDto.class);
}

/**
Expand All @@ -84,7 +77,7 @@ public LayoutResponseDTO createLayout(@Valid @RequestBody LayoutRequestDTO layou
*/
@ResponseStatus(HttpStatus.NO_CONTENT)
@PutMapping("/{id}")
public void updateLayout(@PathVariable UUID id, @Valid @RequestBody LayoutRequestDTO layout) {
public void updateLayout(@PathVariable UUID id, @RequestBody @Valid LayoutRequestDto layout) {
Layout newLayout = mapper.map(layout, Layout.class);

layoutService.updateLayout(id, newLayout);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.finos.vuu.layoutserver.dto.request;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;

@Data
public class LayoutRequestDTO {
public class LayoutRequestDto {

/**
* The definition of the layout as a string (e.g. stringified JSON structure containing
Expand All @@ -18,5 +19,5 @@ public class LayoutRequestDTO {

@JsonProperty(value = "metadata", required = true)
@NotNull(message = "Metadata must not be null")
private MetadataRequestDTO metadata;
private MetadataRequestDto metadata;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.finos.vuu.layoutserver.model.BaseMetadata;

@Data
public class MetadataRequestDTO {
public class MetadataRequestDto {

@JsonUnwrapped
BaseMetadata baseMetadata;
Expand Down
cfisher-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.finos.vuu.layoutserver.dto.response;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;

@Data
public class ApplicationLayoutDto {
private String username;
private JsonNode definition;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.finos.vuu.layoutserver.dto.response;

import lombok.Data;
import org.springframework.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;

@Data
public class ErrorResponse {
private Date timestamp = new Date();
private int status;
private String error;
private List<String> messages;
private String path;

public ErrorResponse(HttpServletRequest request, List<String> messages, HttpStatus status) {
this.status = status.value();
this.error = status.getReasonPhrase();
this.path = request.getRequestURI();
this.messages = messages;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.util.UUID;

@Data
public class LayoutResponseDTO {
public class LayoutResponseDto {

private UUID id;

Expand All @@ -15,5 +15,5 @@ public class LayoutResponseDTO {
*/
private String definition;

private MetadataResponseDTO metadata;
private MetadataResponseDto metadata;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.finos.vuu.layoutserver.dto.response;

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Data;
import org.finos.vuu.layoutserver.model.BaseMetadata;

import java.time.LocalDate;
import java.util.UUID;

@Data
public class MetadataResponseDTO {
public class MetadataResponseDto {

private UUID layoutId;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
package org.finos.vuu.layoutserver.exceptions;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import org.finos.vuu.layoutserver.dto.response.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(NoSuchElementException.class)
public ResponseEntity<String> handleNotFound(NoSuchElementException ex) {
return new ResponseEntity<>(ex.getMessage(),
org.springframework.http.HttpStatus.NOT_FOUND);
public ResponseEntity<ErrorResponse> handleNotFound(HttpServletRequest request, Exception ex) {
HttpStatus status = HttpStatus.NOT_FOUND;
return new ResponseEntity<>(new ErrorResponse(request, List.of(ex.getMessage()), status), status);
}

@ExceptionHandler({
HttpMessageNotReadableException.class,
MethodArgumentTypeMismatchException.class})
public ResponseEntity<String> handleBadRequest(Exception ex) {
return new ResponseEntity<>(ex.getMessage(),
org.springframework.http.HttpStatus.BAD_REQUEST);
HttpMessageNotReadableException.class,
MethodArgumentTypeMismatchException.class})
public ResponseEntity<ErrorResponse> handleBadRequest(HttpServletRequest request, Exception ex) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return new ResponseEntity<>(new ErrorResponse(request, List.of(ex.getMessage()), status), status);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
public ResponseEntity<ErrorResponse> handleMethodArgumentNotValid(HttpServletRequest request, MethodArgumentNotValidException ex) {
HttpStatus status = HttpStatus.BAD_REQUEST;
List<String> errors = ex.getFieldErrors()
.stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.collect(Collectors.toList());
.stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.collect(Collectors.toList());
return new ResponseEntity<>(new ErrorResponse(request, errors, status), status);
}

@ExceptionHandler(InternalServerErrorException.class)
public ResponseEntity<ErrorResponse> handleInternalServerError(HttpServletRequest request, Exception ex) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return new ResponseEntity<>(new ErrorResponse(request, List.of(ex.getMessage()), status), status);

return new ResponseEntity<>(errors.toString(),
org.springframework.http.HttpStatus.BAD_REQUEST);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.finos.vuu.layoutserver.exceptions;

public class InternalServerErrorException extends RuntimeException {
public InternalServerErrorException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.finos.vuu.layoutserver.model;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.finos.vuu.layoutserver.utils.JsonNodeConverter;

import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Id;

@Data
@Entity
@RequiredArgsConstructor
@AllArgsConstructor
public class ApplicationLayout {
@Id
private String username;

@Convert(converter = JsonNodeConverter.class)
cfisher-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
@Column(columnDefinition = "JSON")
private JsonNode definition;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package org.finos.vuu.layoutserver.model;

import lombok.Data;

import javax.persistence.Embeddable;
import javax.persistence.Lob;
import lombok.Data;

@Data
@Embeddable
Expand Down
Loading
Loading