forked from finos/vuu
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
VUU-81 layout definition as ObjectNode
- Loading branch information
1 parent
6a24965
commit c988134
Showing
15 changed files
with
1,380 additions
and
0 deletions.
There are no files selected for viewing
55 changes: 55 additions & 0 deletions
55
...rver/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.node.ObjectNode; | ||
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 ObjectNode 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); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
layout-server/src/main/java/org/finos/vuu/layoutserver/dto/request/LayoutRequestDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.finos.vuu.layoutserver.dto.request; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import lombok.Data; | ||
|
||
import javax.validation.constraints.NotNull; | ||
|
||
@Data | ||
public class LayoutRequestDto { | ||
|
||
@JsonProperty(value = "definition", required = true) | ||
@NotNull(message = "Definition must not be null") | ||
private ObjectNode definition; | ||
|
||
@JsonProperty(value = "metadata", required = true) | ||
@NotNull(message = "Metadata must not be null") | ||
private MetadataRequestDto metadata; | ||
} |
10 changes: 10 additions & 0 deletions
10
...ut-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.node.ObjectNode; | ||
import lombok.Data; | ||
|
||
@Data | ||
public class ApplicationLayoutDto { | ||
private String username; | ||
private ObjectNode definition; | ||
} |
18 changes: 18 additions & 0 deletions
18
layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/LayoutResponseDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.finos.vuu.layoutserver.dto.response; | ||
|
||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import lombok.Data; | ||
import org.finos.vuu.layoutserver.utils.ObjectNodeConverter; | ||
|
||
import javax.persistence.Column; | ||
import javax.persistence.Convert; | ||
import java.util.UUID; | ||
|
||
@Data | ||
public class LayoutResponseDto { | ||
|
||
private UUID id; | ||
private ObjectNode definition; | ||
|
||
private MetadataResponseDto metadata; | ||
} |
25 changes: 25 additions & 0 deletions
25
layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.node.ObjectNode; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import lombok.RequiredArgsConstructor; | ||
import org.finos.vuu.layoutserver.utils.ObjectNodeConverter; | ||
|
||
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 = ObjectNodeConverter.class) | ||
@Column(columnDefinition = "JSON") | ||
private ObjectNode definition; | ||
} |
30 changes: 30 additions & 0 deletions
30
layout-server/src/main/java/org/finos/vuu/layoutserver/model/Layout.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.finos.vuu.layoutserver.model; | ||
|
||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import lombok.Data; | ||
import org.finos.vuu.layoutserver.utils.ObjectNodeConverter; | ||
|
||
import javax.persistence.*; | ||
import java.util.UUID; | ||
|
||
@Data | ||
@Entity | ||
public class Layout { | ||
|
||
@Id | ||
@Column(columnDefinition = "BINARY(16)") | ||
private UUID id; | ||
|
||
@Convert(converter = ObjectNodeConverter.class) | ||
@Column(columnDefinition = "JSON") | ||
private ObjectNode definition; | ||
|
||
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) | ||
@JoinColumn(name = "metadata_id", referencedColumnName = "id") | ||
private Metadata metadata; | ||
|
||
public void setId(UUID id) { | ||
this.id = id; | ||
this.metadata.setId(id); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package org.finos.vuu.layoutserver.service; | ||
|
||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import lombok.RequiredArgsConstructor; | ||
import org.finos.vuu.layoutserver.model.ApplicationLayout; | ||
import org.finos.vuu.layoutserver.repository.ApplicationLayoutRepository; | ||
import org.finos.vuu.layoutserver.utils.DefaultApplicationLayoutLoader; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.dao.EmptyResultDataAccessException; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.NoSuchElementException; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class ApplicationLayoutService { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(ApplicationLayoutService.class); | ||
private final ApplicationLayoutRepository repository; | ||
private final DefaultApplicationLayoutLoader defaultLoader; | ||
|
||
public void persistApplicationLayout(String username, ObjectNode layoutDefinition) { | ||
repository.save(new ApplicationLayout(username, layoutDefinition)); | ||
} | ||
|
||
public ApplicationLayout getApplicationLayout(String username) { | ||
return repository.findById(username).orElseGet(() -> { | ||
logger.info("No application layout for user, returning default"); | ||
return defaultLoader.getDefaultLayout(); | ||
}); | ||
} | ||
|
||
public void deleteApplicationLayout(String username) { | ||
try { | ||
repository.deleteById(username); | ||
} catch (EmptyResultDataAccessException e) { | ||
throw new NoSuchElementException("No layout found for user: " + username); | ||
} | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.finos.vuu.layoutserver.utils; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import org.finos.vuu.layoutserver.exceptions.InternalServerErrorException; | ||
import org.finos.vuu.layoutserver.model.ApplicationLayout; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.core.io.ClassPathResource; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.io.IOException; | ||
|
||
@Component | ||
public class DefaultApplicationLayoutLoader { | ||
private static final String DEFAULT_LAYOUT_FILE = "defaultApplicationLayout.json"; | ||
private static ApplicationLayout defaultLayout; | ||
|
||
@Bean | ||
public ApplicationLayout getDefaultLayout() { | ||
if (defaultLayout == null) { | ||
loadDefaultLayout(); | ||
} | ||
return defaultLayout; | ||
} | ||
|
||
private void loadDefaultLayout() { | ||
ObjectNode definition = loadDefaultLayoutJsonFile(); | ||
defaultLayout = new ApplicationLayout(null, definition); | ||
} | ||
|
||
private ObjectNode loadDefaultLayoutJsonFile() { | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
ClassPathResource resource = new ClassPathResource(DEFAULT_LAYOUT_FILE); | ||
try { | ||
return objectMapper.readValue(resource.getInputStream(), ObjectNode.class); | ||
} catch (IOException e) { | ||
throw new InternalServerErrorException("Failed to read default application layout"); | ||
} | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
layout-server/src/main/java/org/finos/vuu/layoutserver/utils/ObjectNodeConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package org.finos.vuu.layoutserver.utils; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import javax.persistence.AttributeConverter; | ||
import javax.persistence.Converter; | ||
import java.io.IOException; | ||
|
||
@Converter | ||
public class ObjectNodeConverter implements AttributeConverter<ObjectNode, String> { | ||
private static final Logger logger = LoggerFactory.getLogger(ObjectNodeConverter.class); | ||
private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
|
||
@Override | ||
public String convertToDatabaseColumn(ObjectNode definition) { | ||
try { | ||
return objectMapper.writeValueAsString(definition); | ||
} catch (final JsonProcessingException e) { | ||
logger.error("JSON writing error", e); | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
public ObjectNode convertToEntityAttribute(String definition) { | ||
try { | ||
return objectMapper.readValue(extractDefinition(definition), new TypeReference<>() {}); | ||
} catch (final IOException e) { | ||
logger.error("JSON reading error", e); | ||
return null; | ||
} | ||
} | ||
|
||
private String extractDefinition(String definition) { | ||
if (definition.startsWith("\"") && definition.endsWith("\"")) { | ||
definition = definition.substring(1, definition.length() - 1); | ||
} | ||
return definition.replaceAll("\\\\", ""); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
.../src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.finos.vuu.layoutserver.controller; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import org.finos.vuu.layoutserver.dto.response.ApplicationLayoutDto; | ||
import org.finos.vuu.layoutserver.model.ApplicationLayout; | ||
import org.finos.vuu.layoutserver.service.ApplicationLayoutService; | ||
import org.finos.vuu.layoutserver.utils.ObjectNodeConverter; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.Mockito; | ||
import org.modelmapper.ModelMapper; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.*; | ||
|
||
class ApplicationLayoutControllerTest { | ||
private static ApplicationLayoutService mockService; | ||
private static ApplicationLayoutController controller; | ||
private static final ModelMapper modelMapper = new ModelMapper(); | ||
private static final ObjectNodeConverter objectNodeConverter = new ObjectNodeConverter(); | ||
|
||
@BeforeEach | ||
public void setup() { | ||
mockService = Mockito.mock(ApplicationLayoutService.class); | ||
controller = new ApplicationLayoutController(mockService, modelMapper); | ||
} | ||
|
||
@Test | ||
public void getApplicationLayout_anyUsername_returnsLayoutFromService() throws JsonProcessingException { | ||
String user = "user"; | ||
ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); | ||
|
||
when(mockService.getApplicationLayout(user)) | ||
.thenReturn(new ApplicationLayout(user, definition)); | ||
|
||
ApplicationLayoutDto response = controller.getApplicationLayout(user); | ||
|
||
assertThat(response.getUsername()).isEqualTo(user); | ||
assertThat(response.getDefinition()).isEqualTo(definition); | ||
|
||
verify(mockService, times(1)).getApplicationLayout(user); | ||
} | ||
|
||
@Test | ||
public void persistApplicationLayout_anyInput_callsService() throws JsonProcessingException { | ||
String user = "user"; | ||
ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); | ||
|
||
controller.persistApplicationLayout(user, definition); | ||
|
||
verify(mockService, times(1)).persistApplicationLayout(user, definition); | ||
} | ||
|
||
@Test | ||
public void deleteApplicationLayout_anyUsername_callsService() { | ||
String user = "user"; | ||
|
||
controller.deleteApplicationLayout(user); | ||
|
||
verify(mockService, times(1)).deleteApplicationLayout(user); | ||
} | ||
} |
Oops, something went wrong.