From 8f0b5813a3563b65d0bab275c22b4c76126c2015 Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Fri, 13 May 2022 15:33:59 +0200 Subject: [PATCH 1/5] Introduce uuid field for all entities --- .../db/migration/V0.12.0__Add_uuid_column.sql | 24 +++++++ .../shogun/lib/controller/BaseController.java | 62 +++++++++++++++++++ .../shogun/lib/model/BaseEntity.java | 14 +++-- .../de/terrestris/shogun/lib/model/File.java | 9 --- .../lib/repository/BaseCrudRepository.java | 5 ++ .../lib/repository/BaseFileRepository.java | 31 ---------- .../shogun/lib/repository/FileRepository.java | 2 +- .../lib/repository/ImageFileRepository.java | 2 +- .../shogun/lib/service/BaseFileService.java | 12 +--- .../shogun/lib/service/BaseService.java | 6 ++ .../shogun/lib/service/FileService.java | 10 +-- .../shogun/lib/service/IBaseFileService.java | 4 +- .../shogun/lib/service/ImageFileService.java | 2 +- 13 files changed, 119 insertions(+), 64 deletions(-) create mode 100644 shogun-boot/src/main/resources/db/migration/V0.12.0__Add_uuid_column.sql delete mode 100644 shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseFileRepository.java diff --git a/shogun-boot/src/main/resources/db/migration/V0.12.0__Add_uuid_column.sql b/shogun-boot/src/main/resources/db/migration/V0.12.0__Add_uuid_column.sql new file mode 100644 index 000000000..33f559ba2 --- /dev/null +++ b/shogun-boot/src/main/resources/db/migration/V0.12.0__Add_uuid_column.sql @@ -0,0 +1,24 @@ +ALTER TABLE IF EXISTS shogun.applications ADD COLUMN IF NOT EXISTS uuid uuid; +ALTER TABLE IF EXISTS shogun.groups ADD COLUMN IF NOT EXISTS uuid uuid; +ALTER TABLE IF EXISTS shogun.layers ADD COLUMN IF NOT EXISTS uuid uuid; +ALTER TABLE IF EXISTS shogun.users ADD COLUMN IF NOT EXISTS uuid uuid; + +ALTER TABLE IF EXISTS shogun.permissions ADD COLUMN IF NOT EXISTS uuid uuid; +ALTER TABLE IF EXISTS shogun.groupclasspermissions ADD COLUMN IF NOT EXISTS uuid uuid; +ALTER TABLE IF EXISTS shogun.groupinstancepermissions ADD COLUMN IF NOT EXISTS uuid uuid; +ALTER TABLE IF EXISTS shogun.userclasspermissions ADD COLUMN IF NOT EXISTS uuid uuid; +ALTER TABLE IF EXISTS shogun.userinstancepermissions ADD COLUMN IF NOT EXISTS uuid uuid; + +ALTER TABLE IF EXISTS shogun.files RENAME COLUMN file_uuid TO uuid; +ALTER TABLE IF EXISTS shogun.imagefiles RENAME COLUMN file_uuid TO uuid; + +UPDATE shogun.applications SET uuid = gen_random_uuid() WHERE uuid IS NULL; +UPDATE shogun.groups SET uuid = gen_random_uuid() WHERE uuid IS NULL; +UPDATE shogun.layers SET uuid = gen_random_uuid() WHERE uuid IS NULL; +UPDATE shogun.users SET uuid = gen_random_uuid() WHERE uuid IS NULL; + +UPDATE shogun.permissions SET uuid = gen_random_uuid() WHERE uuid IS NULL; +UPDATE shogun.groupclasspermissions SET uuid = gen_random_uuid() WHERE uuid IS NULL; +UPDATE shogun.groupinstancepermissions SET uuid = gen_random_uuid() WHERE uuid IS NULL; +UPDATE shogun.userclasspermissions SET uuid = gen_random_uuid() WHERE uuid IS NULL; +UPDATE shogun.userinstancepermissions SET uuid = gen_random_uuid() WHERE uuid IS NULL; diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java index 85b3a60f4..a2b048a81 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; // TODO Specify and type extension of BaseService @Log4j2 @@ -89,6 +90,67 @@ public List findAll() { } } + @GetMapping("/uuid/{uuid}") + @ResponseStatus(HttpStatus.OK) + public S findOne(@PathVariable("uuid") UUID uuid) { + log.trace("Requested to return entity of type {} with UUID {}", + getGenericClassName(), uuid); + + try { + Optional entity = service.findOne(uuid); + + if (entity.isPresent()) { + S persistedEntity = entity.get(); + + log.trace("Successfully got entity of type {} with UUID {}", + getGenericClassName(), uuid); + + return persistedEntity; + } else { + log.error("Could not find entity of type {} with UUID {}", + getGenericClassName(), uuid); + + throw new ResponseStatusException( + HttpStatus.NOT_FOUND, + messageSource.getMessage( + "BaseController.NOT_FOUND", + null, + LocaleContextHolder.getLocale() + ) + ); + } + } catch (AccessDeniedException ade) { + log.warn("Access to entity of type {} with UUID {} is denied", + getGenericClassName(), uuid); + + throw new ResponseStatusException( + HttpStatus.NOT_FOUND, + messageSource.getMessage( + "BaseController.NOT_FOUND", + null, + LocaleContextHolder.getLocale() + ), + ade + ); + } catch (ResponseStatusException rse) { + throw rse; + } catch (Exception e) { + log.error("Error while requesting entity of type {} with UUID {}: \n {}", + getGenericClassName(), uuid, e.getMessage()); + log.trace("Full stack trace: ", e); + + throw new ResponseStatusException( + HttpStatus.INTERNAL_SERVER_ERROR, + messageSource.getMessage( + "BaseController.INTERNAL_SERVER_ERROR", + null, + LocaleContextHolder.getLocale() + ), + e + ); + } + } + @GetMapping("/{id}") @ResponseStatus(HttpStatus.OK) public S findOne(@PathVariable("id") Long entityId) { diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/BaseEntity.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/BaseEntity.java index 67057b8f8..5dc398dc7 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/BaseEntity.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/BaseEntity.java @@ -22,15 +22,13 @@ import com.vladmihalcea.hibernate.type.json.JsonBinaryType; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; -import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.TypeDef; -import org.hibernate.annotations.TypeDefs; -import org.hibernate.annotations.UpdateTimestamp; +import org.hibernate.annotations.*; import org.hibernate.envers.Audited; import javax.persistence.*; import java.io.Serializable; import java.time.OffsetDateTime; +import java.util.UUID; @MappedSuperclass @Audited @@ -56,6 +54,14 @@ public abstract class BaseEntity implements Serializable { ) private Long id; + @Column(columnDefinition = "uuid") + @Type(type="pg-uuid") + @Getter @Setter + @Schema( + description = "The UUID of the entity." + ) + private UUID uuid = UUID.randomUUID(); + @CreationTimestamp @Column(updatable = false) @Getter @Setter diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java index 9512b9d93..8bcc99519 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java @@ -37,15 +37,6 @@ @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class File extends BaseEntity { - @Column(columnDefinition = "uuid", updatable = false, nullable = false) - @Type(type="pg-uuid") - @Getter - @Schema( - description = "The (auto assigned) UUID of the file.", - accessMode = Schema.AccessMode.READ_ONLY - ) - private UUID fileUuid = UUID.randomUUID(); - @Column @Getter @Setter @Schema( diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseCrudRepository.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseCrudRepository.java index 797d53f67..3b90ea66e 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseCrudRepository.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseCrudRepository.java @@ -23,6 +23,8 @@ import javax.persistence.QueryHint; import java.util.List; +import java.util.Optional; +import java.util.UUID; @NoRepositoryBean public interface BaseCrudRepository extends RevisionRepository, CrudRepository, ShogunRevisionRepository { @@ -30,4 +32,7 @@ public interface BaseCrudRepository extends RevisionRepository findAll(); + @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true")) + Optional findByUuid(UUID uuid); + } diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseFileRepository.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseFileRepository.java deleted file mode 100644 index e25167587..000000000 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/BaseFileRepository.java +++ /dev/null @@ -1,31 +0,0 @@ -/* SHOGun, https://terrestris.github.io/shogun/ - * - * Copyright © 2020-present terrestris GmbH & Co. KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0.txt - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.terrestris.shogun.lib.repository; - -import java.util.Optional; -import java.util.UUID; -import javax.persistence.QueryHint; -import org.springframework.data.jpa.repository.QueryHints; -import org.springframework.data.repository.NoRepositoryBean; - -@NoRepositoryBean -public interface BaseFileRepository extends BaseCrudRepository { - - @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true")) - Optional findByFileUuid(UUID uuid); - -} diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/FileRepository.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/FileRepository.java index bada53f2c..5b70a1bf9 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/FileRepository.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/FileRepository.java @@ -21,4 +21,4 @@ import org.springframework.stereotype.Repository; @Repository -public interface FileRepository extends BaseFileRepository, JpaSpecificationExecutor { } +public interface FileRepository extends BaseCrudRepository, JpaSpecificationExecutor { } diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/ImageFileRepository.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/ImageFileRepository.java index f5ab7220b..3eac25718 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/ImageFileRepository.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/repository/ImageFileRepository.java @@ -21,4 +21,4 @@ import org.springframework.stereotype.Repository; @Repository -public interface ImageFileRepository extends BaseFileRepository, JpaSpecificationExecutor { } +public interface ImageFileRepository extends BaseCrudRepository, JpaSpecificationExecutor { } diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java index 359336e0a..7edab81fd 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java @@ -17,7 +17,7 @@ package de.terrestris.shogun.lib.service; import de.terrestris.shogun.lib.model.File; -import de.terrestris.shogun.lib.repository.BaseFileRepository; +import de.terrestris.shogun.lib.repository.BaseCrudRepository; import de.terrestris.shogun.properties.UploadProperties; import lombok.extern.log4j.Log4j2; import org.apache.commons.io.FileUtils; @@ -31,7 +31,6 @@ import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.util.PatternMatchUtils; import org.springframework.web.multipart.MultipartFile; @@ -39,20 +38,13 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; -import java.util.Optional; -import java.util.UUID; @Log4j2 -public abstract class BaseFileService & JpaSpecificationExecutor, S extends File> extends BaseService implements IBaseFileService { +public abstract class BaseFileService & JpaSpecificationExecutor, S extends File> extends BaseService implements IBaseFileService { @Autowired private UploadProperties uploadProperties; - @PostAuthorize("hasRole('ROLE_ADMIN') or hasPermission(returnObject.orElse(null), 'READ')") - public Optional findOne(UUID fileUuid) { - return repository.findByFileUuid(fileUuid); - } - public abstract S create(MultipartFile uploadFile, Boolean writeToSystem) throws Exception; public void isValid(MultipartFile file) throws Exception { diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java index a6cad3c2d..85d5f045a 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java @@ -48,6 +48,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; @Log4j2 public abstract class BaseService & JpaSpecificationExecutor, S extends BaseEntity> { @@ -91,6 +92,11 @@ public Optional findOne(Long id) { return repository.findById(id); } + @PostAuthorize("hasRole('ROLE_ADMIN') or hasPermission(returnObject.orElse(null), 'READ')") + public Optional findOne(UUID uuid) { + return repository.findByUuid(uuid); + } + @PostFilter("hasRole('ROLE_ADMIN') or hasPermission(filterObject, 'READ')") @Transactional(readOnly = true) public List findAllById(List id) { diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java index 0fb6c190b..aadf41d10 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java @@ -23,15 +23,15 @@ import lombok.extern.log4j.Log4j2; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.util.PatternMatchUtils; import org.springframework.web.multipart.MultipartFile; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.util.List; import java.util.UUID; @@ -95,7 +95,7 @@ public File create(MultipartFile uploadFile, Boolean writeToSystem) throws Excep File savedFile = this.create(file); - UUID fileUuid = savedFile.getFileUuid(); + UUID fileUuid = savedFile.getUuid(); // Setup path and directory String path = fileUuid + "/" + fileName; diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/IBaseFileService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/IBaseFileService.java index b693d8f24..fd95d0458 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/IBaseFileService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/IBaseFileService.java @@ -17,7 +17,7 @@ package de.terrestris.shogun.lib.service; import de.terrestris.shogun.lib.model.File; -import de.terrestris.shogun.lib.repository.BaseFileRepository; +import de.terrestris.shogun.lib.repository.BaseCrudRepository; import org.apache.tika.exception.TikaException; import org.apache.tomcat.util.http.fileupload.InvalidFileNameException; import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException; @@ -30,7 +30,7 @@ import java.util.Optional; import java.util.UUID; -public interface IBaseFileService & JpaSpecificationExecutor, S extends File> { +public interface IBaseFileService & JpaSpecificationExecutor, S extends File> { Optional findOne(UUID fileUuid); diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java index 3b883671d..0dcf4a41e 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java @@ -101,7 +101,7 @@ public ImageFile create(MultipartFile uploadFile, Boolean writeToSystem) throws } ImageFile savedFile = this.create(file); - UUID fileUuid = savedFile.getFileUuid(); + UUID fileUuid = savedFile.getUuid(); // Setup path and directory String path = fileUuid + "/" + fileName; From 618527ef781abb5a8b93f5c4a1995e61f8cb18ed Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Mon, 16 May 2022 08:37:20 +0200 Subject: [PATCH 2/5] Make use of JSON patch to apply partial updates --- .../shogun/lib/controller/BaseController.java | 23 +++++-------------- .../shogun/lib/service/BaseService.java | 15 +++++------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java index a2b048a81..5c390fd71 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseController.java @@ -16,6 +16,7 @@ */ package de.terrestris.shogun.lib.controller; +import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; import de.terrestris.shogun.lib.model.BaseEntity; import de.terrestris.shogun.lib.service.BaseService; import lombok.extern.log4j.Log4j2; @@ -33,7 +34,6 @@ import java.time.OffsetDateTime; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -597,26 +597,15 @@ public S update(@RequestBody S entity, @PathVariable("id") Long entityId) { } } - @PatchMapping("/{id}") + @PatchMapping(value = "/{id}", consumes = "application/json-patch+json") @ResponseStatus(HttpStatus.OK) - public S updatePartial(@RequestBody Map values, @PathVariable("id") Long entityId) { - log.trace("Requested to partially update entity of type {} with ID {} ({})", getGenericClassName(), entityId, values); + public S updatePartial(@RequestBody JsonMergePatch patch, @PathVariable("id") Long entityId) { + log.trace("Requested to partially update entity of type {} with ID {} ({})", getGenericClassName(), entityId, patch); try { - Object idFromValues = values.get("id"); - if (idFromValues == null) { - log.error("Field 'id' (entity {})is missing in the passed values: {}.", entityId, values); - throw new ResponseStatusException(HttpStatus.BAD_REQUEST); - } - Long id = Long.valueOf((Integer) idFromValues); - if (!entityId.equals(id)) { - log.error("IDs of update candidate (ID: {}) and update data ({}) don't match.", entityId, values); - throw new ResponseStatusException(HttpStatus.BAD_REQUEST); - } - S persistedEntity = service.findOne(entityId).orElseThrow(); if (persistedEntity != null) { - S updatedEntity = service.updatePartial(entityId, persistedEntity, values); + S updatedEntity = service.updatePartial(persistedEntity, patch); log.trace("Successfully updated values for entity of type {} with ID {}", getGenericClassName(), entityId); @@ -650,7 +639,7 @@ public S updatePartial(@RequestBody Map values, @PathVariable("i ); } catch (NumberFormatException nfe) { log.error("Can't parse 'id' field ({}) from values ({}). It has to be an Integer.: {}", - values, entityId, nfe.getMessage()); + patch, entityId, nfe.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST); } catch (ResponseStatusException rse) { throw rse; diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java index 85d5f045a..e9fec33f8 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java @@ -18,7 +18,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.fge.jsonpatch.JsonPatchException; +import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; import de.terrestris.shogun.lib.enumeration.PermissionCollectionType; import de.terrestris.shogun.lib.model.BaseEntity; import de.terrestris.shogun.lib.model.User; @@ -27,7 +28,6 @@ import de.terrestris.shogun.lib.service.security.permission.UserInstancePermissionService; import de.terrestris.shogun.lib.service.security.provider.UserProviderService; import lombok.extern.log4j.Log4j2; -import org.apache.commons.lang3.ObjectUtils; import org.hibernate.envers.AuditReader; import org.hibernate.envers.query.AuditEntity; import org.springframework.beans.factory.annotation.Autowired; @@ -46,7 +46,6 @@ import java.io.IOException; import java.time.OffsetDateTime; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -155,12 +154,10 @@ public S update(Long id, S entity) throws IOException { @PreAuthorize("hasRole('ROLE_ADMIN') or hasPermission(#entity, 'UPDATE')") @Transactional(isolation = Isolation.SERIALIZABLE) - public S updatePartial(Long entityId, S entity, Map values) throws IOException { - if (ObjectUtils.notEqual(entityId, entity.getId())) { - throw new IOException("ID's of passed entity and parameter do not match. No partial update possible"); - } - JsonNode jsonObject = objectMapper.valueToTree(values); - S updatedEntity = objectMapper.readerForUpdating(entity).readValue(jsonObject); + public S updatePartial(S entity, JsonMergePatch patch) throws IOException, JsonPatchException { + JsonNode patched = patch.apply(objectMapper.convertValue(entity, JsonNode.class)); + S updatedEntity = (S) objectMapper.treeToValue(patched, getBaseEntityClass()); + return repository.save(updatedEntity); } From 01f02d6758e65a836bdb63c7b39cb5155674c8d4 Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Mon, 16 May 2022 08:37:38 +0200 Subject: [PATCH 3/5] Don't serialize entity for update --- .../de/terrestris/shogun/lib/service/BaseService.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java index e9fec33f8..2acd60e05 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseService.java @@ -139,17 +139,11 @@ public S create(S entity) { public S update(Long id, S entity) throws IOException { Optional persistedEntityOpt = repository.findById(id); - ObjectNode jsonObject = objectMapper.valueToTree(entity); - // Ensure the created timestamp will not be overridden. S persistedEntity = persistedEntityOpt.orElseThrow(); - OffsetDateTime createdTimestamp = persistedEntity.getCreated(); - String serialized = createdTimestamp != null ? createdTimestamp.toInstant().toString() : null; - jsonObject.put("created", serialized); - - S updatedEntity = objectMapper.readerForUpdating(persistedEntity).readValue(jsonObject); + entity.setCreated(persistedEntity.getCreated()); - return repository.save(updatedEntity); + return repository.save(entity); } @PreAuthorize("hasRole('ROLE_ADMIN') or hasPermission(#entity, 'UPDATE')") From e722240553f87e28e6fadf95f88d9d22bd40bc0a Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Mon, 16 May 2022 08:38:28 +0200 Subject: [PATCH 4/5] Allow DynamicUpdate for hibernate updates --- .../shogun/lib/model/Application.java | 2 ++ .../de/terrestris/shogun/lib/model/File.java | 4 ++-- .../de/terrestris/shogun/lib/model/Group.java | 2 ++ .../shogun/lib/model/ImageFile.java | 12 +++++------ .../de/terrestris/shogun/lib/model/Layer.java | 2 ++ .../de/terrestris/shogun/lib/model/User.java | 2 ++ .../permission/GroupClassPermission.java | 17 +++++++-------- .../permission/GroupInstancePermission.java | 17 +++++++-------- .../permission/PermissionCollection.java | 21 +++++++------------ .../permission/UserClassPermission.java | 17 +++++++-------- .../permission/UserInstancePermission.java | 17 +++++++-------- 11 files changed, 55 insertions(+), 58 deletions(-) diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Application.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Application.java index 8bd6398bf..53d2c792e 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Application.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Application.java @@ -24,6 +24,7 @@ import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.Type; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; @@ -33,6 +34,7 @@ @Entity(name = "applications") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "applications_rev", schema = "shogun_rev") @Cacheable diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java index 8bcc99519..3d29b3c93 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java @@ -21,13 +21,13 @@ import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.Type; +import org.hibernate.annotations.DynamicUpdate; import javax.persistence.*; -import java.util.UUID; @Entity(name = "files") @Table(schema = "shogun") +@DynamicUpdate @Cacheable @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "files") @AllArgsConstructor diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Group.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Group.java index 880960308..248814506 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Group.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Group.java @@ -20,6 +20,7 @@ import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; @@ -27,6 +28,7 @@ @Entity(name = "groups") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "groups_rev", schema = "shogun_rev") @Cacheable diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/ImageFile.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/ImageFile.java index afcc19453..0d86d9fb0 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/ImageFile.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/ImageFile.java @@ -17,20 +17,18 @@ package de.terrestris.shogun.lib.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import org.hibernate.annotations.DynamicUpdate; + import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; - @Entity(name = "imagefiles") @Table(schema = "shogun") +@DynamicUpdate @Cacheable @Data @AllArgsConstructor diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Layer.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Layer.java index 856105541..2baabe416 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Layer.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/Layer.java @@ -24,6 +24,7 @@ import org.geojson.GeoJsonObject; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.Type; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; @@ -32,6 +33,7 @@ @Entity(name = "layers") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "layers_rev", schema = "shogun_rev") @Cacheable diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/User.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/User.java index 594382a5d..887dd737a 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/User.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/User.java @@ -22,6 +22,7 @@ import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.Type; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; @@ -30,6 +31,7 @@ @Entity(name = "users") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "users_rev", schema = "shogun_rev") @Cacheable diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupClassPermission.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupClassPermission.java index 9fe0656b7..cfae342b1 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupClassPermission.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupClassPermission.java @@ -17,22 +17,21 @@ package de.terrestris.shogun.lib.model.security.permission; import de.terrestris.shogun.lib.model.Group; -import javax.persistence.Cacheable; -import javax.persistence.Entity; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; +import javax.persistence.Cacheable; +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + @Entity(name = "groupclasspermissions") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "groupclasspermissions_rev", schema = "shogun_rev") @Cacheable diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupInstancePermission.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupInstancePermission.java index cae6b8260..1d5cf8fa1 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupInstancePermission.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/GroupInstancePermission.java @@ -17,22 +17,21 @@ package de.terrestris.shogun.lib.model.security.permission; import de.terrestris.shogun.lib.model.Group; -import javax.persistence.Cacheable; -import javax.persistence.Entity; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; +import javax.persistence.Cacheable; +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + @Entity(name = "groupinstancepermissions") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "groupinstancepermissions_rev", schema = "shogun_rev") @Cacheable diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/PermissionCollection.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/PermissionCollection.java index 59a3ccdb9..864fbd479 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/PermissionCollection.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/PermissionCollection.java @@ -19,27 +19,22 @@ import de.terrestris.shogun.lib.enumeration.PermissionCollectionType; import de.terrestris.shogun.lib.enumeration.PermissionType; import de.terrestris.shogun.lib.model.BaseEntity; -import java.util.HashSet; -import java.util.Set; -import javax.persistence.Cacheable; -import javax.persistence.CollectionTable; -import javax.persistence.Column; -import javax.persistence.ElementCollection; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.hibernate.annotations.*; import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; + +import javax.persistence.*; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.HashSet; +import java.util.Set; @Entity(name = "permissions") @Table(schema = "shogun") +@DynamicUpdate @Cacheable @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "permissions") @Data diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserClassPermission.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserClassPermission.java index ee07a18bb..694cd68cb 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserClassPermission.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserClassPermission.java @@ -17,22 +17,21 @@ package de.terrestris.shogun.lib.model.security.permission; import de.terrestris.shogun.lib.model.User; -import javax.persistence.Cacheable; -import javax.persistence.Entity; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; +import javax.persistence.Cacheable; +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + @Entity(name = "userclasspermissions") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "userclasspermissions_rev", schema = "shogun_rev") @Cacheable diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserInstancePermission.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserInstancePermission.java index 67eaa5684..ddd26cb4d 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserInstancePermission.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/security/permission/UserInstancePermission.java @@ -17,22 +17,21 @@ package de.terrestris.shogun.lib.model.security.permission; import de.terrestris.shogun.lib.model.User; -import javax.persistence.Cacheable; -import javax.persistence.Entity; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; +import javax.persistence.Cacheable; +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + @Entity(name = "userinstancepermissions") @Table(schema = "shogun") +@DynamicUpdate @Audited @AuditTable(value = "userinstancepermissions_rev", schema = "shogun_rev") @Cacheable From 79ca50439b215dec7e46b6f2eccfe1b23a818d35 Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Mon, 16 May 2022 08:51:09 +0200 Subject: [PATCH 5/5] Remove unnecessary stubbings --- .../de/terrestris/shogun/lib/service/BaseServiceTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shogun-lib/src/test/java/de/terrestris/shogun/lib/service/BaseServiceTest.java b/shogun-lib/src/test/java/de/terrestris/shogun/lib/service/BaseServiceTest.java index 6a6aa91e7..d8df675e2 100644 --- a/shogun-lib/src/test/java/de/terrestris/shogun/lib/service/BaseServiceTest.java +++ b/shogun-lib/src/test/java/de/terrestris/shogun/lib/service/BaseServiceTest.java @@ -178,10 +178,6 @@ public void update_ShouldCallCorrectRepositoryMethodAndShouldReturnTheCreatedEnt when(baseCrudRepositoryMock.findById(1909L)).thenReturn(Optional.of(mockEntity)); when(baseCrudRepositoryMock.save(mockEntity)).thenReturn(mockEntity); - when(objectMapperMock.valueToTree(mockEntity)).thenReturn(returnNode); - when(objectMapperMock.readerForUpdating(mockEntity)).thenReturn(objectReaderMock); - when(objectReaderMock.readValue(returnNode)).thenReturn(mockEntity); - S returnValue = (S) service.update(1909L, mockEntity); verify(baseCrudRepositoryMock, times(1)).findById(1909L);