diff --git a/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/TableEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/TableEndpoint.java index 8ea36ff682ace6f66d1744662668b485f61cbc9a..78dc75f59d57a3eb8ee341a9ef914c88d0c57657 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/TableEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/TableEndpoint.java @@ -664,6 +664,7 @@ public class TableEndpoint extends RestEndpoint { throw new DatabaseUnavailableException("Failed to establish connection to database", e); } metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization); + storageService.saveImportEvent(databaseId, getId(principal), data.getLocation()); return ResponseEntity.accepted() .build(); } diff --git a/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/UploadEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/UploadEndpoint.java index b7a88fc3f7c63116689dbbe26d4466ed43e0160b..97dea3f16e1784ef1ada9041c9827816f740f9c2 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/UploadEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/UploadEndpoint.java @@ -1,9 +1,13 @@ package at.ac.tuwien.ifs.dbrepo.endpoints; +import at.ac.tuwien.ifs.dbrepo.core.api.database.ActionTypeDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateStorageEventDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.ViewDto; import at.ac.tuwien.ifs.dbrepo.core.api.error.ApiErrorDto; import at.ac.tuwien.ifs.dbrepo.core.api.file.UploadResponseDto; import at.ac.tuwien.ifs.dbrepo.core.exception.*; +import at.ac.tuwien.ifs.dbrepo.gateway.MetadataServiceGateway; +import at.ac.tuwien.ifs.dbrepo.mapper.DataMapper; import at.ac.tuwien.ifs.dbrepo.service.StorageService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -21,6 +25,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.security.Principal; @Log4j2 @RestController @@ -29,10 +34,12 @@ import java.io.IOException; public class UploadEndpoint extends RestEndpoint { private final StorageService storageService; + private final MetadataServiceGateway metadataServiceGateway; @Autowired - public UploadEndpoint(StorageService storageService) { + public UploadEndpoint(StorageService storageService, MetadataServiceGateway metadataServiceGateway) { this.storageService = storageService; + this.metadataServiceGateway = metadataServiceGateway; } @PostMapping @@ -52,11 +59,13 @@ public class UploadEndpoint extends RestEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<UploadResponseDto> create(@NotNull @RequestParam("file") MultipartFile file) throws DatabaseUnavailableException, + public ResponseEntity<UploadResponseDto> create(@NotNull @RequestParam("file") MultipartFile file, + Principal principal) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, ViewMalformedException, MetadataServiceException { log.debug("endpoint upload file, file.originalFilename={}", file.getOriginalFilename()); try { final String key = storageService.putObject(file.getBytes()); + storageService.saveUploadEvent(file, getId(principal), key); return ResponseEntity.status(HttpStatus.CREATED) .body(UploadResponseDto.builder() .s3Key(key) diff --git a/dbrepo-data-service/rest-service/src/test/java/at/ac/tuwien/ifs/dbrepo/service/StorageServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/ac/tuwien/ifs/dbrepo/service/StorageServiceIntegrationTest.java index 4f1c6efe40329961c213a463c1aa2fccb2168a33..8def1c1540da698c35b5480df04e59185b7b0719 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/ac/tuwien/ifs/dbrepo/service/StorageServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/ac/tuwien/ifs/dbrepo/service/StorageServiceIntegrationTest.java @@ -166,21 +166,6 @@ public class StorageServiceIntegrationTest extends BaseTest { }); } - @Test - public void getResource_succeeds() throws StorageUnavailableException, StorageNotFoundException { - - /* mock */ - s3Client.putObject(PutObjectRequest.builder() - .key("s3key") - .bucket(s3Config.getS3Bucket()) - .build(), RequestBody.fromFile(new File("src/test/resources/csv/weather_aus.csv"))); - - /* test */ - final ExportResourceDto response = storageService.getResource(s3Config.getS3Bucket(), "s3key"); - assertEquals("s3key", response.getFilename()); - assertNotNull(response.getResource()); - } - @Test public void getResource_notFound_fails() { diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/MetadataServiceGateway.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/MetadataServiceGateway.java index c2f901c3cb25d5a923748e9109a2fc6075a45075..57e9498facfada7a2f11757c991931648535c94d 100644 --- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/MetadataServiceGateway.java +++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/MetadataServiceGateway.java @@ -1,6 +1,7 @@ package at.ac.tuwien.ifs.dbrepo.gateway; import at.ac.tuwien.ifs.dbrepo.core.api.container.ContainerDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateStorageEventDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseAccessDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.ViewDto; @@ -15,6 +16,9 @@ import java.util.UUID; // todo ? public interface MetadataServiceGateway { + void saveStorageEvent(CreateStorageEventDto event) throws RemoteUnavailableException, + DatabaseNotFoundException, MetadataServiceException; + /** * Get a container with given id from the metadata service. * diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/impl/MetadataServiceGatewayImpl.java index 7175151c109419f76d89f4f25c9a1a0912075d9e..8cdb57d07d70cca7d28a9adfdeeccdec92470134 100644 --- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/gateway/impl/MetadataServiceGatewayImpl.java @@ -1,6 +1,7 @@ package at.ac.tuwien.ifs.dbrepo.gateway.impl; import at.ac.tuwien.ifs.dbrepo.core.api.container.ContainerDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateStorageEventDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseAccessDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.ViewDto; @@ -32,15 +33,37 @@ import java.util.UUID; public class MetadataServiceGatewayImpl implements MetadataServiceGateway { private final DataMapper dataMapper; - private final GatewayConfig gatewayConfig; private final RestTemplate internalRestTemplate; + private final GatewayConfig gatewayConfig; @Autowired - public MetadataServiceGatewayImpl(DataMapper dataMapper, GatewayConfig gatewayConfig, - @Qualifier("internalRestTemplate") RestTemplate internalRestTemplate) { + public MetadataServiceGatewayImpl(DataMapper dataMapper, + @Qualifier("internalRestTemplate") RestTemplate internalRestTemplate, + GatewayConfig gatewayConfig) { this.dataMapper = dataMapper; - this.gatewayConfig = gatewayConfig; this.internalRestTemplate = internalRestTemplate; + this.gatewayConfig = gatewayConfig; + } + + @Override + public void saveStorageEvent(CreateStorageEventDto event) throws RemoteUnavailableException, + DatabaseNotFoundException, MetadataServiceException { + final ResponseEntity<Void> response; + final String url = "/api/database/" + event.getDatabaseId() + "/storage"; + log.debug("save storage event in metadata service: {}", url); + try { + response = internalRestTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(event), Void.class); + } catch (ResourceAccessException | HttpServerErrorException e) { + log.error("Failed to save storage event: {}", e.getMessage()); + throw new RemoteUnavailableException("Failed to save storage event: " + e.getMessage(), e); + } catch (HttpClientErrorException.NotFound e) { + log.error("Failed to find database/user: {}", e.getMessage()); + throw new DatabaseNotFoundException("Failed to find database/user: " + e.getMessage(), e); + } + if (response.getStatusCode() != HttpStatus.ACCEPTED) { + log.error("Failed to save storage event: service responded unsuccessful: {}", response.getStatusCode()); + throw new MetadataServiceException("Failed to save storage event: service responded unsuccessful: " + response.getStatusCode()); + } } @Override diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/mapper/DataMapper.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/mapper/DataMapper.java index ddf7f9a882ab9b460f101b09a4a83b7c3b41e410..fdde65b4369df5ff20c666858bd9ba45bdae6a7b 100644 --- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/mapper/DataMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/mapper/DataMapper.java @@ -1,10 +1,7 @@ package at.ac.tuwien.ifs.dbrepo.mapper; import at.ac.tuwien.ifs.dbrepo.core.api.container.ContainerDto; -import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseBriefDto; -import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseDto; -import at.ac.tuwien.ifs.dbrepo.core.api.database.ViewColumnDto; -import at.ac.tuwien.ifs.dbrepo.core.api.database.ViewDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.*; import at.ac.tuwien.ifs.dbrepo.core.api.database.internal.CreateDatabaseDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.query.QueryDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.table.*; diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/StorageService.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/StorageService.java index 19540194abbb08be71fe741e5d0b9586ec992e47..3aa5cdff0ca4a82817383066632f283366e72e53 100644 --- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/StorageService.java +++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/StorageService.java @@ -1,18 +1,23 @@ package at.ac.tuwien.ifs.dbrepo.service; import at.ac.tuwien.ifs.dbrepo.core.api.ExportResourceDto; -import at.ac.tuwien.ifs.dbrepo.core.exception.MalformedException; -import at.ac.tuwien.ifs.dbrepo.core.exception.StorageNotFoundException; -import at.ac.tuwien.ifs.dbrepo.core.exception.StorageUnavailableException; -import at.ac.tuwien.ifs.dbrepo.core.exception.TableMalformedException; +import at.ac.tuwien.ifs.dbrepo.core.exception.*; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; +import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.util.List; +import java.util.UUID; public interface StorageService { + void saveUploadEvent(MultipartFile file, UUID userId, String s3key) throws DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException; + + void saveImportEvent(UUID databaseId, UUID userId, String s3key) throws DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException; + String putObject(byte[] content); /** @@ -49,28 +54,6 @@ public interface StorageService { void deleteObject(String bucket, String key); - /** - * Loads an object of the default export bucket from the Storage Service into an export resource. - * - * @param key The object key. - * @return The export resource, if successful. - * @throws StorageUnavailableException The object failed to be loaded from the Storage Service. - * @throws StorageNotFoundException The key was not found in the Storage Service. - */ - ExportResourceDto getResource(String key) throws StorageUnavailableException, StorageNotFoundException; - - /** - * Loads an object of a bucket from the Storage Service into an export resource. - * - * @param bucket The bucket name. - * @param key The object key. - * @return The export resource, if successful. - * @throws StorageUnavailableException The object failed to be loaded from the Storage Service. - * @throws StorageNotFoundException The key was not found in the Storage Service. - */ - ExportResourceDto getResource(String bucket, String key) throws StorageUnavailableException, - StorageNotFoundException; - /** * Transforms the given dataset into a downloadable dataset. * diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/StorageServiceS3Impl.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/StorageServiceS3Impl.java index d29f5e2a2dc8c864402c4d0d66c4ab7de336461b..23cfa5b982409e927b811a1f2c88343aef363c10 100644 --- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/StorageServiceS3Impl.java +++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/StorageServiceS3Impl.java @@ -1,12 +1,12 @@ package at.ac.tuwien.ifs.dbrepo.service.impl; import at.ac.tuwien.ifs.dbrepo.config.S3Config; -import at.ac.tuwien.ifs.dbrepo.service.StorageService; import at.ac.tuwien.ifs.dbrepo.core.api.ExportResourceDto; -import at.ac.tuwien.ifs.dbrepo.core.exception.MalformedException; -import at.ac.tuwien.ifs.dbrepo.core.exception.StorageNotFoundException; -import at.ac.tuwien.ifs.dbrepo.core.exception.StorageUnavailableException; -import at.ac.tuwien.ifs.dbrepo.core.exception.TableMalformedException; +import at.ac.tuwien.ifs.dbrepo.core.api.database.ActionTypeDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateStorageEventDto; +import at.ac.tuwien.ifs.dbrepo.core.exception.*; +import at.ac.tuwien.ifs.dbrepo.gateway.MetadataServiceGateway; +import at.ac.tuwien.ifs.dbrepo.service.StorageService; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.RandomStringUtils; import org.apache.spark.sql.*; @@ -15,16 +15,14 @@ import org.apache.spark.sql.types.StructField; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.*; import java.io.*; import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static scala.collection.JavaConverters.asScalaIteratorConverter; @@ -35,12 +33,39 @@ public class StorageServiceS3Impl implements StorageService { private final S3Config s3Config; private final S3Client s3Client; private final SparkSession sparkSession; + private final MetadataServiceGateway metadataServiceGateway; @Autowired - public StorageServiceS3Impl(S3Config s3Config, S3Client s3Client, SparkSession sparkSession) { + public StorageServiceS3Impl(S3Config s3Config, S3Client s3Client, SparkSession sparkSession, + MetadataServiceGateway metadataServiceGateway) { this.s3Config = s3Config; this.s3Client = s3Client; this.sparkSession = sparkSession; + this.metadataServiceGateway = metadataServiceGateway; + } + + @Override + public void saveUploadEvent(MultipartFile file, UUID userId, String s3key) throws DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException { + final CreateStorageEventDto event = CreateStorageEventDto.builder() + .size(file.getSize()) + .action(ActionTypeDto.UPLOAD) + .userId(userId) + .s3key(s3key) + .build(); + metadataServiceGateway.saveStorageEvent(event); + } + + @Override + public void saveImportEvent(UUID databaseId, UUID userId, String s3key) throws DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException { + final CreateStorageEventDto event = CreateStorageEventDto.builder() + .databaseId(databaseId) + .action(ActionTypeDto.IMPORT) + .userId(userId) + .s3key(s3key) + .build(); + metadataServiceGateway.saveStorageEvent(event); } @Override @@ -98,22 +123,7 @@ public class StorageServiceS3Impl implements StorageService { } @Override - public ExportResourceDto getResource(String key) throws StorageNotFoundException, StorageUnavailableException { - return getResource(s3Config.getS3Bucket(), key); - } - - @Override - public ExportResourceDto getResource(String bucket, String key) throws StorageNotFoundException, - StorageUnavailableException { - final InputStreamResource resource = new InputStreamResource(getObject(bucket, key)); - log.trace("return export resource with filename: {}", key); - return ExportResourceDto.builder() - .resource(resource) - .filename(key) - .build(); - } - - @Override + // TODO should be export to S3 -> load from S3 public ExportResourceDto transformDataset(Dataset<Row> dataset) throws StorageUnavailableException { final List<Map<String, String>> inMemory = dataset.collectAsList() .stream() diff --git a/dbrepo-metadata-db/1_setup-schema.sql b/dbrepo-metadata-db/1_setup-schema.sql index 4ba9d70b174dca5b3481d8042f47c332afc4cf2b..8d07179e1bda9e161ea2cde6c835be74e281e73f 100644 --- a/dbrepo-metadata-db/1_setup-schema.sql +++ b/dbrepo-metadata-db/1_setup-schema.sql @@ -425,6 +425,7 @@ CREATE TABLE IF NOT EXISTS `mdb_identifier_creators` ( id VARCHAR(36) NOT NULL DEFAULT UUID(), pid VARCHAR(36) NOT NULL, + ordinal_position INT NOT NULL, given_names TEXT, family_name TEXT, creator_name VARCHAR(255) NOT NULL, @@ -502,6 +503,20 @@ CREATE TABLE IF NOT EXISTS `mdb_image_operators` UNIQUE (image_id, value) ) WITH SYSTEM VERSIONING; +CREATE TABLE IF NOT EXISTS `mdb_databases_storages` +( + id VARCHAR(36) NOT NULL DEFAULT UUID(), + database_id VARCHAR(36), + user_id VARCHAR(36) NOT NULL, + s3key VARCHAR(100) NOT NULL, + action ENUM ('EXPORT', 'IMPORT') NOT NULL, + size BIGINT COMMENT 'size in bytes', + created TIMESTAMP NOT NULL DEFAULT NOW(), + PRIMARY KEY (`id`), + FOREIGN KEY (`database_id`) REFERENCES `mdb_databases` (`id`), + FOREIGN KEY (`user_id`) REFERENCES `mdb_users` (`id`) +) WITH SYSTEM VERSIONING; + COMMIT; BEGIN; diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/ac/tuwien/ifs/dbrepo/repository/StorageRepository.java b/dbrepo-metadata-service/repositories/src/main/java/at/ac/tuwien/ifs/dbrepo/repository/StorageRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..8a517d1b507a47c3822ab7cb5b75e2325853430f --- /dev/null +++ b/dbrepo-metadata-service/repositories/src/main/java/at/ac/tuwien/ifs/dbrepo/repository/StorageRepository.java @@ -0,0 +1,12 @@ +package at.ac.tuwien.ifs.dbrepo.repository; + +import at.ac.tuwien.ifs.dbrepo.core.entity.database.StorageEvent; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface StorageRepository extends JpaRepository<StorageEvent, UUID> { + +} diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/StorageEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/StorageEndpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..26df9678eb65df45521765120496393656bb8a9b --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/StorageEndpoint.java @@ -0,0 +1,75 @@ +package at.ac.tuwien.ifs.dbrepo.endpoints; + +import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateStorageEventDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseAccessDto; +import at.ac.tuwien.ifs.dbrepo.core.api.error.ApiErrorDto; +import at.ac.tuwien.ifs.dbrepo.core.entity.database.Database; +import at.ac.tuwien.ifs.dbrepo.core.entity.user.User; +import at.ac.tuwien.ifs.dbrepo.core.exception.DatabaseNotFoundException; +import at.ac.tuwien.ifs.dbrepo.core.exception.UserNotFoundException; +import at.ac.tuwien.ifs.dbrepo.service.DatabaseService; +import at.ac.tuwien.ifs.dbrepo.service.UserService; +import io.micrometer.observation.annotation.Observed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +@Log4j2 +@RestController +@CrossOrigin(origins = "*") +@RequestMapping(path = "/api/database/{databaseId}/storage") +public class StorageEndpoint extends AbstractEndpoint { + + private final UserService userService; + private final DatabaseService databaseService; + + @Autowired + public StorageEndpoint(UserService userService, DatabaseService databaseService) { + this.userService = userService; + this.databaseService = databaseService; + } + + @PostMapping + @Transactional + @Observed(name = "dbrepo_storage_event") + @PreAuthorize("hasAuthority('system')") + @Operation(summary = "Log storage event", + description = "Logs a storage event in the Metadata Database. Requires role `system`.", + security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", + description = "Saved successfully", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = DatabaseAccessDto.class))}), + @ApiResponse(responseCode = "404", + description = "Database or user not found", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), + }) + public ResponseEntity<DatabaseAccessDto> save(@NotNull @PathVariable("databaseId") UUID databaseId, + @Valid @RequestBody CreateStorageEventDto data) + throws DatabaseNotFoundException, UserNotFoundException { + log.debug("endpoint save storage event, databaseId={}, data.action={}", databaseId, data.getAction()); + final Database database = databaseService.findById(databaseId); + final User user = userService.findById(data.getUserId()); + databaseService.saveStorageEvent(database, user, data); + return ResponseEntity.accepted() + .build(); + } + +} diff --git a/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/DatabaseService.java b/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/DatabaseService.java index 351e383b05ed01cbffbf4e3685f79b2b279b2eb2..107c517542e26b5e415a9052370e401888038e3e 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/DatabaseService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/DatabaseService.java @@ -1,9 +1,11 @@ package at.ac.tuwien.ifs.dbrepo.service; import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateDatabaseDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateStorageEventDto; import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseModifyVisibilityDto; import at.ac.tuwien.ifs.dbrepo.core.entity.container.Container; import at.ac.tuwien.ifs.dbrepo.core.entity.database.Database; +import at.ac.tuwien.ifs.dbrepo.core.entity.database.StorageEvent; import at.ac.tuwien.ifs.dbrepo.core.entity.user.User; import at.ac.tuwien.ifs.dbrepo.core.exception.*; import org.springframework.stereotype.Service; @@ -63,6 +65,8 @@ public interface DatabaseService { */ List<Database> findAllPublicOrSchemaPublicByInternalName(String internalName); + StorageEvent saveStorageEvent(Database database, User user, CreateStorageEventDto data); + /** * Find a database by given id. * diff --git a/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/DatabaseServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/DatabaseServiceImpl.java index 6cfa0180a8891c97e49fdcebd1c2923da8088adf..932f581d61f16b0098f34cc421aeef9aaf1c37e1 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/DatabaseServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/DatabaseServiceImpl.java @@ -1,13 +1,11 @@ package at.ac.tuwien.ifs.dbrepo.service.impl; -import at.ac.tuwien.ifs.dbrepo.core.api.database.CreateDatabaseDto; -import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseDto; -import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseModifyVisibilityDto; -import at.ac.tuwien.ifs.dbrepo.core.api.database.ViewDto; +import at.ac.tuwien.ifs.dbrepo.core.api.database.*; import at.ac.tuwien.ifs.dbrepo.core.api.database.table.TableDto; import at.ac.tuwien.ifs.dbrepo.core.api.user.internal.UpdateUserPasswordDto; import at.ac.tuwien.ifs.dbrepo.core.entity.container.Container; import at.ac.tuwien.ifs.dbrepo.core.entity.database.Database; +import at.ac.tuwien.ifs.dbrepo.core.entity.database.StorageEvent; import at.ac.tuwien.ifs.dbrepo.core.entity.database.View; import at.ac.tuwien.ifs.dbrepo.core.entity.database.ViewColumn; import at.ac.tuwien.ifs.dbrepo.core.entity.database.table.Table; @@ -22,6 +20,7 @@ import at.ac.tuwien.ifs.dbrepo.core.mapper.MetadataMapper; import at.ac.tuwien.ifs.dbrepo.gateway.DataServiceGateway; import at.ac.tuwien.ifs.dbrepo.gateway.SearchServiceGateway; import at.ac.tuwien.ifs.dbrepo.repository.DatabaseRepository; +import at.ac.tuwien.ifs.dbrepo.repository.StorageRepository; import at.ac.tuwien.ifs.dbrepo.service.DatabaseService; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.RandomStringUtils; @@ -39,14 +38,17 @@ import java.util.UUID; public class DatabaseServiceImpl implements DatabaseService { private final MetadataMapper metadataMapper; + private final StorageRepository storageRepository; private final DatabaseRepository databaseRepository; private final DataServiceGateway dataServiceGateway; private final SearchServiceGateway searchServiceGateway; @Autowired - public DatabaseServiceImpl(MetadataMapper metadataMapper, DatabaseRepository databaseRepository, - DataServiceGateway dataServiceGateway, SearchServiceGateway searchServiceGateway) { + public DatabaseServiceImpl(MetadataMapper metadataMapper, StorageRepository storageRepository, + DatabaseRepository databaseRepository, DataServiceGateway dataServiceGateway, + SearchServiceGateway searchServiceGateway) { this.metadataMapper = metadataMapper; + this.storageRepository = storageRepository; this.databaseRepository = databaseRepository; this.dataServiceGateway = dataServiceGateway; this.searchServiceGateway = searchServiceGateway; @@ -82,6 +84,16 @@ public class DatabaseServiceImpl implements DatabaseService { return databaseRepository.findAllPublicOrSchemaPublicByInternalNameDesc(internalName); } + @Override + public StorageEvent saveStorageEvent(Database database, User user, CreateStorageEventDto data) { + final StorageEvent entity = metadataMapper.createStorageEventDtoToStorageEvent(data); + entity.setUser(user); + entity.setDatabase(database); + final StorageEvent event = storageRepository.save(entity); + log.info("Saved storage event {} for database with id: {}", event.getAction(), event.getDatabase().getId()); + return event; + } + @Override @Transactional(readOnly = true) public Database findById(UUID id) throws DatabaseNotFoundException { diff --git a/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/IdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/IdentifierServiceImpl.java index f8b2c273e8c9032a103b327f84892528aad7826f..4257c7b5018e15940deaa254ef54e8575c422af9 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/IdentifierServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/ac/tuwien/ifs/dbrepo/service/impl/IdentifierServiceImpl.java @@ -1,11 +1,11 @@ package at.ac.tuwien.ifs.dbrepo.service.impl; +import at.ac.tuwien.ifs.dbrepo.config.MetadataConfig; import at.ac.tuwien.ifs.dbrepo.core.api.database.query.QueryDto; import at.ac.tuwien.ifs.dbrepo.core.api.identifier.BibliographyTypeDto; import at.ac.tuwien.ifs.dbrepo.core.api.identifier.CreateIdentifierDto; import at.ac.tuwien.ifs.dbrepo.core.api.identifier.IdentifierSaveDto; import at.ac.tuwien.ifs.dbrepo.core.api.identifier.IdentifierTypeDto; -import at.ac.tuwien.ifs.dbrepo.config.MetadataConfig; import at.ac.tuwien.ifs.dbrepo.core.entity.database.Database; import at.ac.tuwien.ifs.dbrepo.core.entity.database.LanguageType; import at.ac.tuwien.ifs.dbrepo.core.entity.database.View; @@ -184,40 +184,60 @@ public class IdentifierServiceImpl implements IdentifierService { .stream() .map(metadataMapper::creatorCreateDtoToCreator) .toList())); + final int[] idx = new int[]{0}; identifier.getCreators() - .forEach(c -> c.setIdentifier(identifier)); + .forEach(c -> { + c.setOrdinalPosition(idx[0]++); + c.setIdentifier(identifier); + }); } if (data.getRelatedIdentifiers() != null) { identifier.setRelatedIdentifiers(new LinkedList<>(data.getRelatedIdentifiers() .stream() .map(metadataMapper::relatedIdentifierCreateDtoToRelatedIdentifier) .toList())); + final int[] idx = new int[]{0}; identifier.getRelatedIdentifiers() - .forEach(r -> r.setIdentifier(identifier)); + .forEach(r -> { + r.setOrdinalPosition(idx[0]++); + r.setIdentifier(identifier); + }); } if (data.getTitles() != null) { identifier.setTitles(new LinkedList<>(data.getTitles() .stream() .map(metadataMapper::identifierCreateTitleDtoToIdentifierTitle) .toList())); + final int[] idx = new int[]{0}; identifier.getTitles() - .forEach(t -> t.setIdentifier(identifier)); + .forEach(t -> { + t.setOrdinalPosition(idx[0]++); + t.setIdentifier(identifier); + }); } if (data.getDescriptions() != null) { identifier.setDescriptions(new LinkedList<>(data.getDescriptions() .stream() .map(metadataMapper::identifierCreateDescriptionDtoToIdentifierDescription) .toList())); + final int[] idx = new int[]{0}; identifier.getDescriptions() - .forEach(d -> d.setIdentifier(identifier)); + .forEach(d -> { + d.setOrdinalPosition(idx[0]++); + d.setIdentifier(identifier); + }); } if (data.getFunders() != null) { identifier.setFunders(new LinkedList<>(data.getFunders() .stream() .map(metadataMapper::identifierFunderSaveDtoToIdentifierFunder) .toList())); + final int[] idx = new int[]{0}; identifier.getFunders() - .forEach(f -> f.setIdentifier(identifier)); + .forEach(f -> { + f.setOrdinalPosition(idx[0]++); + f.setIdentifier(identifier); + }); } return save(identifier); } diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/ActionTypeDto.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/ActionTypeDto.java new file mode 100644 index 0000000000000000000000000000000000000000..b87d28c2fb3b691a0c3cf987aa6f438de813a1a5 --- /dev/null +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/ActionTypeDto.java @@ -0,0 +1,28 @@ +package at.ac.tuwien.ifs.dbrepo.core.api.database; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public enum ActionTypeDto { + + @JsonProperty("export") + EXPORT("export"), + + @JsonProperty("import") + IMPORT("import"), + + @JsonProperty("upload") + UPLOAD("upload"); + + private final String value; + + ActionTypeDto(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } +} diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/CreateStorageEventDto.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/CreateStorageEventDto.java new file mode 100644 index 0000000000000000000000000000000000000000..0987240fc615bf136b0987d8aa531fa66d27edb5 --- /dev/null +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/CreateStorageEventDto.java @@ -0,0 +1,36 @@ +package at.ac.tuwien.ifs.dbrepo.core.api.database; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.*; +import lombok.extern.jackson.Jacksonized; + +import java.util.UUID; + +@Getter +@Setter +@Builder +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +@Jacksonized +@ToString +public class CreateStorageEventDto { + + @NotNull + @JsonProperty("user_id") + private UUID userId; + + @JsonProperty("database_id") + private UUID databaseId; + + @NotBlank + private String s3key; + + private Long size; + + @NotNull + private ActionTypeDto action; + +} diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/StorageEventDto.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/StorageEventDto.java new file mode 100644 index 0000000000000000000000000000000000000000..9678426318bc5525e4824eabfd7c439fa0dfba15 --- /dev/null +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/api/database/StorageEventDto.java @@ -0,0 +1,39 @@ +package at.ac.tuwien.ifs.dbrepo.core.api.database; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.*; +import lombok.extern.jackson.Jacksonized; + +import java.util.UUID; + +@Getter +@Setter +@Builder +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +@Jacksonized +@ToString +public class StorageEventDto { + + @NotNull + private UUID id; + + @NotNull + @JsonProperty("user_id") + private UUID userId; + + @NotNull + private UUID databaseId; + + @NotBlank + private String s3key; + + private Long size; + + @NotNull + private ActionTypeDto action; + +} diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/ActionType.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/ActionType.java new file mode 100644 index 0000000000000000000000000000000000000000..ae50d2673af93483bc3cc4937d000dba7115620f --- /dev/null +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/ActionType.java @@ -0,0 +1,10 @@ +package at.ac.tuwien.ifs.dbrepo.core.entity.database; + +import lombok.Getter; + +@Getter +public enum ActionType { + EXPORT, + IMPORT, + UPLOAD; +} diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/Database.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/Database.java index 2aefc232a30908654572a89b8a894abbc00d8c96..bb9e35cf54834eaee378fecd41a29697148fda69 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/Database.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/Database.java @@ -91,19 +91,23 @@ public class Database implements Serializable { @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE}, mappedBy = "database") @Where(clause = "identifier_type='DATABASE'") - @OrderBy("id DESC") + @OrderBy("created DESC") private List<Identifier> identifiers; @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE}, mappedBy = "database") @Where(clause = "identifier_type='SUBSET'") - @OrderBy("id DESC") + @OrderBy("created DESC") private List<Identifier> subsets; - @OrderBy("id DESC") + @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE}, mappedBy = "database") + @OrderBy("created DESC") + private List<StorageEvent> storageEvents; + + @OrderBy("created DESC") @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "database", orphanRemoval = true) private List<at.ac.tuwien.ifs.dbrepo.core.entity.database.table.Table> tables; - @OrderBy("id DESC") + @OrderBy("created DESC") @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "database", orphanRemoval = true) private List<View> views; diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/StorageEvent.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/StorageEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..a7ec0f35a47256f5dd7498814725f130a57298ac --- /dev/null +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/StorageEvent.java @@ -0,0 +1,73 @@ +package at.ac.tuwien.ifs.dbrepo.core.entity.database; + +import at.ac.tuwien.ifs.dbrepo.core.entity.user.User; +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.JdbcTypeCode; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.Instant; +import java.util.UUID; + +@Data +@Entity +@Builder +@ToString +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode +@EntityListeners(AuditingEntityListener.class) +@Table(name = "mdb_databases_storages") +public class StorageEvent { + + @Id + @JdbcTypeCode(java.sql.Types.VARCHAR) + @Column(columnDefinition = "VARCHAR(36)") + private UUID id; + + @ToString.Exclude + @JdbcTypeCode(java.sql.Types.VARCHAR) + @Column(name = "user_id", nullable = false, columnDefinition = "VARCHAR(36)") + private UUID userId; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @JoinColumns({ + @JoinColumn(name = "user_id", referencedColumnName = "id", insertable = false, updatable = false) + }) + private User user; + + @ToString.Exclude + @JdbcTypeCode(java.sql.Types.VARCHAR) + @Column(name = "database_id", columnDefinition = "VARCHAR(36)") + private UUID databaseId; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @JoinColumns({ + @JoinColumn(name = "database_id", referencedColumnName = "id", insertable = false, updatable = false) + }) + private Database database; + + @Column(nullable = false, columnDefinition = "VARCHAR(100)") + private String s3key; + + private Long size; + + @Column(nullable = false, columnDefinition = "ENUM('EXPORT', 'IMPORT')") + private ActionType action; + + @EqualsAndHashCode.Exclude + @CreatedDate + @Column(nullable = false, updatable = false, columnDefinition = "TIMESTAMP default NOW()") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") + private Instant created; + + @PrePersist + public void prePersist() { + if (this.id == null) { + this.id = UUID.randomUUID(); + } + } + +} diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/View.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/View.java index d84b75530b1b5dd2b27a896461f6d86671588ea1..38680c01db39d2e66e5cc8866e4689a0f5b13e0b 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/View.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/View.java @@ -81,7 +81,7 @@ public class View { @JoinColumn(name = "dbid", referencedColumnName = "vdbid", insertable = false, updatable = false) }) @Where(clause = "identifier_type='VIEW'") - @OrderBy("id DESC") + @OrderBy("created DESC") private List<Identifier> identifiers; @ToString.Exclude diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/table/Table.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/table/Table.java index 42faf301ca49b73bfc4bcded087b9c005a5c764a..41a5c950a34bf5dbad6e30b762798256e5837600 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/table/Table.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/database/table/Table.java @@ -83,7 +83,7 @@ public class Table { @JoinColumn(name = "dbid", referencedColumnName = "tdbid", insertable = false, updatable = false) }) @Where(clause = "identifier_type='TABLE'") - @OrderBy("id DESC") + @OrderBy("created DESC") private List<Identifier> identifiers; @Embedded diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Creator.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Creator.java index 8a2bcf25e2e0a7bfc4add70406bc84503cb959d2..428d70d719cb3ca2751d29935787608cc7ab0678 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Creator.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Creator.java @@ -23,6 +23,9 @@ public class Creator { @Column(columnDefinition = "VARCHAR(36)") private UUID id; + @Column + private Integer ordinalPosition; + @Column(name = "given_names") private String firstname; diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Identifier.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Identifier.java index 04471509c6cd711b745f1a9060227d869f2dcb33..15fd36ca8eac8723c753d3315ccbe9ff8959dcea 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Identifier.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/Identifier.java @@ -54,7 +54,7 @@ public class Identifier implements Serializable { * Creators are created/updated/deleted by the Identifier entity. */ @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "identifier") - @OrderBy("id") + @OrderBy("ordinalPosition ASC") private List<Creator> creators; @Column(nullable = false) @@ -72,21 +72,21 @@ public class Identifier implements Serializable { * Titles are created/updated/deleted by the Identifier entity. */ @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "identifier") - @OrderBy("id") + @OrderBy("ordinalPosition ASC") private List<IdentifierTitle> titles; /** * Descriptions are created/updated/deleted by the Identifier entity. */ @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "identifier") - @OrderBy("id") + @OrderBy("ordinalPosition ASC") private List<IdentifierDescription> descriptions; /** * Funders are created/updated/deleted by the Identifier entity. */ @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "identifier") - @OrderBy("id") + @OrderBy("ordinalPosition ASC") private List<IdentifierFunder> funders; /** @@ -144,7 +144,7 @@ public class Identifier implements Serializable { private Database database; @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "identifier") - @OrderBy("id") + @OrderBy("ordinalPosition ASC") private List<RelatedIdentifier> relatedIdentifiers; @Column diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierDescription.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierDescription.java index ab73de406ee607253f14a182a4c9af9f748666dc..72c531c82b4e7d081a1112513a633040c3cc4e6c 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierDescription.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierDescription.java @@ -25,6 +25,9 @@ public class IdentifierDescription implements Serializable { @Column(columnDefinition = "VARCHAR(36)") private UUID id; + @Column + private Integer ordinalPosition; + @Column(nullable = false, columnDefinition = "TEXT") private String description; diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierFunder.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierFunder.java index eb3c21e61794726e53cabfcfe8e32e58072789d9..252957f8e43e6915a7b6361a3d9d92e8c707a234 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierFunder.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierFunder.java @@ -24,6 +24,9 @@ public class IdentifierFunder implements Serializable { @Column(columnDefinition = "VARCHAR(36)") private UUID id; + @Column + private Integer ordinalPosition; + @Column(nullable = false) private String funderName; diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierTitle.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierTitle.java index 8dbbdac0a9dd7804f61315892201c5df53857403..c19a02e9873ccf0654d81d6ca384c3d0ee748dd1 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierTitle.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/IdentifierTitle.java @@ -25,6 +25,9 @@ public class IdentifierTitle implements Serializable { @Column(columnDefinition = "VARCHAR(36)") private UUID id; + @Column + private Integer ordinalPosition; + @Column(nullable = false, columnDefinition = "TEXT") private String title; diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/RelatedIdentifier.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/RelatedIdentifier.java index e0eb28d11b7c68753c2f99564d28bb59be7fbf17..29fcf641599b08ca15d14fc7a360eb8e1a934973 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/RelatedIdentifier.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/entity/identifier/RelatedIdentifier.java @@ -25,6 +25,9 @@ public class RelatedIdentifier { @Column(columnDefinition = "VARCHAR(36)") private UUID id; + @Column + private Integer ordinalPosition; + @Column(nullable = false) private String value; diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java index 0ed6e783c033c7f85e9f3278552374de8ab6f50e..80aa58ef5239a47d7dbbbdbf4e1e1c4f519efd76 100644 --- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java +++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java @@ -104,6 +104,12 @@ public interface MetadataMapper { }) DataTypeDto dataTypeToDataTypeDto(DataType data); + StorageEventDto storageEventToStorageEventDto(StorageEvent data); + + StorageEvent storageEventDtoToStorageEvent(StorageEventDto data); + + StorageEvent createStorageEventDtoToStorageEvent(CreateStorageEventDto data); + @Mappings({ @Mapping(target = "databaseName", source = "internalName"), @Mapping(target = "ownerUsername", source = "owner.username")