From ed5576ca1a1511e7cd2137de0687da5ea8069e8e Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 14 Feb 2024 08:32:57 +0100 Subject: [PATCH 001/181] Add endpoints for new API. --- .../web/IMetadataController_v2.java | 184 +++++++ .../web/impl/MetadataControllerImpl_v2.java | 507 ++++++++++++++++++ 2 files changed, 691 insertions(+) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java new file mode 100644 index 00000000..f4b674ec --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java @@ -0,0 +1,184 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web; + +import edu.kit.datamanager.metastore2.domain.AclRecord; +import edu.kit.datamanager.metastore2.domain.DataResource; +import edu.kit.datamanager.repo.domain.DataResource; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.ArraySchema; +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 java.net.URISyntaxException; +import java.time.Instant; +import java.util.List; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springdoc.core.converters.models.PageableAsQueryParam; +import org.springframework.boot.actuate.info.InfoContributor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Interface for metadata documents controller. + */ +@ApiResponses(value = { + @ApiResponse(responseCode = "401", description = "Unauthorized is returned if authorization in required but was not provided."), + @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) +public interface IMetadataController_v2 extends InfoContributor { + + @Operation(summary = "Ingest a new metadata document and its record.", description = "This endpoint allows to create a new metadata record by providing the record metadata as JSON document as well as the actual metadata as file upload. The record metadata mainly contains " + + "the resource identifier the record is associated with as well as the identifier of the schema which can be used to validate the provided metadata document. In the current version, both parameters are required. If no schema version is given (if 'INTERNAL' reference" + + "is used) the most recent schema version will be used.", + responses = { + @ApiResponse(responseCode = "201", description = "Created is returned only if the record has been validated, persisted and the document was successfully validated and stored.", content = @Content(schema = @Schema(implementation = DataResource.class))), + @ApiResponse(responseCode = "400", description = "Bad Request is returned if the provided metadata record is invalid or if the validation using the provided schema failed."), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no schema for the provided schema id was found."), + @ApiResponse(responseCode = "409", description = "A Conflict is returned, if there is already a record for the related resource id and the provided schema id.")}) + + @RequestMapping(value = {"/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) + @ResponseBody + public ResponseEntity createRecord( + @Parameter(description = "Json representation of the datacite record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile dataciteRecord, + @Parameter(description = "The metadata document associated with the record. The document must match the schema selected by the record.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, + final HttpServletRequest request, + final HttpServletResponse response, + final UriComponentsBuilder uriBuilder) throws URISyntaxException; + + @Operation(summary = "Get a metadata record by id.", description = "Obtain a single record by its resource identifier. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " + + "by providing a version number as request parameter.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = DataResource.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) + + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.metadata-record+json"}) + public ResponseEntity getRecordById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); + + @Operation(summary = "Get a metadata record by id.", description = "Obtain a single record by its resource identifier. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " + + "by providing a version number as request parameter.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = DataResource.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) + + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.acl+json"}) + public ResponseEntity getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); + + @Operation(summary = "Get a landing page by id.", description = "Obtain a single record by its resource identifier. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " + + "by providing a version number as request parameter.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = DataResource.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) + + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"text/html"}) + public ModelAndView getLandingpageById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the metadata document. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); + @Operation(summary = "Get a metadata document by record identifier.", description = "Obtain a single metadata document identified by its resource identifier." + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + + "Furthermore, a specific version of the metadata document can be returned by providing a version number as request parameter.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the metadata document is returned if the record exists and the user has sufficient permission."), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) + + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}) + @ResponseBody + public ResponseEntity getMetadataDocumentById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); + + @Operation(summary = "Get all records.", description = "List all records in a paginated and sorted form. The result can be refined by providing id, specific related resource id(s) and/or metadata schema id(s) valid records must match. " + + "If 'id' is provided all available versions for given 'id' will be returned and all other parameters will be ignored." + + "If 'resourceId' and 'schemaId' are provided, a record matches if its related resource identifier AND the used metadata schema are matching. " + + "Furthermore, the UTC time of the last update can be provided in three different fashions: 1) Providing only updateFrom returns all records updated at or after the provided date, 2) Providing only updateUntil returns all records updated before or " + + "at the provided date, 3) Providing both returns all records updated within the provided date range." + + "If no parameters are provided, all accessible records are listed. If versioning is enabled, only the most recent version is listed (except in case of 'id' is provided).", + responses = { + @ApiResponse(responseCode = "200", description = "OK and a list of records or an empty list if no record matches.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DataResource.class))))}) + @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) + @PageableAsQueryParam + @ResponseBody + public ResponseEntity> getRecords( + @Parameter(description = "ID of the metadata document.", required = false) @RequestParam(value = "id", required = false) String id, + @Parameter(description = "A list of related resource identifiers.", required = false) @RequestParam(value = "resourceId", required = false) List relatedIds, + @Parameter(description = "A list of metadata schema identifiers.", required = false) @RequestParam(value = "schemaId", required = false) List schemaIds, + @Parameter(description = "The UTC time of the earliest update of a returned record.", required = false) @RequestParam(name = "from", required = false) Instant updateFrom, + @Parameter(description = "The UTC time of the latest update of a returned record.", required = false) @RequestParam(name = "until", required = false) Instant updateUntil, + @Parameter(hidden = true)@PageableDefault(sort = {"lastUpdate"}, direction = Sort.Direction.DESC) Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb); + + @Operation(summary = "Update a metadata record and/or metadata document.", description = "Apply an update to the metadata record with the provided resource identifier and/or its accociated metadata document." + + "If versioning is enabled and a (new) metadata document is provided, a new version of the record is created. Otherwise, the record and/or its metadata document are overwritten.", + responses = { + @ApiResponse(responseCode = "200", description = "OK is returned in case of a successful update, e.g. the record (if provided) was in the correct format and the document (if provided) matches the provided schema id." + + "The updated record is returned in the response.", content = @Content(schema = @Schema(implementation = DataResource.class))), + @ApiResponse(responseCode = "400", description = "Bad Request is returned if the provided metadata record is invalid or if the validation using the provided schema failed."), + @ApiResponse(responseCode = "404", description = "Not Found is returned if no record for the provided id or no schema for the provided schema id was found.")}) + @RequestMapping(value = "/{id}", method = RequestMethod.PUT, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {"application/json"}) + @Parameters({ + @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) + }) + ResponseEntity updateRecord( + @Parameter(description = "The resource identifier.", required = true) @PathVariable("id") String id, + @Parameter(description = "JSON representation of the metadata record.", required = false) @RequestPart(name = "record", required = false) final MultipartFile dataciteRecord, + @Parameter(description = "The metadata document associated with the record. The document must match the schema defined in the record.", required = false) @RequestPart(name = "document", required = false) final MultipartFile document, + final WebRequest request, + final HttpServletResponse response, + final UriComponentsBuilder uriBuilder + ); + + @Operation(summary = "Delete a record.", description = "Delete a single metadata record and the associated metadata document linked with the provided resource identifier. " + + "Deleting a record typically requires the caller to have special permissions. " + + "In some cases, deleting a record can also be available for the owner or other privileged users or can be forbidden at all. Deletion of a record affects all versions of the particular record.", + responses = { + @ApiResponse(responseCode = "204", description = "No Content is returned as long as no error occurs while deleting a record. Multiple delete operations to the same record will also return HTTP 204 even if the deletion succeeded in the first call.")}) + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.DELETE}) + @Parameters({ + @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) + }) + @ResponseBody + public ResponseEntity deleteRecord(@Parameter(description = "The resource identifier.", required = true) @PathVariable(value = "id") String id, WebRequest wr, HttpServletResponse hsr); +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java new file mode 100644 index 00000000..749cb51f --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java @@ -0,0 +1,507 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web.impl; + +import com.fasterxml.jackson.core.JsonParseException; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; +import edu.kit.datamanager.exceptions.AccessForbiddenException; +import edu.kit.datamanager.exceptions.BadArgumentException; +import edu.kit.datamanager.exceptions.ResourceNotFoundException; +import edu.kit.datamanager.exceptions.UnprocessableEntityException; +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.ILinkedDataResourceDao; +import edu.kit.datamanager.metastore2.domain.AclRecord; +import edu.kit.datamanager.metastore2.domain.LinkedDataResource; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.util.ActuatorUtil; +import edu.kit.datamanager.metastore2.util.DataResourceUtil; +import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; +import edu.kit.datamanager.metastore2.web.IMetadataController_v2; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.service.IMessagingService; +import edu.kit.datamanager.service.impl.LogfileMessagingService; +import edu.kit.datamanager.util.AuthenticationHelper; +import edu.kit.datamanager.util.ControllerUtils; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.UnaryOperator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.FileSystemResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Controller for metadata documents. + */ +@Controller +@RequestMapping(value = "/api/v2/metadata") +@Tag(name = "Metadata Repository") +@Schema(description = "Metadata Resource Management") +public class MetadataControllerImpl_v2 implements IMetadataController_v2 { + + public static final String POST_FILTER = "post_filter"; + /** + * Placeholder string for id of resource. (landingpage) + */ + public static final String PLACEHOLDER_ID = "$(id)"; + /** + * Placeholder string for version of resource. (landingpage) + */ + public static final String PLACEHOLDER_VERSION = "$(version)"; + + private static final Logger LOG = LoggerFactory.getLogger(MetadataControllerImpl_v2.class); + + private final ApplicationProperties applicationProperties; + + private final ILinkedDataResourceDao metadataRecordDao; + + private final MetastoreConfiguration metadataConfig; + + private final IDataResourceDao dataResourceDao; + + /** + * Optional messagingService bean may or may not be available, depending on a + * service's configuration. If messaging capabilities are disabled, this bean + * should be not available. In that case, messages are only logged. + */ + @Autowired + private Optional messagingService; + + private final String guestToken; + + /** + * Constructor for metadata documents controller. + * + * @param applicationProperties Configuration for controller. + * @param metadataConfig Configuration for metadata documents repository. + * @param metadataRecordDao DAO for metadata records. + * @param dataResourceDao DAO for data resources. + */ + public MetadataControllerImpl_v2(ApplicationProperties applicationProperties, + MetastoreConfiguration metadataConfig, + ILinkedDataResourceDao metadataRecordDao, + IDataResourceDao dataResourceDao) { + this.applicationProperties = applicationProperties; + this.metadataConfig = metadataConfig; + this.metadataRecordDao = metadataRecordDao; + this.dataResourceDao = dataResourceDao; + LOG.info("------------------------------------------------------"); + LOG.info("------{}", this.metadataConfig); + LOG.info("------------------------------------------------------"); + LOG.trace("Create guest token"); + guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken("guest", RepoUserRole.GUEST). + addSimpleClaim("email", "metastore@localhost"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + DataResourceUtil.setToken(guestToken); + } + + @Override + public ResponseEntity createRecord( + @RequestPart(name = "record") final MultipartFile recordDocument, + @RequestPart(name = "document") final MultipartFile document, + HttpServletRequest request, + HttpServletResponse response, + UriComponentsBuilder uriBuilder) throws URISyntaxException { + + long nano1 = System.nanoTime() / 1000000; + LOG.trace("Performing createRecord({},...).", recordDocument); + DataResource metadataRecord; + if (recordDocument == null || recordDocument.isEmpty()) { + String message = "No metadata record provided. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + try { + metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); + } catch (IOException ex) { + String message = "No valid metadata record provided. Returning HTTP BAD_REQUEST."; + if (ex instanceof JsonParseException) { + message = message + " Reason: " + ex.getMessage(); + } + LOG.error("Error parsing json: ", ex); + throw new BadArgumentException(message); + } + long nano2 = System.nanoTime() / 1000000; + + if (metadataRecord.getRelatedResource() == null || metadataRecord.getRelatedResource().getIdentifier() == null || metadataRecord.getSchema() == null || metadataRecord.getSchema().getIdentifier() == null) { + LOG.error("Mandatory attributes relatedResource and/or schemaId not found in record. Returning HTTP BAD_REQUEST."); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Mandatory attributes relatedResource and/or schemaId not found in record."); + } + + LOG.debug("Test for existing metadata record for given schema and resource"); + ResourceIdentifier schemaIdentifier; + try { + schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(metadataConfig, metadataRecord); + } catch (ResourceNotFoundException rnfe) { + LOG.debug("Error checking for existing relations.", rnfe); + throw new UnprocessableEntityException("Schema ID seems to be invalid"); + } + boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId(metadataRecord.getRelatedResource().getIdentifier(), schemaIdentifier.getIdentifier()); + long nano3 = System.nanoTime() / 1000000; + + if (recordAlreadyExists) { + String message = String.format("Conflict! There is already a metadata document with " + + "the same schema ('%s') and the same related resource ('%s')", + metadataRecord.getSchemaId(), + metadataRecord.getRelatedResource().getIdentifier()); + LOG.error(message); + return ResponseEntity.status(HttpStatus.CONFLICT).body(message); + } + DataResource result = DataResourceUtil.createDataResource(metadataConfig, recordDocument, document); + // Successfully created metadata record. + long nano4 = System.nanoTime() / 1000000; + LOG.trace("Metadata record successfully persisted. Returning result."); + DataResourceUtil.fixMetadataDocumentUri(result); + long nano5 = System.nanoTime() / 1000000; + metadataRecordDao.save(new LinkedDataResource(result)); + long nano6 = System.nanoTime() / 1000000; + + URI locationUri; + locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), result.getRecordVersion(), null, null)).toUri(); + long nano7 = System.nanoTime() / 1000000; + LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); + + LOG.trace("Sending CREATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryCreateMetadataMessage(result, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); + + return ResponseEntity.created(locationUri).eTag("\"" + result.getEtag() + "\"").body(result); + } + + @Override + public ResponseEntity getRecordById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing getRecordById({}, {}).", id, version); + + LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); + DataResource metadataRecord = DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); + LOG.trace("Metadata record found. Prepare response."); + //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN + LOG.trace("Get ETag of DataResource."); + String etag = metadataRecord.getEtag(); + DataResourceUtil.fixMetadataDocumentUri(metadataRecord); + + return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); + } + + @Override + public ResponseEntity getAclById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing getAclById({}, {}).", id, version); + if (!AuthenticationHelper.isAuthenticatedAsService()) { + throw new AccessForbiddenException("Only for services!"); + } + + DataResource metadataRecord = DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); + DataResourceUtil.fixMetadataDocumentUri(metadataRecord); + AclRecord aclRecord = new AclRecord(); + aclRecord.setAcl(metadataRecord.getAcl()); + aclRecord.setDataResource(metadataRecord); + + return ResponseEntity.ok().body(aclRecord); + } + + @Override + public ResponseEntity getMetadataDocumentById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); + + Path metadataDocumentPath = DataResourceUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); + + return ResponseEntity. + ok(). + header(HttpHeaders.CONTENT_LENGTH, String.valueOf(metadataDocumentPath.toFile().length())). + body(new FileSystemResource(metadataDocumentPath.toFile())); + } + + @Override + public ModelAndView getLandingpageById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr) { + LOG.trace("Performing Landing page for metadata document with ({}, {}).", id, version); + String redirectUrl = applicationProperties.getMetadataLandingPage(); + redirectUrl = redirectUrl.replace(PLACEHOLDER_ID, id); + String versionString = ""; + if (version != null) { + versionString = version.toString(); + } + redirectUrl = "redirect:" + redirectUrl.replace(PLACEHOLDER_VERSION, versionString); + + LOG.trace("Redirect to '{}'", redirectUrl); + + return new ModelAndView(redirectUrl); + } + + public ResponseEntity> getAllVersions( + @PathVariable(value = "id") String id, + Pageable pgbl + ) { + LOG.trace("Performing getAllVersions({}).", id); + // Search for resource type of MetadataSchemaRecord + + //if security is enabled, include principal in query + LOG.debug("Performing query for records."); + DataResource recordByIdAndVersion = DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id); + List recordList = new ArrayList<>(); + long totalNoOfElements = recordByIdAndVersion.getRecordVersion(); + for (long version = totalNoOfElements - pgbl.getOffset(), size = 0; version > 0 && size < pgbl.getPageSize(); version--, size++) { + recordList.add(DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id, version)); + } + + LOG.trace("Transforming Dataresource to DataResource"); + List metadataList = new ArrayList<>(); + recordList.forEach(metadataRecord -> { + DataResourceUtil.fixMetadataDocumentUri(metadataRecord); + metadataList.add(metadataRecord); + }); + + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); + + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); + } + + @Override + public ResponseEntity> getRecords( + @RequestParam(value = "id", required = false) String id, + @RequestParam(value = "resourceId", required = false) List relatedIds, + @RequestParam(value = "schemaId", required = false) List schemaIds, + @RequestParam(name = "from", required = false) Instant updateFrom, + @RequestParam(name = "until", required = false) Instant updateUntil, + Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb + ) { + LOG.trace("Performing getRecords({}, {}, {}, {}).", relatedIds, schemaIds, updateFrom, updateUntil); + if (id != null) { + return getAllVersions(id, pgbl); + } + // Search for resource type of MetadataSchemaRecord + Specification spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResource.RESOURCE_TYPE)); + // Add authentication if enabled + if (metadataConfig.isAuthEnabled()) { + boolean isAdmin; + isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); + // Add authorization for non administrators + if (!isAdmin) { + List authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); + if (authorizationIdentities != null) { + LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); + Specification permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); + spec = spec.and(permissionSpec); + } else { + LOG.trace("No permission information provided. Skip creating permission specification."); + } + } + } + List allRelatedIdentifiersSchema = new ArrayList<>(); + List allRelatedIdentifiersResource = new ArrayList<>(); + +// File file = new File(new URIoa) + if (schemaIds != null) { + for (String schemaId : schemaIds) { + MetadataSchemaRecord currentSchemaRecord; + try { + currentSchemaRecord = DataResourceUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); + // Test for internal URI -> Transform to global URI. + if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { + ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); + currentSchemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); + } + allRelatedIdentifiersSchema.add(currentSchemaRecord.getSchemaDocumentUri()); + } catch (Exception rnfe) { + // schemaID not found set version to 1 + currentSchemaRecord = new MetadataSchemaRecord(); + currentSchemaRecord.setSchemaVersion(1l); + allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); + } + for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { + MetadataSchemaRecord schemaRecord = DataResourceUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); + // Test for internal URI -> Transform to global URI. + if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { + ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); + schemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); + } + allRelatedIdentifiersSchema.add(schemaRecord.getSchemaDocumentUri()); + } + } + Specification schemaSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersSchema.toArray(new String[allRelatedIdentifiersSchema.size()])); + spec = spec.and(schemaSpecification); + } + if (relatedIds != null) { + allRelatedIdentifiersResource.addAll(relatedIds); + Specification relResourceSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersResource.toArray(new String[allRelatedIdentifiersResource.size()])); + spec = spec.and(relResourceSpecification); + } + if ((updateFrom != null) || (updateUntil != null)) { + spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); + } + + // Hide revoked and gone data resources. + DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; + List stateList = Arrays.asList(states); + spec = spec.and(StateSpecification.toSpecification(stateList)); + + if (LOG.isTraceEnabled()) { + Page records = dataResourceDao.findAll(pgbl); + LOG.trace("List all data resources..."); + LOG.trace("-----------------------------------------------"); + for (DataResource item : records.getContent()) { + LOG.trace("- '{}'", item); + } + LOG.trace("-----------------------------------------------"); + LOG.trace("Specification: '{}'", spec); + } + LOG.debug("Performing query for records."); + Page records = dataResourceDao.findAll(spec, pgbl); + + LOG.trace("Transforming Dataresource to DataResource"); + List recordList = records.getContent(); + List metadataList = new ArrayList<>(); + recordList.forEach(metadataRecord -> { + DataResource item = DataResourceUtil.migrateToDataResource(metadataConfig, metadataRecord, false); + DataResourceUtil.fixMetadataDocumentUri(item); + metadataList.add(item); + }); + + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); + + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); + } + + @Override + public ResponseEntity updateRecord( + @PathVariable("id") String id, + @RequestPart(name = "record", required = false) MultipartFile metadataRecord, + @RequestPart(name = "document", required = false) final MultipartFile document, + WebRequest request, + HttpServletResponse response, + UriComponentsBuilder uriBuilder + ) { + LOG.trace("Performing updateRecord({}, {}, {}).", id, metadataRecord, "#document"); + UnaryOperator getById; + getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); + String eTag = ControllerUtils.getEtagFromHeader(request); + DataResource updateDataResource = DataResourceUtil.updateDataResource(metadataConfig, id, eTag, metadataRecord, document, getById); + + LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); + String etag = updateDataResource.getEtag(); + DataResourceUtil.fixMetadataDocumentUri(updateDataResource); + + URI locationUri; + locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(updateDataResource.getId(), updateDataResource.getRecordVersion(), null, null)).toUri(); + + LOG.trace("Sending UPDATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryUpdateMetadataMessage(updateDataResource, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); + + return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updateDataResource); + } + + @Override + public ResponseEntity deleteRecord( + @PathVariable(value = "id") String id, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing deleteRecord({}).", id); + UnaryOperator getById; + getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, wr, hsr)).toString(); + + String eTag = ControllerUtils.getEtagFromHeader(wr); + DataResourceUtil.deleteDataResource(metadataConfig, id, eTag, getById); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public void contribute(Info.Builder builder) { + LOG.trace("Check for MetadataRepo actuator information..."); + + URL basePath = metadataConfig.getBasepath(); + Map details = ActuatorUtil.testDirectory(basePath); + + if (!details.isEmpty()) { + details.put("No of metadata documents", Long.toString(DataResourceUtil.getNoOfDocuments())); + builder.withDetail("metadataRepo", details); + } + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} From 5389f8a16515b661cb67830bf1b884280e63de0b Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 19 Feb 2024 16:08:09 +0100 Subject: [PATCH 002/181] First steps... --- .../dao/ILinkedDataResourceDao.java | 20 + .../util/DataResourceRecordUtil.java | 1759 +++++++++++++++++ .../web/IMetadataController_v2.java | 1 - .../web/ISchemaRegistryController_v2.java | 184 ++ .../web/impl/MetadataControllerImpl_v2.java | 65 +- .../impl/SchemaRegistryControllerImpl_v2.java | 362 ++++ 6 files changed, 2357 insertions(+), 34 deletions(-) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/dao/ILinkedDataResourceDao.java create mode 100644 src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController_v2.java create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl_v2.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ILinkedDataResourceDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ILinkedDataResourceDao.java new file mode 100644 index 00000000..99753bb5 --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ILinkedDataResourceDao.java @@ -0,0 +1,20 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.dao; + +import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** + * DAO for linked metadata resources. + */ +public interface ILinkedDataResourceDao extends JpaRepository, JpaSpecificationExecutor { + + boolean existsMetadataRecordByRelatedResourceAndSchemaId(String relatedResource, String schemaId); + +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java new file mode 100644 index 00000000..8fbf42ff --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -0,0 +1,1759 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.util; + +import com.fasterxml.jackson.core.JsonParseException; +import edu.kit.datamanager.clients.SimpleServiceClient; +import edu.kit.datamanager.entities.Identifier; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.exceptions.AccessForbiddenException; +import edu.kit.datamanager.exceptions.BadArgumentException; +import edu.kit.datamanager.exceptions.CustomInternalServerError; +import edu.kit.datamanager.exceptions.ResourceNotFoundException; +import edu.kit.datamanager.exceptions.UnprocessableEntityException; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.IDataRecordDao; +import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; +import edu.kit.datamanager.metastore2.domain.DataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; +import static edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType.INTERNAL; +import static edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType.URL; +import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.metastore2.domain.Url2Path; +import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; +import static edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil.fixRelativeURI; +import edu.kit.datamanager.metastore2.validation.IValidator; +import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; +import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; +import edu.kit.datamanager.repo.domain.ContentInformation; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.Date; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.repo.domain.Scheme; +import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import edu.kit.datamanager.repo.service.IContentInformationService; +import edu.kit.datamanager.repo.util.ContentDataUtils; +import edu.kit.datamanager.repo.util.DataResourceUtils; +import edu.kit.datamanager.util.AuthenticationHelper; +import edu.kit.datamanager.util.ControllerUtils; +import io.swagger.v3.core.util.Json; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Utility class for handling json documents + */ +public class DataResourceRecordUtil { + + /** + * Separator for separating schemaId and schemaVersion. + */ + public static final String SCHEMA_VERSION_SEPARATOR = ":"; + /** + * Logger for messages. + */ + private static final Logger LOG = LoggerFactory.getLogger(DataResourceRecordUtil.class); + + private static final String LOG_ERROR_READ_METADATA_DOCUMENT = "Failed to read metadata document from input stream."; + private static final String LOG_SEPARATOR = "-----------------------------------------"; + private static final String LOG_SCHEMA_REGISTRY = "No external schema registries defined. Try to use internal one..."; + private static final String LOG_FETCH_SCHEMA = "Try to fetch schema from '{}'."; + private static final String PATH_SCHEMA = "schemas"; + private static final String LOG_ERROR_ACCESS = "Failed to access schema registry at '%s'. Proceeding with next registry."; + private static final String LOG_SCHEMA_RECORD = "Found schema record: '{}'"; + private static final String ERROR_PARSING_JSON = "Error parsing json: "; + + private static MetastoreConfiguration schemaConfig; + private static String guestToken = null; + + private static IDataRecordDao dataRecordDao; + private static ISchemaRecordDao schemaRecordDao; + private static IMetadataFormatDao metadataFormatDao; + + private static IUrl2PathDao url2PathDao; + + DataResourceRecordUtil() { + //Utility class + } + + /** + * Create/Ingest an instance of MetadataSchemaRecord. + * + * @param applicationProperties Settings of repository. + * @param recordDocument Record of the schema. + * @param document Schema document. + * @param getSchemaDocumentById Method for creating access URL. + * @return Record of registered schema document. + */ + public static DataResource createDataResourceRecord4Schema(MetastoreConfiguration applicationProperties, + MultipartFile recordDocument, + MultipartFile document, + BiFunction getSchemaDocumentById) { + DataResource metadataRecord; + + // Do some checks first. + metadataRecord = checkParameters(recordDocument, document, true); + + if (metadataRecord.getId() == null) { + String message = "Mandatory attribute 'id' not found in record. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + // Check if id is lower case and URL encodable. + DataResourceRecordUtil.check4validId(metadataRecord); + // Create schema record + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(metadataRecord.getId()); + if (!metadataRecord.getFormats().isEmpty()) { + String mimeType = metadataRecord.getFormats().iterator().next().toLowerCase(); + if (mimeType.contains("json")) { + schemaRecord.setType(JSON); + } else if (mimeType.contains("xml")) { + schemaRecord.setType(XML); + } + } + // End of parameter checks + // validate schema document / determine type if not given + validateMetadataSchemaDocument(applicationProperties, schemaRecord, document); + // set internal parameters + if (metadataRecord.getFormats().isEmpty()) { + LOG.trace("No mimetype set! Try to determine..."); + if (document.getContentType() != null) { + LOG.trace("Set mimetype determined from document: '{}'", document.getContentType()); + metadataRecord.getFormats().add(document.getContentType()); + } else { + LOG.trace("Set mimetype according to type '{}'.", schemaRecord.getType()); + switch (schemaRecord.getType()) { + case JSON: + metadataRecord.getFormats().add(MediaType.APPLICATION_JSON_VALUE); + break; + case XML: + metadataRecord.getFormats().add(MediaType.APPLICATION_XML_VALUE); + break; + default: + throw new BadArgumentException("Please provide mimetype for type '" + schemaRecord.getType() + "'"); + } + } + } + metadataRecord.setVersion(Long.toString(1)); + // create record. + DataResource dataResource = metadataRecord; + DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); + // store document + ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); + schemaRecord.setVersion(applicationProperties.getAuditService().getCurrentVersion(dataResource.getId())); + schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); + schemaRecord.setDocumentHash(contentInformation.getHash()); + saveNewSchemaRecord(schemaRecord); + // Settings for OAI PMH + if (MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType())) { + try { + MetadataFormat metadataFormat = new MetadataFormat(); + metadataFormat.setMetadataPrefix(schemaRecord.getSchemaId()); + metadataFormat.setSchema(getSchemaDocumentById.apply(schemaRecord.getSchemaId(), schemaRecord.getVersion())); + String metadataNamespace = SchemaUtils.getTargetNamespaceFromSchema(document.getBytes()); + metadataFormat.setMetadataNamespace(metadataNamespace); + metadataFormatDao.save(metadataFormat); + } catch (IOException ex) { + String message = LOG_ERROR_READ_METADATA_DOCUMENT; + LOG.error(message, ex); + throw new UnprocessableEntityException(message); + } + } + + return metadataRecord; + } + + /** + * Create a digital object from metadata record and metadata document. + * + * @param applicationProperties Configuration properties. + * @param recordDocument Metadata record. + * @param document Metadata document. + * @return Enriched metadata record. + */ + public static MetadataRecord createDataResource4MetadataDocument(MetastoreConfiguration applicationProperties, + MultipartFile recordDocument, MultipartFile document) { + DataResource metadataRecord; + long nano1 = System.nanoTime() / 1000000; + // Do some checks first. + metadataRecord = checkParameters(recordDocument, document, true); + + if (metadataRecord.getRelatedResource() == null || metadataRecord.getRelatedResource().getIdentifier() == null || metadataRecord.getSchema() == null || metadataRecord.getSchema().getIdentifier() == null) { + String message = "Mandatory attributes relatedResource and/or schema not found in record. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + // Test for schema version + if (metadataRecord.getSchemaVersion() == null) { + MetadataSchemaRecord currentSchemaRecord; + try { + currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(applicationProperties, metadataRecord.getSchema()); + } catch (ResourceNotFoundException rnfe) { + throw new UnprocessableEntityException("Unknown schema ID '" + metadataRecord.getSchema().getIdentifier() + "'!"); + } + metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); + } + + // validate document + long nano2 = System.nanoTime() / 1000000; + // validate schema document + validateMetadataDocument(applicationProperties, metadataRecord, document); + // set internal parameters + metadataRecord.setRecordVersion(1l); + + long nano3 = System.nanoTime() / 1000000; + // create record. + DataResource dataResource = migrateToDataResource(applicationProperties, metadataRecord); + // add id as internal identifier if exists + // Note: DataResourceUtils.createResource will ignore id of resource. + // id will be set to alternate identifier if exists. + if (dataResource.getId() != null) { + // check for valid identifier without any chars which may be encoded + try { + String originalId = dataResource.getId(); + String value = URLEncoder.encode(originalId, StandardCharsets.UTF_8.toString()); + if (!value.equals(originalId)) { + String message = "Not a valid id! Encoded: " + value; + LOG.error(message); + throw new BadArgumentException(message); + } + } catch (UnsupportedEncodingException ex) { + String message = "Error encoding id " + metadataRecord.getSchemaId(); + LOG.error(message); + throw new CustomInternalServerError(message); + } + + dataResource.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(dataResource.getId())); + } + long nano4 = System.nanoTime() / 1000000; + DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); + long nano5 = System.nanoTime() / 1000000; + // store document + ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); + long nano6 = System.nanoTime() / 1000000; + // Create additional metadata record for faster access + DataRecord dataRecord = new DataRecord(); + dataRecord.setMetadataId(createResource.getId()); + dataRecord.setVersion(metadataRecord.getRecordVersion()); + dataRecord.setSchemaId(metadataRecord.getSchema().getIdentifier()); + dataRecord.setSchemaVersion(metadataRecord.getSchemaVersion()); + dataRecord.setMetadataDocumentUri(contentInformation.getContentUri()); + dataRecord.setDocumentHash(contentInformation.getHash()); + dataRecord.setLastUpdate(dataResource.getLastUpdate()); + saveNewDataRecord(dataRecord); + long nano7 = System.nanoTime() / 1000000; + LOG.info("Create Record times, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); + + return migrateToMetadataRecord(applicationProperties, createResource, true); + } + + /** + * Update a digital object with given metadata record and/or metadata + * document. + * + * @param applicationProperties Configuration properties. + * @param resourceId Identifier of digital object. + * @param eTag ETag of the old digital object. + * @param recordDocument Metadata record. + * @param document Metadata document. + * @param supplier Function for updating record. + * @return Enriched metadata record. + */ + public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applicationProperties, + String resourceId, + String eTag, + MultipartFile recordDocument, + MultipartFile document, + UnaryOperator supplier) { + MetadataRecord metadataRecord = null; + MetadataRecord existingRecord; + + // Do some checks first. + if ((recordDocument == null || recordDocument.isEmpty()) && (document == null || document.isEmpty())) { + String message = "Neither metadata record nor metadata document provided."; + LOG.error(message); + throw new BadArgumentException(message); + } + if (!(recordDocument == null || recordDocument.isEmpty())) { + try { + metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), MetadataRecord.class); + } catch (IOException ex) { + String message = "Can't map record document to MetadataRecord"; + if (ex instanceof JsonParseException) { + message = message + " Reason: " + ex.getMessage(); + } + LOG.error(ERROR_PARSING_JSON, ex); + throw new BadArgumentException(message); + } + } + + LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); + DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); + LOG.trace("Checking provided ETag."); + ControllerUtils.checkEtag(eTag, dataResource); + if (metadataRecord != null) { + existingRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); + existingRecord = mergeRecords(existingRecord, metadataRecord); + dataResource = migrateToDataResource(applicationProperties, existingRecord); + } else { + dataResource = DataResourceUtils.copyDataResource(dataResource); + } + + boolean noChanges = false; + if (document != null) { + metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); + validateMetadataDocument(applicationProperties, metadataRecord, document); + + ContentInformation info; + String fileName = document.getOriginalFilename(); + info = getContentInformationOfResource(applicationProperties, dataResource); + if (info != null) { + fileName = info.getRelativePath(); + noChanges = true; + // Check for changes... + try { + byte[] currentFileContent; + File file = new File(URI.create(info.getContentUri())); + if (document.getSize() == Files.size(file.toPath())) { + currentFileContent = FileUtils.readFileToByteArray(file); + byte[] newFileContent = document.getBytes(); + for (int index = 0; index < currentFileContent.length; index++) { + if (currentFileContent[index] != newFileContent[index]) { + noChanges = false; + break; + } + } + } else { + noChanges = false; + } + } catch (IOException ex) { + LOG.error("Error reading current file!", ex); + } + } + if (!noChanges) { + // Everything seems to be fine update document and increment version + LOG.trace("Updating schema document (and increment version)..."); + String version = dataResource.getVersion(); + if (version != null) { + dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + } + ContentDataUtils.addFile(applicationProperties, dataResource, document, fileName, null, true, supplier); + } + + } else { + // validate if document is still valid due to changed record settings. + metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); + URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); + + Path metadataDocumentPath = Paths.get(metadataDocumentUri); + if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { + LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); + throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); + } + + try { + InputStream inputStream = Files.newInputStream(metadataDocumentPath); + SchemaRecord schemaRecord = MetadataSchemaRecordUtil.getSchemaRecord(metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); + MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); + } catch (IOException ex) { + LOG.error("Error validating file!", ex); + } + + } + if (noChanges) { + Optional dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(dataResource.getId()); + if (dataRecord.isPresent()) { + dataRecordDao.delete(dataRecord.get()); + } + } + dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); + + return migrateToMetadataRecord(applicationProperties, dataResource, true); + } + + /** + * Delete a digital object with given identifier. + * + * @param applicationProperties Configuration properties. + * @param id Identifier of digital object. + * @param eTag ETag of the old digital object. + * @param supplier Function for updating record. + */ + public static void deleteMetadataRecord(MetastoreConfiguration applicationProperties, + String id, + String eTag, + UnaryOperator supplier) { + DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); + try { + DataResourceUtils.getResourceByIdentifierOrRedirect(applicationProperties, id, null, supplier); + } catch (ResourceNotFoundException rnfe) { + Optional dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(id); + while (dataRecord.isPresent()) { + dataRecordDao.delete(dataRecord.get()); + dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(id); + } + } + } + + /** + * Migrate metadata record to data resource. + * + * @param applicationProperties Configuration settings of repository. + * @param metadataRecord Metadata record to migrate. + * @return Data resource of metadata record. + */ + public static DataResource migrateToDataResource(RepoBaseConfiguration applicationProperties, + MetadataRecord metadataRecord) { + DataResource dataResource; + if (metadataRecord.getId() != null) { + try { + dataResource = applicationProperties.getDataResourceService().findById(metadataRecord.getId(), metadataRecord.getRecordVersion()); + dataResource = DataResourceUtils.copyDataResource(dataResource); + } catch (ResourceNotFoundException rnfe) { + LOG.error("Error catching DataResource for " + metadataRecord.getId() + " -> " + rnfe.getMessage()); + dataResource = DataResource.factoryNewDataResource(metadataRecord.getId()); + dataResource.setVersion("1"); + } + } else { + dataResource = new DataResource(); + dataResource.setVersion("1"); + } + dataResource.setAcls(metadataRecord.getAcl()); + if (metadataRecord.getCreatedAt() != null) { + boolean createDateExists = false; + Set dates = dataResource.getDates(); + for (edu.kit.datamanager.repo.domain.Date d : dates) { + if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { + LOG.trace("Creation date entry found."); + createDateExists = true; + break; + } + } + if (!createDateExists) { + dataResource.getDates().add(Date.factoryDate(metadataRecord.getCreatedAt(), Date.DATE_TYPE.CREATED)); + } + } + Set identifiers = dataResource.getAlternateIdentifiers(); + if (metadataRecord.getPid() != null) { + ResourceIdentifier identifier = metadataRecord.getPid(); + MetadataSchemaRecordUtil.checkAlternateIdentifier(identifiers, identifier.getIdentifier(), Identifier.IDENTIFIER_TYPE.valueOf(identifier.getIdentifierType().name())); + } else { + LOG.trace("Remove existing identifiers (others than URL)..."); + Set removeItems = new HashSet<>(); + for (Identifier item : identifiers) { + if (item.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { + LOG.trace("... {}, {}", item.getValue(), item.getIdentifierType()); + removeItems.add(item); + } + } + identifiers.removeAll(removeItems); + } + boolean relationFound = false; + boolean schemaIdFound = false; + for (RelatedIdentifier relatedIds : dataResource.getRelatedIdentifiers()) { + if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { + LOG.trace("Set relation to '{}'", metadataRecord.getRelatedResource()); + relatedIds.setValue(metadataRecord.getRelatedResource().getIdentifier()); + relatedIds.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(metadataRecord.getRelatedResource().getIdentifierType().name())); + relationFound = true; + } + if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + updateRelatedIdentifierForSchema(relatedIds, metadataRecord); + schemaIdFound = true; + } + } + if (!relationFound) { + RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, metadataRecord.getRelatedResource().getIdentifier(), null, null); + relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(metadataRecord.getRelatedResource().getIdentifierType().name())); + dataResource.getRelatedIdentifiers().add(relatedResource); + } + if (!schemaIdFound) { + RelatedIdentifier schemaId = updateRelatedIdentifierForSchema(null, metadataRecord); + dataResource.getRelatedIdentifiers().add(schemaId); + } + String defaultTitle = "Metadata 4 metastore"; + boolean titleExists = false; + for (Title title : dataResource.getTitles()) { + if (title.getTitleType() == Title.TYPE.OTHER && title.getValue().equals(defaultTitle)) { + titleExists = true; + } + } + if (!titleExists) { + dataResource.getTitles().add(Title.factoryTitle(defaultTitle, Title.TYPE.OTHER)); + } + dataResource.setResourceType(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE)); + checkLicense(dataResource, metadataRecord.getLicenseUri()); + + return dataResource; + } + + /** + * Migrate data resource to metadata record. + * + * @param applicationProperties Configuration settings of repository. + * @param dataResource Data resource to migrate. + * @param provideETag Flag for calculating etag. + * @return Metadata record of data resource. + */ + public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration applicationProperties, + DataResource dataResource, + boolean provideETag) { + long nano1 = System.nanoTime() / 1000000; + MetadataRecord metadataRecord = new MetadataRecord(); + if (dataResource != null) { + metadataRecord.setId(dataResource.getId()); + if (provideETag) { + metadataRecord.setETag(dataResource.getEtag()); + } + metadataRecord.setAcl(dataResource.getAcls()); + + for (edu.kit.datamanager.repo.domain.Date d : dataResource.getDates()) { + if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { + LOG.trace("Creation date entry found."); + metadataRecord.setCreatedAt(d.getValue()); + break; + } + } + if (dataResource.getLastUpdate() != null) { + metadataRecord.setLastUpdate(dataResource.getLastUpdate()); + } + + for (Identifier identifier : dataResource.getAlternateIdentifiers()) { + if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { + if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().getValue())); + LOG.trace("Set PID to '{}' of type '{}'", resourceIdentifier.getIdentifier(), resourceIdentifier.getIdentifierType()); + metadataRecord.setPid(resourceIdentifier); + break; + } else { + LOG.debug("'INTERNAL' identifier shouldn't be used! Migrate them to 'URL' if possible."); + } + } + } + + Long recordVersion = 1l; + if (dataResource.getVersion() != null) { + recordVersion = Long.parseLong(dataResource.getVersion()); + } + metadataRecord.setRecordVersion(recordVersion); + + for (RelatedIdentifier relatedIds : dataResource.getRelatedIdentifiers()) { + LOG.trace("Found related Identifier: '{}'", relatedIds); + if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(relatedIds.getValue()); + if (relatedIds.getIdentifierType() != null) { + resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); + } + LOG.trace("Set relation to '{}'", resourceIdentifier); + metadataRecord.setRelatedResource(resourceIdentifier); + } + if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); + metadataRecord.setSchema(resourceIdentifier); + if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { + //Try to fetch version from URL (only works with URLs including the version as query parameter. + Matcher matcher = Pattern.compile(".*[&?]version=(\\d*).*").matcher(resourceIdentifier.getIdentifier()); + while (matcher.find()) { + metadataRecord.setSchemaVersion(Long.parseLong(matcher.group(1))); + } + } else { + metadataRecord.setSchemaVersion(1l); + } + LOG.trace("Set schema to '{}'", resourceIdentifier); + } + } + if (metadataRecord.getSchema() == null) { + String message = "Missing schema identifier for metadata document. Not a valid metadata document ID. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + DataRecord dataRecord = null; + long nano2 = System.nanoTime() / 1000000; + Optional dataRecordResult = dataRecordDao.findByMetadataIdAndVersion(dataResource.getId(), recordVersion); + long nano3 = System.nanoTime() / 1000000; + long nano4 = nano3; + boolean isAvailable = false; + boolean saveDataRecord = false; + if (dataRecordResult.isPresent()) { + LOG.trace("Get document URI from DataRecord."); + dataRecord = dataRecordResult.get(); + nano4 = System.nanoTime() / 1000000; + metadataRecord.setMetadataDocumentUri(dataRecord.getMetadataDocumentUri()); + metadataRecord.setDocumentHash(dataRecord.getDocumentHash()); + metadataRecord.setSchemaVersion(dataRecord.getSchemaVersion()); + isAvailable = true; + } else { + saveDataRecord = true; + } + if (!isAvailable) { + LOG.trace("Get document URI from ContentInformation."); + ContentInformation info; + info = getContentInformationOfResource(applicationProperties, dataResource); + nano4 = System.nanoTime() / 1000000; + if (info != null) { + metadataRecord.setDocumentHash(info.getHash()); + metadataRecord.setMetadataDocumentUri(info.getContentUri()); + MetadataSchemaRecord currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(schemaConfig, metadataRecord.getSchema()); + metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); + if (saveDataRecord) { + saveNewDataRecord(metadataRecord); + } + } + } + // Only one license allowed. So don't worry about size of set. + if (!dataResource.getRights().isEmpty()) { + metadataRecord.setLicenseUri(dataResource.getRights().iterator().next().getSchemeUri()); + } + long nano5 = System.nanoTime() / 1000000; + LOG.info("Migrate to MetadataRecord, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, provideETag); + } + + return metadataRecord; + } + + private static ContentInformation getContentInformationOfResource(RepoBaseConfiguration applicationProperties, + DataResource dataResource) { + ContentInformation returnValue = null; + long nano1 = System.nanoTime() / 1000000; + IContentInformationService contentInformationService = applicationProperties.getContentInformationService(); + ContentInformation info = new ContentInformation(); + info.setParentResource(dataResource); + long nano2 = System.nanoTime() / 1000000; + List listOfFiles = contentInformationService.findAll(info, PageRequest.of(0, 100)).getContent(); + long nano3 = System.nanoTime() / 1000000; + if (LOG.isTraceEnabled()) { + LOG.trace("Found {} files for resource '{}'", listOfFiles.size(), dataResource.getId()); + for (ContentInformation ci : listOfFiles) { + DataResource parentResource = ci.getParentResource(); + ci.setParentResource(null); + LOG.trace("ContentInformation: {}", ci); + ci.setParentResource(parentResource); + } + } + if (!listOfFiles.isEmpty()) { + returnValue = listOfFiles.get(0); + } + LOG.info("Get content information of resource, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1); + return returnValue; + } + + /** + * Returns schema record with the current version. + * + * @param metastoreProperties Configuration for accessing services + * @param schemaId SchemaID of the schema. + * @return MetadataSchemaRecord ResponseEntity in case of an error. + */ + public static MetadataSchemaRecord getCurrentInternalSchemaRecord(MetastoreConfiguration metastoreProperties, + String schemaId) { + LOG.trace("Get current internal schema record for id '{}'.", schemaId); + MetadataSchemaRecord returnValue = null; + boolean success = false; + StringBuilder errorMessage = new StringBuilder(); + if (metastoreProperties.getSchemaRegistries().size() == 0) { + LOG.trace(LOG_SCHEMA_REGISTRY); + + returnValue = MetadataSchemaRecordUtil.getRecordById(metastoreProperties, schemaId); + success = true; + } else { + for (String schemaRegistry : metastoreProperties.getSchemaRegistries()) { + LOG.trace(LOG_FETCH_SCHEMA, schemaRegistry); + URI schemaRegistryUri = URI.create(schemaRegistry); + UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(schemaRegistryUri.getScheme()).host(schemaRegistryUri.getHost()).port(schemaRegistryUri.getPort()).pathSegment(schemaRegistryUri.getPath(), PATH_SCHEMA, schemaId); + + URI finalUri = builder.build().toUri(); + + try { + returnValue = SimpleServiceClient.create(finalUri.toString()).withBearerToken(guestToken).accept(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).getResource(MetadataSchemaRecord.class + ); + success = true; + break; + } catch (HttpClientErrorException ce) { + String message = "Error accessing schema '" + schemaId + "' at '" + schemaRegistry + "'!"; + LOG.error(message, ce); + errorMessage.append(message).append("\n"); + } catch (RestClientException ex) { + String message = String.format(LOG_ERROR_ACCESS, schemaRegistry); + LOG.error(message, ex); + errorMessage.append(message).append("\n"); + } + } + } + if (!success) { + throw new UnprocessableEntityException(errorMessage.toString()); + } + LOG.trace(LOG_SCHEMA_RECORD, returnValue); + return returnValue; + } + + /** + * Returns schema record with the current version. + * + * @param metastoreProperties Configuration for accessing services + * @param schemaId SchemaID of the schema. + * @param version Version of the schema. + * @return MetadataSchemaRecord ResponseEntity in case of an error. + */ + public static MetadataSchemaRecord getInternalSchemaRecord(MetastoreConfiguration metastoreProperties, + String schemaId, + Long version) { + MetadataSchemaRecord returnValue = null; + boolean success = false; + StringBuilder errorMessage = new StringBuilder(); + LOG.trace("Get internal schema record for id '{}'.", schemaId); + if (metastoreProperties.getSchemaRegistries().size() == 0) { + LOG.trace(LOG_SCHEMA_REGISTRY); + + returnValue = MetadataSchemaRecordUtil.getRecordByIdAndVersion(metastoreProperties, schemaId, version); + success = true; + } else { + for (String schemaRegistry : metastoreProperties.getSchemaRegistries()) { + LOG.trace(LOG_FETCH_SCHEMA, schemaRegistry); + URI schemaRegistryUri = URI.create(schemaRegistry); + UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(schemaRegistryUri.getScheme()).host(schemaRegistryUri.getHost()).port(schemaRegistryUri.getPort()).pathSegment(schemaRegistryUri.getPath(), PATH_SCHEMA, schemaId).queryParam("version", version); + + URI finalUri = builder.build().toUri(); + + try { + returnValue = SimpleServiceClient.create(finalUri.toString()).withBearerToken(guestToken).accept(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).getResource(MetadataSchemaRecord.class + ); + success = true; + break; + } catch (HttpClientErrorException ce) { + String message = "Error accessing schema '" + schemaId + "' at '" + schemaRegistry + "'!"; + LOG.error(message, ce); + errorMessage.append(message).append("\n"); + } catch (RestClientException ex) { + String message = String.format(LOG_ERROR_ACCESS, schemaRegistry); + LOG.error(message, ex); + errorMessage.append(message).append("\n"); + } + } + } + if (!success) { + throw new UnprocessableEntityException(errorMessage.toString()); + } + LOG.trace(LOG_SCHEMA_RECORD, returnValue); + return returnValue; + } + + /** + * Update/create related identifier to values given by metadata record. + * + * @param relatedIdentifier related identifier (if null create a new one) + * @param metadataRecord record holding schema information. + * @return updated/created related identifier. + */ + private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentifier relatedIdentifier, MetadataRecord metadataRecord) { + if (relatedIdentifier == null) { + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, null, null, null); + } + ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(schemaIdentifier.getIdentifierType().name())); + relatedIdentifier.setValue(schemaIdentifier.getIdentifier()); + LOG.trace("Set relatedId for schema to '{}'", relatedIdentifier); + + return relatedIdentifier; + } + + /** + * Validate metadata document with given schema. + * + * @param metastoreProperties Configuration for accessing services + * @param metadataRecord metadata of the document. + * @param document document + */ + private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, + MetadataRecord metadataRecord, + MultipartFile document) { + LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, metadataRecord, document); + if (document == null || document.isEmpty()) { + String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + boolean validationSuccess = false; + StringBuilder errorMessage = new StringBuilder(); + if (metastoreProperties.getSchemaRegistries().isEmpty() || metadataRecord.getSchema().getIdentifierType() != IdentifierType.INTERNAL) { + LOG.trace(LOG_SCHEMA_REGISTRY); + if (schemaConfig != null) { + try { + MetadataSchemaRecordUtil.validateMetadataDocument(schemaConfig, document, metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); + validationSuccess = true; + } catch (Exception ex) { + String message = "Error validating document!"; + LOG.error(message, ex); + errorMessage.append(ex.getMessage()).append("\n"); + } + } else { + throw new CustomInternalServerError("No schema registries defined! "); + } + } else { + for (String schemaRegistry : metastoreProperties.getSchemaRegistries()) { + LOG.trace(LOG_FETCH_SCHEMA, schemaRegistry); + URI schemaRegistryUri = URI.create(schemaRegistry); + UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(schemaRegistryUri.getScheme()).host(schemaRegistryUri.getHost()).port(schemaRegistryUri.getPort()).pathSegment(schemaRegistryUri.getPath(), PATH_SCHEMA, metadataRecord.getSchema().getIdentifier(), "validate").queryParam("version", metadataRecord.getSchemaVersion()); + + URI finalUri = builder.build().toUri(); + + try { + HttpStatus status = SimpleServiceClient.create(finalUri.toString()).withBearerToken(guestToken).accept(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).withFormParam("document", document.getInputStream()).postForm(MediaType.MULTIPART_FORM_DATA); + + if (Objects.equals(HttpStatus.NO_CONTENT, status)) { + LOG.trace("Successfully validated document against schema {} in registry {}.", metadataRecord.getSchema().getIdentifier(), schemaRegistry); + validationSuccess = true; + break; + } + } catch (HttpClientErrorException ce) { + //not valid + String message = "Failed to validate metadata document against schema " + metadataRecord.getSchema().getIdentifier() + " at '" + schemaRegistry + "' with status " + ce.getStatusCode() + "."; + LOG.error(message, ce); + errorMessage.append(message).append("\n"); + } catch (IOException | RestClientException ex) { + String message = String.format(LOG_ERROR_ACCESS, schemaRegistry); + LOG.error(message, ex); + errorMessage.append(message).append("\n"); + } + } + } + if (!validationSuccess) { + throw new UnprocessableEntityException(errorMessage.toString()); + } + } + + public static DataResource getRecordById(MetastoreConfiguration metastoreProperties, + String recordId) throws ResourceNotFoundException { + return getRecordByIdAndVersion(metastoreProperties, recordId, null); + } + + + public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metastoreProperties, + String recordId, Long version) throws ResourceNotFoundException { + //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN + long nano = System.nanoTime() / 1000000; + long nano2; + Page dataResource; + try { + dataResource = metastoreProperties.getDataResourceService().findAllVersions(recordId, null); + } catch (ResourceNotFoundException ex) { + ex.setDetail("Metadata document with ID '" + recordId + "' doesn't exist!"); + throw ex; + } + nano2 = System.nanoTime() / 1000000; + Stream stream = dataResource.get(); + if (version != null) { + stream = stream.filter(resource -> Long.parseLong(resource.getVersion()) == version); + } + Optional findFirst = stream.findFirst(); + if (findFirst.isEmpty()) { + String message = String.format("Version '%d' of ID '%s' doesn't exist!", version, recordId); + LOG.error(message); + throw new ResourceNotFoundException(message); + } + long nano3 = System.nanoTime() / 1000000; + LOG.info("getRecordByIdAndVersion {}, {}, {}", nano, (nano2 - nano), (nano3 - nano)); + return findFirst.get(); + } + + public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, + String recordId) throws ResourceNotFoundException { + return getMetadataDocumentByIdAndVersion(metastoreProperties, recordId, null); + } + + public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, + String recordId, Long version) throws ResourceNotFoundException { + LOG.trace("Obtaining metadata record with id {} and version {}.", recordId, version); + MetadataRecord metadataRecord = getRecordByIdAndVersion(metastoreProperties, recordId, version); + + URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); + + Path metadataDocumentPath = Paths.get(metadataDocumentUri); + if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { + LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); + throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); + } + return metadataDocumentPath; + } + + /** + * Merge new metadata record in the existing one. + * + * @param managed Existing metadata record. + * @param provided New metadata record. + * @return Merged record + */ + public static MetadataRecord mergeRecords(MetadataRecord managed, MetadataRecord provided) { + if (provided != null && managed != null) { + //update pid + managed.setPid(mergeEntry("Update record->pid", managed.getPid(), provided.getPid())); + + //update acl + managed.setAcl(mergeAcl(managed.getAcl(), provided.getAcl())); + //update getRelatedResource + managed.setRelatedResource(mergeEntry("Updating record->relatedResource", managed.getRelatedResource(), provided.getRelatedResource())); + //update schemaId + managed.setSchema(mergeEntry("Updating record->schema", managed.getSchema(), provided.getSchema())); + //update schemaVersion + managed.setSchemaVersion(mergeEntry("Updating record->schemaVersion", managed.getSchemaVersion(), provided.getSchemaVersion())); + // update licenseUri + managed.setLicenseUri(mergeEntry("Updating record->licenseUri", managed.getLicenseUri(), provided.getLicenseUri(), true)); + } else { + managed = (managed != null) ? managed : provided; + } + return managed; + } + + /** + * Check validity of acl list and then merge new acl list in the existing one. + * + * @param managed Existing metadata record. + * @param provided New metadata record. + * @return Merged list + */ + public static Set mergeAcl(Set managed, Set provided) { + // Check for null parameters (which shouldn't happen) + managed = (managed == null) ? new HashSet<>() : managed; + provided = (provided == null) ? new HashSet<>() : provided; + if (!provided.isEmpty()) { + if (!provided.equals(managed)) { + // check for special access rights + // - only administrators are allowed to change ACL + checkAccessRights(managed, true); + // - at least principal has to remain as ADMIN + checkAccessRights(provided, false); + LOG.trace("Updating record acl from {} to {}.", managed, provided); + managed = provided; + } else { + LOG.trace("Provided ACL is still the same -> Continue using old one."); + } + } else { + LOG.trace("Provided ACL is empty -> Continue using old one."); + } + return managed; + } + + /** + * Set new value for existing one. + * + * @param description For logging purposes only + * @param managed Existing value. + * @param provided New value. + * @return Merged record + */ + public static T mergeEntry(String description, T managed, T provided) { + return mergeEntry(description, managed, provided, false); + } + + /** + * Set new value for existing one. + * + * @param description For logging purposes only + * @param managed Existing value. + * @param provided New value. + * @param overwriteWithNull Allows also deletion of a value. + * @return Merged record + */ + public static T mergeEntry(String description, T managed, T provided, boolean overwriteWithNull) { + if ((provided != null && !provided.equals(managed)) + || overwriteWithNull) { + LOG.trace(description + " from '{}' to '{}'", managed, provided); + managed = provided; + } + return managed; + } + + /** + * Return the number of ingested documents. If there are two versions of the + * same document this will be counted as two. + * + * @return Number of registered documents. + */ + public static long getNoOfDocuments() { + return dataRecordDao.count(); + } + + public static void setToken(String bearerToken) { + guestToken = bearerToken; + } + + /** + * Set schema config. + * + * @param aSchemaConfig the schemaConfig to set + */ + public static void setSchemaConfig(MetastoreConfiguration aSchemaConfig) { + schemaConfig = aSchemaConfig; + } + + /** + * Set DAO for data record. + * + * @param aDataRecordDao the dataRecordDao to set + */ + public static void setDataRecordDao(IDataRecordDao aDataRecordDao) { + dataRecordDao = aDataRecordDao; + } + + /** + * Set the DAO for MetadataFormat. + * + * @param aMetadataFormatDao the metadataFormatDao to set + */ + public static void setMetadataFormatDao(IMetadataFormatDao aMetadataFormatDao) { + metadataFormatDao = aMetadataFormatDao; + } + + /** + * Set the DAO for SchemaRecord. + * + * @param aSchemaRecordDao the schemaRecordDao to set + */ + public static void setSchemaRecordDao(ISchemaRecordDao aSchemaRecordDao) { + schemaRecordDao = aSchemaRecordDao; + } + + private static void saveNewDataRecord(MetadataRecord result) { + DataRecord dataRecord; + + // Create shortcut for access. + LOG.trace("Save new data record!"); + dataRecord = transformToDataRecord(result); + + saveNewDataRecord(dataRecord); + } + + private static DataRecord transformToDataRecord(MetadataRecord result) { + DataRecord dataRecord = null; + if (result != null) { + LOG.trace("Transform to data record!"); + dataRecord = new DataRecord(); + dataRecord.setMetadataId(result.getId()); + dataRecord.setVersion(result.getRecordVersion()); + dataRecord.setSchemaId(result.getSchema().getIdentifier()); + dataRecord.setSchemaVersion(result.getSchemaVersion()); + dataRecord.setDocumentHash(result.getDocumentHash()); + dataRecord.setMetadataDocumentUri(result.getMetadataDocumentUri()); + dataRecord.setLastUpdate(result.getLastUpdate()); + } + return dataRecord; + } + + private static void saveNewDataRecord(DataRecord dataRecord) { + if (dataRecordDao != null) { + try { + dataRecordDao.save(dataRecord); + } catch (Exception ex) { + LOG.error("Error saving data record", ex); + } + LOG.trace("Data record saved: {}", dataRecord); + } + } + + /** + * Checks if current user is allowed to access with given AclEntries. + * + * @param aclEntries AclEntries of resource. + * @param currentAcl Check current ACL (true) or new one (false). + * + * @return Allowed (true) or not. + */ + public static boolean checkAccessRights(Set aclEntries, boolean currentAcl) { + boolean isAllowed = false; + String errorMessage1 = "Error invalid ACL! Reason: Only ADMINISTRATORS are allowed to change ACL entries."; + String errorMessage2 = "Error invalid ACL! Reason: You are not allowed to revoke your own administrator rights."; + Authentication authentication = AuthenticationHelper.getAuthentication(); + List authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); + for (GrantedAuthority authority : authentication.getAuthorities()) { + authorizationIdentities.add(authority.getAuthority()); + } + if (authorizationIdentities.contains(RepoUserRole.ADMINISTRATOR.getValue())) { + //ROLE_ADMINISTRATOR detected -> no further permission check necessary. + return true; + } + if (LOG.isTraceEnabled()) { + LOG.trace("Check access rights for changing ACL list!"); + for (String authority : authorizationIdentities) { + LOG.trace("Indentity/Authority: '{}'", authority); + } + } + // Check if authorized user still has ADMINISTRATOR rights + Iterator iterator = aclEntries.iterator(); + while (iterator.hasNext()) { + AclEntry aclEntry = iterator.next(); + LOG.trace("'{}' has ’{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); + if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) + && authorizationIdentities.contains(aclEntry.getSid())) { + isAllowed = true; + LOG.trace("Confirm permission for updating ACL: '{}' has ’{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); + break; + } + } + if (!isAllowed) { + String errorMessage = currentAcl ? errorMessage1 : errorMessage2; + LOG.warn(errorMessage); + if (schemaConfig.isAuthEnabled()) { + if (currentAcl) { + throw new AccessForbiddenException(errorMessage1); + } else { + throw new BadArgumentException(errorMessage2); + } + } + } + return isAllowed; + } + + public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { + String metadataDocumentUri = metadataRecord.getMetadataDocumentUri(); + metadataRecord + .setMetadataDocumentUri(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImpl.class + ).getMetadataDocumentById(metadataRecord.getId(), metadataRecord.getRecordVersion(), null, null)).toUri().toString()); + LOG.trace("Fix metadata document Uri '{}' -> '{}'", metadataDocumentUri, metadataRecord.getMetadataDocumentUri()); + } + + public static void checkLicense(DataResource dataResource, String licenseUri) { + if (licenseUri != null) { + Set rights = dataResource.getRights(); + String licenseId = licenseUri.substring(licenseUri.lastIndexOf("/")); + Scheme license = Scheme.factoryScheme(licenseId, licenseUri); + if (rights.isEmpty()) { + rights.add(license); + } else { + // Check if license already exists (only one license allowed) + if (!rights.contains(license)) { + rights.clear(); + rights.add(license); + } + } + } else { + // Remove license + dataResource.getRights().clear(); + } + } + + public static void check4RelatedResource(DataResource dataResource, RelatedIdentifier relatedResource) { + if (relatedResource != null) { + Set relatedResources = dataResource.getRelatedIdentifiers(); + + if (relatedResources.isEmpty()) { + relatedResources.add(relatedResource); + } else { + // Check if related resource already exists (only one related resource of each type allowed) + for (RelatedIdentifier item : relatedResources) { + if (item.getRelationType().equals(relatedResource.getRelationType()) + && !item.getValue().equals(relatedResource.getValue())) { + relatedResources.remove(item); + relatedResources.add(relatedResource); + break; + } + } + } + } + } + + public static void validateRelatedResources4MetadataDocuments(DataResource dataResource) throws BadArgumentException { + int noOfRelatedData = 0; + int noOfRelatedSchemas = 0; + String message = "Invalid related resources! Expected '1' related resource found '%d'. Expected '1' related schema found '%d'!"; + if (dataResource != null) { + Set relatedResources = dataResource.getRelatedIdentifiers(); + + // Check if related resource already exists (only one related resource of type isMetadataFor allowed) + for (RelatedIdentifier item : relatedResources) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR)) { + noOfRelatedData++; + } + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + noOfRelatedSchemas++; + } + } + } + if (noOfRelatedData != 1 || noOfRelatedSchemas != 1) { + String errorMessage = ""; + if (noOfRelatedData == 0) { + errorMessage = "Mandatory attribute relatedIdentifier of type 'isMetadataFor' was not found in record. \n"; + } + if (noOfRelatedData > 1) { + errorMessage = "Mandatory attribute relatedIdentifier of type 'isMetadataFor' was provided more than once in record. \n"; + } + if (noOfRelatedSchemas == 0) { + errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'isDerivedFrom' was not found in record. \n"; + } + if (noOfRelatedSchemas > 1) { + errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'isDerivedFrom' was provided more than once in record. \n"; + } + errorMessage = errorMessage + "Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(errorMessage); + } + } + + /** + * Transform schema identifier to global available identifier (if neccessary). + * + * @param dataResourceRecord Metadata record hold schema identifier. + * @return ResourceIdentifier with a global accessible identifier. + */ + public static RelatedIdentifier getSchemaIdentifier(DataResource dataResourceRecord) { + LOG.trace("Get schema identifier for '{}'.", dataResourceRecord.getId()); + RelatedIdentifier schemaIdentifier = null; + + Set relatedResources = dataResourceRecord.getRelatedIdentifiers(); + + // Check if related resource already exists (only one related resource of type isMetadataFor allowed) + for (RelatedIdentifier item : relatedResources) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + schemaIdentifier = item; + } + } + return schemaIdentifier; + } + + /** + * Check if ID for schema is valid. Requirements: - shouldn't change if URL + * encoded - should be lower case If it's not lower case the original ID will + * we set as an alternate ID. + * + * @param metadataRecord Datacite Record. + */ + public static final void check4validSchemaId(DataResource metadataRecord) { + check4validId(metadataRecord); + String id = metadataRecord.getId(); + String lowerCaseId = id.toLowerCase(); + // schema id should be lower case due to elasticsearch + if (!lowerCaseId.equals(id)) { + metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(id)); + metadataRecord.setId(lowerCaseId); + } + } + + public static final void check4validId(DataResource metadataRecord) { + try { + String value = URLEncoder.encode(metadataRecord.getId(), StandardCharsets.UTF_8.toString()); + if (!value.equals(metadataRecord.getId())) { + String message = "Not a valid schema id! Encoded: " + value; + LOG.error(message); + throw new BadArgumentException(message); + } + } catch (UnsupportedEncodingException ex) { + String message = "Error encoding schemaId " + metadataRecord.getId(); + LOG.error(message); + throw new CustomInternalServerError(message); + } + } + + private static void validateMetadataSchemaDocument(MetastoreConfiguration metastoreProperties, SchemaRecord schemaRecord, MultipartFile document) { + LOG.debug("Validate metadata schema document..."); + if (document == null || document.isEmpty()) { + String message = "Missing metadata schema document in body. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + try { + validateMetadataSchemaDocument(metastoreProperties, schemaRecord, document.getBytes()); + } catch (IOException ex) { + String message = LOG_ERROR_READ_METADATA_DOCUMENT; + LOG.error(message, ex); + throw new UnprocessableEntityException(message); + } + } + + private static void validateMetadataSchemaDocument(MetastoreConfiguration metastoreProperties, SchemaRecord schemaRecord, byte[] document) { + LOG.debug("Validate metadata schema document..."); + if (document == null || document.length == 0) { + String message = "Missing metadata schema document in body. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + + IValidator applicableValidator = null; + try { + applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, document); + + if (applicableValidator == null) { + String message = "No validator found for schema type " + schemaRecord.getType() + ". Returning HTTP UNPROCESSABLE_ENTITY."; + LOG.error(message); + throw new UnprocessableEntityException(message); + } else { + LOG.trace("Validator found. Checking provided schema file."); + LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); + try (InputStream inputStream = new ByteArrayInputStream(document)) { + if (!applicableValidator.isSchemaValid(inputStream)) { + String message = "Metadata schema document validation failed. Returning HTTP UNPROCESSABLE_ENTITY."; + LOG.warn(message); + if (LOG.isTraceEnabled()) { + LOG.trace("Schema: \n'{}'", new String(document, StandardCharsets.UTF_8)); + } + throw new UnprocessableEntityException(message); + } + } + } + } catch (IOException ex) { + String message = LOG_ERROR_READ_METADATA_DOCUMENT; + LOG.error(message, ex); + throw new UnprocessableEntityException(message); + } + + LOG.trace("Schema document is valid!"); + } + + private static IValidator getValidatorForRecord(MetastoreConfiguration metastoreProperties, SchemaRecord schemaRecord, byte[] schemaDocument) { + IValidator applicableValidator = null; + //obtain/guess record type + if (schemaRecord.getType() == null) { + schemaRecord.setType(SchemaUtils.guessType(schemaDocument)); + if (schemaRecord.getType() == null) { + String message = "Unable to detect schema type automatically. Please provide a valid type"; + LOG.error(message); + throw new UnprocessableEntityException(message); + } else { + LOG.debug("Automatically detected schema type {}.", schemaRecord.getType()); + } + } + for (IValidator validator : metastoreProperties.getValidators()) { + if (validator.supportsSchemaType(schemaRecord.getType())) { + applicableValidator = validator.getInstance(); + LOG.trace("Found validator for schema: '{}'", schemaRecord.getType().name()); + break; + } + } + return applicableValidator; + } + + private static void saveNewSchemaRecord(SchemaRecord schemaRecord) { + if (schemaRecordDao != null) { + try { + schemaRecordDao.save(schemaRecord); + } catch (Exception npe) { + LOG.error("Can't save schema record: " + schemaRecord, npe); + } + LOG.trace("Schema record saved: {}", schemaRecord); + } + } + + private static DataResource checkParameters(MultipartFile dataResourceRecord, MultipartFile document, boolean bothRequired) { + boolean recordAvailable; + boolean documentAvailable; + DataResource metadataRecord = null; + + recordAvailable = dataResourceRecord == null || dataResourceRecord.isEmpty(); + documentAvailable = document == null || document.isEmpty(); + String message = null; + if (bothRequired) { + if (!recordAvailable || !documentAvailable) { + message = "No data resource record and/or metadata document provided. Returning HTTP BAD_REQUEST."; + } + } else { + if (!(recordAvailable || documentAvailable)) { + message = "Neither metadata record nor metadata document provided."; + } + } + if (message != null) { + LOG.error(message); + throw new BadArgumentException(message); + } + // Do some checks first. + if (!recordAvailable) { + try { + metadataRecord = Json.mapper().readValue(dataResourceRecord.getInputStream(), DataResource.class); + } catch (IOException ex) { + message = "Can't map record document to MetadataRecord"; + if (ex instanceof JsonParseException) { + message = message + " Reason: " + ex.getMessage(); + } + LOG.error(ERROR_PARSING_JSON, ex); + throw new BadArgumentException(message); + } + } + return metadataRecord; + } + + /** + * Validate metadata document with given schema. In case of an error a runtime + * exception is thrown. + * + * @param metastoreProperties Configuration properties. + * @param document Document to validate. + * @param schemaId SchemaId of schema. + * @param version Version of the document. + */ + public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, + MultipartFile document, + String schemaId, + Long version) { + LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); + if (schemaId == null) { + String message = "Missing schemaID. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + + long nano1 = System.nanoTime() / 1000000; + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(schemaId); + SchemaRecord schemaRecord = getSchemaRecord(resourceIdentifier, version); + long nano2 = System.nanoTime() / 1000000; + validateMetadataDocument(metastoreProperties, document, schemaRecord); + long nano3 = System.nanoTime() / 1000000; + LOG.info("Validate document(schemaId,version), {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1); + + cleanUp(schemaRecord); + } + + /** + * Validate metadata document with given schema. In case of an error a runtime + * exception is thrown. + * + * @param metastoreProperties Configuration properties. + * @param document Document to validate. + * @param schemaRecord Record of the schema. + */ + public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, + MultipartFile document, + SchemaRecord schemaRecord) { + LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaRecord, document); + + if (document == null || document.isEmpty()) { + String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + try { + try (InputStream inputStream = document.getInputStream()) { + validateMetadataDocument(metastoreProperties, inputStream, schemaRecord); + } + } catch (IOException ex) { + String message = LOG_ERROR_READ_METADATA_DOCUMENT; + LOG.error(message, ex); + throw new UnprocessableEntityException(message); + } + } + + /** + * Validate metadata document with given schema. In case of an error a runtime + * exception is thrown. + * + * @param metastoreProperties Configuration properties. + * @param inputStream Document to validate. + * @param schemaRecord Record of the schema. + */ + public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, + InputStream inputStream, + SchemaRecord schemaRecord) throws IOException { + LOG.trace("validateMetadataInputStream {},{}, {}", metastoreProperties, schemaRecord, inputStream); + + long nano1 = System.nanoTime() / 1000000; + if (schemaRecord == null || schemaRecord.getSchemaDocumentUri() == null || schemaRecord.getSchemaDocumentUri().trim().isEmpty()) { + String message = "Missing or invalid schema record. Returning HTTP BAD_REQUEST."; + LOG.error(message + " -> '{}'", schemaRecord); + throw new BadArgumentException(message); + } + long nano2 = System.nanoTime() / 1000000; + LOG.trace("Checking local schema file."); + Path schemaDocumentPath = Paths.get(URI.create(schemaRecord.getSchemaDocumentUri())); + + if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { + LOG.error("Schema document with schemaId '{}'at path {} either does not exist or is no file or is not readable.", schemaRecord.getSchemaId(), schemaDocumentPath); + throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); + } + LOG.trace("obtain validator for type"); + IValidator applicableValidator; + if (schemaRecord.getType() == null) { + byte[] schemaDocument = FileUtils.readFileToByteArray(schemaDocumentPath.toFile()); + applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, schemaDocument); + } else { + applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, null); + } + long nano3 = System.nanoTime() / 1000000; + + if (applicableValidator == null) { + String message = "No validator found for schema type " + schemaRecord.getType(); + LOG.error(message); + throw new UnprocessableEntityException(message); + } else { + LOG.trace("Validator found."); + + LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); + long nano4 = System.nanoTime() / 1000000; + if (!applicableValidator.validateMetadataDocument(schemaDocumentPath.toFile(), inputStream)) { + LOG.warn("Metadata document validation failed. -> " + applicableValidator.getErrorMessage()); + throw new UnprocessableEntityException(applicableValidator.getErrorMessage()); + } + long nano5 = System.nanoTime() / 1000000; + LOG.info("Validate document(schemaRecord), {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1); + } + LOG.trace("Metadata document validation succeeded."); + } + + /** + * Gets SchemaRecord from identifier. Afterwards there should be a clean up. + * + * @see #cleanUp(edu.kit.datamanager.metastore2.domain.ResourceIdentifier, + * edu.kit.datamanager.metastore2.domain.SchemaRecord) + * + * @param identifier ResourceIdentifier of type INTERNAL or URL. + * @param version Version (may be null) + * @return schema record. + */ + public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long version) { + LOG.trace("getSchemaRecord {},{}", identifier, version); + SchemaRecord schemaRecord; + if (identifier == null || identifier.getIdentifierType() == null) { + String message = "Missing resource identifier for schema. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + switch (identifier.getIdentifierType()) { + case INTERNAL -> { + String schemaId = identifier.getIdentifier(); + if (schemaId == null) { + String message = "Missing schemaID. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + if (version != null) { + schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + } else { + schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(schemaId); + } + } + case URL -> { + schemaRecord = prepareResourceFromUrl(identifier, version); + } + default -> throw new BadArgumentException("For schema document identifier type '" + identifier.getIdentifierType() + "' is not allowed!"); + } + if (schemaRecord != null) { + LOG.trace("getSchemaRecord {},{}", schemaRecord.getSchemaDocumentUri(), schemaRecord.getVersion()); + } else { + LOG.trace("No matching schema record found!"); + } + return schemaRecord; + } + + private static SchemaRecord prepareResourceFromUrl(ResourceIdentifier identifier, Long version) { + String url = identifier.getIdentifier(); + Path pathToFile; + MetadataSchemaRecord.SCHEMA_TYPE type = null; + Optional findByUrl = url2PathDao.findByUrl(url); + if (findByUrl.isPresent()) { + url = findByUrl.get().getPath(); + type = findByUrl.get().getType(); + pathToFile = Paths.get(URI.create(url)); + } else { + URI resourceUrl; + try { + resourceUrl = new URI(url); + } catch (URISyntaxException ex) { + String message = String.format("Invalid URL: '%s'", url); + LOG.error(message, ex); + throw new BadArgumentException(message); + } + Optional path = DownloadUtil.downloadResource(resourceUrl); + pathToFile = path.get(); + } + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaDocumentUri(pathToFile.toUri().toString()); + schemaRecord.setType(type); + return schemaRecord; + } + /** + * Remove all downloaded files for schema Record. + * + * @param schemaRecord Schema record. + */ + public static void cleanUp(SchemaRecord schemaRecord) { + LOG.trace("Clean up {}", schemaRecord); + if (schemaRecord == null || schemaRecord.getSchemaDocumentUri() == null) { + String message = "Missing resource locator for schema."; + LOG.error(message); + } else { + String pathToSchemaDocument = fixRelativeURI(schemaRecord.getSchemaDocumentUri()); + List findByUrl = url2PathDao.findByPath(pathToSchemaDocument); + if (findByUrl.isEmpty()) { + if (LOG.isTraceEnabled()) { + LOG.trace(LOG_SEPARATOR); + Page page = url2PathDao.findAll(PageRequest.of(0, 100)); + LOG.trace("List '{}' of '{}'", page.getSize(), page.getTotalElements()); + LOG.trace(LOG_SEPARATOR); + page.getContent().forEach(item -> LOG.trace("- {}", item)); + LOG.trace(LOG_SEPARATOR); + } + // Remove downloaded file + String uri = schemaRecord.getSchemaDocumentUri(); + Path pathToFile = Paths.get(URI.create(uri)); + DownloadUtil.removeFile(pathToFile); + } + } + } + + /** + * Set the DAO holding url and paths. + * + * @param aUrl2PathDao the url2PathDao to set + */ + public static void setUrl2PathDao(IUrl2PathDao aUrl2PathDao) { + url2PathDao = aUrl2PathDao; + } + + /** + * Update schema document. + * + * @param applicationProperties Settings of repository. + * @param resourceId ID of the schema document. + * @param eTag E-Tag of the current schema document. + * @param recordDocument Record of the schema. + * @param schemaDocument Schema document. + * @param supplier Method for creating access URL. + * @return Record of updated schema document. + */ + public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration applicationProperties, + String resourceId, + String eTag, + MultipartFile recordDocument, + MultipartFile schemaDocument, + UnaryOperator supplier) { + DataResource metadataRecord; + metadataRecord = checkParameters(recordDocument, schemaDocument, false); + + LOG.trace("Obtaining most recent metadata schema record with id {}.", resourceId); + DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); + LOG.trace("Checking provided ETag."); + ControllerUtils.checkEtag(eTag, dataResource); + SchemaRecord schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(dataResource.getId()); + if (metadataRecord != null) { + metadataRecord.setVersion(Long.toString(schemaRecord.getVersion())); + existingRecord = mergeRecords(existingRecord, metadataRecord); + mergeSchemaRecord(schemaRecord, existingRecord); + dataResource = migrateToDataResource(applicationProperties, existingRecord); + } else { + dataResource = DataResourceUtils.copyDataResource(dataResource); + } + + if (schemaDocument != null) { + // Get schema record for this schema + validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDocument); + + ContentInformation info; + info = getContentInformationOfResource(applicationProperties, dataResource); + + boolean noChanges = false; + String fileName = schemaDocument.getOriginalFilename(); + if (info != null) { + noChanges = true; + fileName = info.getRelativePath(); + // Check for changes... + try { + byte[] currentFileContent; + File file = new File(URI.create(info.getContentUri())); + if (schemaDocument.getSize() == Files.size(file.toPath())) { + currentFileContent = FileUtils.readFileToByteArray(file); + byte[] newFileContent = schemaDocument.getBytes(); + for (int index = 0; index < currentFileContent.length; index++) { + if (currentFileContent[index] != newFileContent[index]) { + noChanges = false; + break; + } + } + } else { + noChanges = false; + } + } catch (IOException ex) { + LOG.error("Error reading current file!", ex); + throw new BadArgumentException("Error reading schema document!"); + } + } + if (!noChanges) { + // Everything seems to be fine update document and increment version + LOG.trace("Updating schema document (and increment version)..."); + String version = dataResource.getVersion(); + if (version != null) { + dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + } + ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); + } else { + schemaRecordDao.delete(schemaRecord); + } + } else { + schemaRecordDao.delete(schemaRecord); + // validate if document is still valid due to changed record settings. + metadataRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); + URI schemaDocumentUri = URI.create(metadataRecord.getSchemaDocumentUri()); + + Path schemaDocumentPath = Paths.get(schemaDocumentUri); + if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { + LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); + throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); + } + + try { + byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); + MetadataSchemaRecordUtil.validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDoc); + } catch (IOException ex) { + LOG.error("Error validating file!", ex); + } + + } + dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); + + return migrateToMetadataSchemaRecord(applicationProperties, dataResource, true); + } + + +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java index f4b674ec..2dc2966d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java @@ -16,7 +16,6 @@ package edu.kit.datamanager.metastore2.web; import edu.kit.datamanager.metastore2.domain.AclRecord; -import edu.kit.datamanager.metastore2.domain.DataResource; import edu.kit.datamanager.repo.domain.DataResource; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController_v2.java new file mode 100644 index 00000000..e39d3115 --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController_v2.java @@ -0,0 +1,184 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web; + +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.repo.domain.DataResource; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.ArraySchema; +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 jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.time.Instant; +import java.util.List; +import org.springdoc.core.converters.models.PageableAsQueryParam; +import org.springframework.boot.actuate.info.InfoContributor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Interface for schema document controller. + * + * @author jejkal + */ +@ApiResponses(value = { + @ApiResponse(responseCode = "401", description = "Unauthorized is returned if authorization in required but was not provided."), + @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) +public interface ISchemaRegistryController_v2 extends InfoContributor { + + @Operation(summary = "Register a schema document and its record.", description = "This endpoint allows to register a schema document and its record. " + + "The record must contain at least an unique identifier (schemaId) and the type of the schema (type).", + responses = { + @ApiResponse(responseCode = "201", description = "Created is returned only if the record has been validated, persisted and the document was successfully validated and stored.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), + @ApiResponse(responseCode = "400", description = "Bad Request is returned if the provided metadata record is invalid or if the validation of the provided schema failed."), + @ApiResponse(responseCode = "409", description = "A Conflict is returned, if there is already a record for the provided schema id.")}) + @RequestMapping(value = {"", "/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) + @ResponseBody + public ResponseEntity createRecord( + @Parameter(description = "Json representation of the schema record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile schemaRecord, + @Parameter(description = "The metadata schema document associated with the record.", required = true) @RequestPart(name = "schema", required = true) final MultipartFile document, + final HttpServletRequest request, + final HttpServletResponse response, + final UriComponentsBuilder uriBuilder); + + @Operation(summary = "Get schema record by schema id (and version).", description = "Obtain is single schema record by its schema id. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + + "Furthermore, a specific version of the record can be returned by providing a version number as request parameter. If no version is specified, the most recent version is returned.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.schema-record+json"}) + @ResponseBody + public ResponseEntity getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); + + @Operation(summary = "Get landing page of schema by schema id (and version).", description = "Show landing page by its schema id. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + + "Furthermore, a specific version of the schema can be returned by providing a version number as request parameter. If no version is specified, all versions will be returned.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the landingpage is returned if the id exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"text/html"}) + public ModelAndView getLandingPageById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); + + @Operation(summary = "Validate a metadata document.", description = "Validate the provided metadata document using the addressed schema. If all parameters" + + " are provided, the schema is identified uniquely by schemaId and version. If the version is omitted, the most recent version of the " + + "schema is used. This endpoint returns HTTP NO_CONTENT if it succeeds. Otherwise, an error response is returned, e.g. HTTP UNPROCESSABLE_ENTITY (422) if validation fails.", + responses = { + @ApiResponse(responseCode = "204", description = "No Content if validate succeeded."), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no schema for the provided schemaId and version was found."), + @ApiResponse(responseCode = "422", description = "Unprocessable Entity if validation fails.") + }) + @RequestMapping(value = {"/{schemaId}/validate"}, method = {RequestMethod.POST}, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) + @ResponseBody + public ResponseEntity validate(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + @Parameter(description = "The metadata file to validate against the addressed schema.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, + WebRequest wr, + HttpServletResponse hsr); + + @Operation(summary = "Get a schema document by schema id.", description = "Obtain a single schema document identified by its schema id. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + + "Furthermore, a specific version of the schema document can be returned by providing a version number as request parameter. If no version is specified, the most recent version is returned.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the schema document is returned if the record exists and the user has sufficient permission."), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/json", "application/xml"}) + @ResponseBody + public ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); + + @Operation(summary = "Get all schema records.", description = "List all schema records in a paginated and/or sorted form. The result can be refined by providing schemaId, a list of one or more mimetypes and/or a date range. Returned schema record(s) must match. " + + "if 'schemaId' is provided all other parameters were skipped and all versions of the given schemaId record will be returned. " + + "If 'mimetype' is provided, a record matches if its associated mime type matchs. " + + "Furthermore, the UTC time of the last update can be provided in three different fashions: 1) Providing only updateFrom returns all records updated at or after the provided date, 2) Providing only updateUntil returns all records updated before or " + + "at the provided date, 3) Providing both returns all records updated within the provided date range. " + + "If no parameters are provided, all accessible records are listed. With regard to schema versions, only the most recent version of each schema is listed.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and a list of records or an empty list of no record matches.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = MetadataSchemaRecord.class))))}) + @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) + @ResponseBody + @PageableAsQueryParam + public ResponseEntity> getRecords( + @Parameter(description = "SchemaId", required = false) @RequestParam(value = "schemaId", required = false) String schemaId, + @Parameter(description = "A list of mime types returned schemas are associated with.", required = false) @RequestParam(value = "mimeType", required = false) List mimeTypes, + @Parameter(description = "The UTC time of the earliest update of a returned record.", required = false) @RequestParam(name = "from", required = false) Instant updateFrom, + @Parameter(description = "The UTC time of the latest update of a returned record.", required = false) @RequestParam(name = "until", required = false) Instant updateUntil, + @Parameter(hidden = true) @PageableDefault(sort = {"id"}, direction = Sort.Direction.ASC) Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb); + + @Operation(summary = "Update a schema record.", description = "Apply an update to the schema record and/or schema document with the provided schema id. " + + "The update capabilities for a schema record are quite limited. An update is always related to the most recent version. " + + "Only the associated mimeType and acl can be changed. All other fields are updated automatically or are read-only. Updating only the metadata record does not affect the version number. " + + "A new version is only created while providing a (new) schema document.", + responses = { + @ApiResponse(responseCode = "200", description = "OK is returned in case of a successful update. " + + "The updated record is returned in the response.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), + @ApiResponse(responseCode = "400", description = "Bad Request is returned if the provided schema record/schema document is invalid."), + @ApiResponse(responseCode = "404", description = "Not Found is returned if no record for the provided id was found.")}) + @RequestMapping(value = "/{schemaId}", method = RequestMethod.PUT, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) + @Parameters({ + @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) + }) + ResponseEntity updateRecord( + @Parameter(description = "The schema id.", required = true) @PathVariable("schemaId") final String schemaId, + @Parameter(description = "Json representation of the schema record.", required = false) @RequestPart(name = "record", required = false) final MultipartFile schemaRecord, + @Parameter(description = "The metadata schema document associated with the record.", required = false) @RequestPart(name = "schema", required = false) final MultipartFile document, + final WebRequest request, + final HttpServletResponse response + ); + + @Operation(summary = "Delete a schema record.", description = "Delete a single schema record. " + + "Deleting a record typically requires the caller to have special permissions. " + + "In some cases, deleting a record can also be available for the owner or other privileged users or can be forbidden at all. Deletion of a record affects all versions of the particular record.", + responses = { + @ApiResponse(responseCode = "204", description = "No Content is returned as long as no error occurs while deleting a record. Multiple delete operations to the same record will also return HTTP 204 even if the deletion succeeded in the first call.")}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.DELETE}) + @Parameters({ + @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) + }) + @ResponseBody + public ResponseEntity deleteRecord(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, @Header(name = "ETag", required = true) WebRequest wr, HttpServletResponse hsr); +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java index 749cb51f..f16d3116 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java @@ -25,13 +25,14 @@ import edu.kit.datamanager.exceptions.UnprocessableEntityException; import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; -import edu.kit.datamanager.metastore2.dao.ILinkedDataResourceDao; +import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.domain.AclRecord; -import edu.kit.datamanager.metastore2.domain.LinkedDataResource; +import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.util.ActuatorUtil; -import edu.kit.datamanager.metastore2.util.DataResourceUtil; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.IMetadataController_v2; import edu.kit.datamanager.repo.dao.IDataResourceDao; @@ -41,6 +42,7 @@ import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.service.IMessagingService; import edu.kit.datamanager.service.impl.LogfileMessagingService; @@ -110,7 +112,7 @@ public class MetadataControllerImpl_v2 implements IMetadataController_v2 { private final ApplicationProperties applicationProperties; - private final ILinkedDataResourceDao metadataRecordDao; + private final ILinkedMetadataRecordDao metadataRecordDao; private final MetastoreConfiguration metadataConfig; @@ -136,7 +138,7 @@ public class MetadataControllerImpl_v2 implements IMetadataController_v2 { */ public MetadataControllerImpl_v2(ApplicationProperties applicationProperties, MetastoreConfiguration metadataConfig, - ILinkedDataResourceDao metadataRecordDao, + ILinkedMetadataRecordDao metadataRecordDao, IDataResourceDao dataResourceDao) { this.applicationProperties = applicationProperties; this.metadataConfig = metadataConfig; @@ -151,7 +153,7 @@ public MetadataControllerImpl_v2(ApplicationProperties applicationProperties, addSimpleClaim("loginFailures", 0). addSimpleClaim("active", true). addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); - DataResourceUtil.setToken(guestToken); + MetadataRecordUtil.setToken(guestToken); } @Override @@ -166,14 +168,14 @@ public ResponseEntity createRecord( LOG.trace("Performing createRecord({},...).", recordDocument); DataResource metadataRecord; if (recordDocument == null || recordDocument.isEmpty()) { - String message = "No metadata record provided. Returning HTTP BAD_REQUEST."; + String message = "No data resource record provided. Returning HTTP BAD_REQUEST."; LOG.error(message); throw new BadArgumentException(message); } try { metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); } catch (IOException ex) { - String message = "No valid metadata record provided. Returning HTTP BAD_REQUEST."; + String message = "No valid data resource record provided. Returning HTTP BAD_REQUEST."; if (ex instanceof JsonParseException) { message = message + " Reason: " + ex.getMessage(); } @@ -182,15 +184,12 @@ public ResponseEntity createRecord( } long nano2 = System.nanoTime() / 1000000; - if (metadataRecord.getRelatedResource() == null || metadataRecord.getRelatedResource().getIdentifier() == null || metadataRecord.getSchema() == null || metadataRecord.getSchema().getIdentifier() == null) { - LOG.error("Mandatory attributes relatedResource and/or schemaId not found in record. Returning HTTP BAD_REQUEST."); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Mandatory attributes relatedResource and/or schemaId not found in record."); - } + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(metadataRecord); LOG.debug("Test for existing metadata record for given schema and resource"); - ResourceIdentifier schemaIdentifier; + RelatedIdentifier schemaIdentifier; try { - schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(metadataConfig, metadataRecord); + schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); } catch (ResourceNotFoundException rnfe) { LOG.debug("Error checking for existing relations.", rnfe); throw new UnprocessableEntityException("Schema ID seems to be invalid"); @@ -206,13 +205,13 @@ public ResponseEntity createRecord( LOG.error(message); return ResponseEntity.status(HttpStatus.CONFLICT).body(message); } - DataResource result = DataResourceUtil.createDataResource(metadataConfig, recordDocument, document); + DataResource result = MetadataRecordUtil.createDataResource(metadataConfig, recordDocument, document); // Successfully created metadata record. long nano4 = System.nanoTime() / 1000000; LOG.trace("Metadata record successfully persisted. Returning result."); - DataResourceUtil.fixMetadataDocumentUri(result); + MetadataRecordUtil.fixMetadataDocumentUri(result); long nano5 = System.nanoTime() / 1000000; - metadataRecordDao.save(new LinkedDataResource(result)); + metadataRecordDao.save(new LinkedMetadataRecord(result)); long nano6 = System.nanoTime() / 1000000; URI locationUri; @@ -237,12 +236,12 @@ public ResponseEntity getRecordById( LOG.trace("Performing getRecordById({}, {}).", id, version); LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); - DataResource metadataRecord = DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); + DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); LOG.trace("Metadata record found. Prepare response."); //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN LOG.trace("Get ETag of DataResource."); String etag = metadataRecord.getEtag(); - DataResourceUtil.fixMetadataDocumentUri(metadataRecord); + MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); } @@ -259,8 +258,8 @@ public ResponseEntity getAclById( throw new AccessForbiddenException("Only for services!"); } - DataResource metadataRecord = DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); - DataResourceUtil.fixMetadataDocumentUri(metadataRecord); + DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); + MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); AclRecord aclRecord = new AclRecord(); aclRecord.setAcl(metadataRecord.getAcl()); aclRecord.setDataResource(metadataRecord); @@ -277,7 +276,7 @@ public ResponseEntity getMetadataDocumentById( ) { LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); - Path metadataDocumentPath = DataResourceUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); + Path metadataDocumentPath = MetadataRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); return ResponseEntity. ok(). @@ -314,17 +313,17 @@ public ResponseEntity> getAllVersions( //if security is enabled, include principal in query LOG.debug("Performing query for records."); - DataResource recordByIdAndVersion = DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id); + DataResource recordByIdAndVersion = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id); List recordList = new ArrayList<>(); long totalNoOfElements = recordByIdAndVersion.getRecordVersion(); for (long version = totalNoOfElements - pgbl.getOffset(), size = 0; version > 0 && size < pgbl.getPageSize(); version--, size++) { - recordList.add(DataResourceUtil.getRecordByIdAndVersion(metadataConfig, id, version)); + recordList.add(MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version)); } LOG.trace("Transforming Dataresource to DataResource"); List metadataList = new ArrayList<>(); recordList.forEach(metadataRecord -> { - DataResourceUtil.fixMetadataDocumentUri(metadataRecord); + MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); metadataList.add(metadataRecord); }); @@ -375,7 +374,7 @@ public ResponseEntity> getRecords( for (String schemaId : schemaIds) { MetadataSchemaRecord currentSchemaRecord; try { - currentSchemaRecord = DataResourceUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); + currentSchemaRecord = MetadataRecordUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); // Test for internal URI -> Transform to global URI. if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); @@ -389,7 +388,7 @@ public ResponseEntity> getRecords( allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); } for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { - MetadataSchemaRecord schemaRecord = DataResourceUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); + MetadataSchemaRecord schemaRecord = MetadataRecordUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); // Test for internal URI -> Transform to global URI. if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); @@ -432,8 +431,8 @@ public ResponseEntity> getRecords( List recordList = records.getContent(); List metadataList = new ArrayList<>(); recordList.forEach(metadataRecord -> { - DataResource item = DataResourceUtil.migrateToDataResource(metadataConfig, metadataRecord, false); - DataResourceUtil.fixMetadataDocumentUri(item); + DataResource item = MetadataRecordUtil.migrateToDataResource(metadataConfig, metadataRecord, false); + MetadataRecordUtil.fixMetadataDocumentUri(item); metadataList.add(item); }); @@ -455,11 +454,11 @@ public ResponseEntity updateRecord( UnaryOperator getById; getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); String eTag = ControllerUtils.getEtagFromHeader(request); - DataResource updateDataResource = DataResourceUtil.updateDataResource(metadataConfig, id, eTag, metadataRecord, document, getById); + DataResource updateDataResource = MetadataRecordUtil.updateDataResource(metadataConfig, id, eTag, metadataRecord, document, getById); LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); String etag = updateDataResource.getEtag(); - DataResourceUtil.fixMetadataDocumentUri(updateDataResource); + MetadataRecordUtil.fixMetadataDocumentUri(updateDataResource); URI locationUri; locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(updateDataResource.getId(), updateDataResource.getRecordVersion(), null, null)).toUri(); @@ -482,7 +481,7 @@ public ResponseEntity deleteRecord( getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, wr, hsr)).toString(); String eTag = ControllerUtils.getEtagFromHeader(wr); - DataResourceUtil.deleteDataResource(metadataConfig, id, eTag, getById); + MetadataRecordUtil.deleteDataResource(metadataConfig, id, eTag, getById); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @@ -495,7 +494,7 @@ public void contribute(Info.Builder builder) { Map details = ActuatorUtil.testDirectory(basePath); if (!details.isEmpty()) { - details.put("No of metadata documents", Long.toString(DataResourceUtil.getNoOfDocuments())); + details.put("No of metadata documents", Long.toString(MetadataRecordUtil.getNoOfDocuments())); builder.withDetail("metadataRepo", details); } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl_v2.java new file mode 100644 index 00000000..f390c2df --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl_v2.java @@ -0,0 +1,362 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web.impl; + +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.exceptions.ResourceNotFoundException; +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.util.ActuatorUtil; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; +import edu.kit.datamanager.metastore2.web.ISchemaRegistryController; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.TitleSpec; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.util.AuthenticationHelper; +import edu.kit.datamanager.util.ControllerUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.actuate.info.Info; +import org.springframework.core.io.FileSystemResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Controller for schema documents. + */ +@Controller +@RequestMapping(value = "/api/v2/schemas") +@Tag(name = "Schema Registry") +@Schema(description = "Schema Registry") +public class SchemaRegistryControllerImpl_v2 implements ISchemaRegistryController { + + private static final Logger LOG = LoggerFactory.getLogger(SchemaRegistryControllerImpl_v2.class); + + private final ApplicationProperties applicationProperties; + + private final MetastoreConfiguration schemaConfig; + + private final IDataResourceDao dataResourceDao; + + /** + * Constructor for schema documents controller. + * + * @param applicationProperties Configuration for controller. + * @param schemaConfig Configuration for metadata documents repository. + * @param dataResourceDao DAO for data resources. + */ + public SchemaRegistryControllerImpl_v2(ApplicationProperties applicationProperties, + MetastoreConfiguration schemaConfig, + IDataResourceDao dataResourceDao) { + this.applicationProperties = applicationProperties; + this.schemaConfig = schemaConfig; + this.dataResourceDao = dataResourceDao; + LOG.info("------------------------------------------------------"); + LOG.info("------{}", schemaConfig); + LOG.info("------------------------------------------------------"); + } + + @Override + public ResponseEntity createRecord( + @RequestPart(name = "record") final MultipartFile recordDocument, + @RequestPart(name = "schema") MultipartFile document, + HttpServletRequest request, + HttpServletResponse response, + UriComponentsBuilder uriBuilder) { + LOG.trace("Performing createRecord({},....", recordDocument); + BiFunction getSchemaDocumentById; + getSchemaDocumentById = (schema, version) -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getSchemaDocumentById(schema, version, null, null)).toString(); + + DataResource dataResourceRecord = DataResourceRecordUtil.createDataResourceRecord4Schema(schemaConfig, recordDocument, document, getSchemaDocumentById); + LOG.trace("Schema record successfully persisted. Returning result."); + String etag = dataResourceRecord.getEtag(); + + LOG.trace("Schema record successfully persisted."); + URI locationUri; + locationUri = SchemaRegistryControllerImpl_v2.getSchemaDocumentUri(dataResourceRecord); + LOG.warn("location uri " + locationUri); + return ResponseEntity.created(locationUri).eTag("\"" + etag + "\"").body(dataResourceRecord); + } + + @Override + public ResponseEntity getRecordById( + @PathVariable(value = "schemaId") String schemaId, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr) { + LOG.trace("Performing getRecordById({}, {}).", schemaId, version); + + LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); + DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); + String etag = schemaRecord.getEtag(); + + LOG.trace("Returning result."); + return ResponseEntity.ok().eTag("\"" + etag + "\"").body(schemaRecord); + } + + @Override + public ModelAndView getLandingPageById(@PathVariable(value = "schemaId") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr) { + LOG.trace("Performing Landing page for schema document with ({}, {}).", id, version); + String redirectUrl = applicationProperties.getSchemaLandingPage(); + redirectUrl = redirectUrl.replace(MetadataControllerImpl.PLACEHOLDER_ID, id); + String versionString = ""; + if (version != null) { + versionString = version.toString(); + } + redirectUrl = "redirect:" + redirectUrl.replace(MetadataControllerImpl.PLACEHOLDER_VERSION, versionString); + + LOG.trace("Redirect to '{}'", redirectUrl); + + return new ModelAndView(redirectUrl); + } + + @Override + public ResponseEntity getSchemaDocumentById( + @PathVariable(value = "schemaId") String schemaId, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr) { + LOG.trace("Performing getSchemaDocumentById({}, {}).", schemaId, version); + + LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); + MetadataSchemaRecord schemaRecord = MetadataSchemaRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); + URI schemaDocumentUri = URI.create(schemaRecord.getSchemaDocumentUri()); + + MediaType contentType = MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType()) ? MediaType.APPLICATION_XML : MediaType.APPLICATION_JSON; + Path schemaDocumentPath = Paths.get(schemaDocumentUri); + if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { + LOG.trace("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Schema document on server either does not exist or is no file or is not readable."); + } + + return ResponseEntity. + ok(). + contentType(contentType). + header(HttpHeaders.CONTENT_LENGTH, String.valueOf(schemaDocumentPath.toFile().length())). + body(new FileSystemResource(schemaDocumentPath.toFile())); + } + + public ResponseEntity> getAllVersions( + String id, + Pageable pgbl + ) { + LOG.trace("Performing getAllVersions({}).", id); + // Search for resource type of MetadataSchemaRecord + + //if security is enabled, include principal in query + LOG.debug("Performing query for records."); + DataResource recordByIdAndVersion; + List recordList = new ArrayList<>(); + long totalNoOfElements = 5; + try { + recordByIdAndVersion = DataResourceRecordUtil.getRecordById(schemaConfig, id); + totalNoOfElements = Long.parseLong(recordByIdAndVersion.getVersion()); + for (long version = totalNoOfElements - pgbl.getOffset(), size = 0; version > 0 && size < pgbl.getPageSize(); version--, size++) { + recordList.add(DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version)); + } + } catch (ResourceNotFoundException rnfe) { + LOG.info("Schema ID '{}' is unkown. Return empty list...", id); + } + + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); + + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); + } + + @Override + public ResponseEntity validate(@PathVariable(value = "schemaId") String schemaId, + @RequestParam(value = "version", required = false) Long version, + MultipartFile document, + WebRequest wr, + HttpServletResponse hsr) { + LOG.trace("Performing validate({}, {}, {}).", schemaId, version, "#document"); + DataResourceRecordUtil.validateMetadataDocument(schemaConfig, document, schemaId, version); + LOG.trace("Metadata document validation succeeded. Returning HTTP NOT_CONTENT."); + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } + + @Override + public ResponseEntity> getRecords(@RequestParam(value = "schemaId", required = false) String schemaId, + @RequestParam(value = "mimeType", required = false) List mimeTypes, + @RequestParam(name = "from", required = false) Instant updateFrom, + @RequestParam(name = "until", required = false) Instant updateUntil, + Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb) { + LOG.trace("Performing getRecords({}, {}, {}, {}).", schemaId, mimeTypes, updateFrom, updateUntil); + // if schemaId is given return all versions + if (schemaId != null) { + return getAllVersions(schemaId, pgbl); + } + // Search for resource type of MetadataSchemaRecord + Specification spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE)); + // Add authentication if enabled + spec = addAuthenticationSpecification(spec); + //one of given mimetypes. + if ((mimeTypes != null) && !mimeTypes.isEmpty()) { + spec = spec.and(TitleSpec.toSpecification(mimeTypes.toArray(new String[mimeTypes.size()]))); + } + if ((updateFrom != null) || (updateUntil != null)) { + spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); + } + // Hide revoked and gone data resources. + DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; + List stateList = Arrays.asList(states); + spec = spec.and(StateSpecification.toSpecification(stateList)); + + LOG.debug("Performing query for records."); + Page records = null; + try { + records = dataResourceDao.findAll(spec, pgbl); + } catch (Exception ex) { + LOG.error("Error find metadata records by specification!", ex); + throw ex; + } + List recordList = records.getContent(); + if (LOG.isTraceEnabled()) { + LOG.trace("Cleaning up schemaDocumentUri of query result."); + for (DataResource item : recordList) { + LOG.trace("---> " + item.toString()); + } + } + + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); + + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); + } + + @Override + public ResponseEntity updateRecord(@PathVariable("schemaId") final String schemaId, + @RequestPart(name = "record", required = false) MultipartFile schemaRecord, + @RequestPart(name = "schema", required = false) final MultipartFile document, + final WebRequest request, final HttpServletResponse response) { + LOG.trace("Performing updateRecord({}, {}).", schemaId, schemaRecord); + UnaryOperator getById; + getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); + String eTag = ControllerUtils.getEtagFromHeader(request); + MetadataSchemaRecord updatedSchemaRecord = MetadataSchemaRecordUtil.updateMetadataSchemaRecord(schemaConfig, schemaId, eTag, schemaRecord, document, getById); + + LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); + String etag = updatedSchemaRecord.getEtag(); + MetadataSchemaRecordUtil.fixSchemaDocumentUri(updatedSchemaRecord, true); + // Fix Url for OAI PMH entry + MetadataSchemaRecordUtil.updateMetadataFormat(updatedSchemaRecord); + + URI locationUri; + locationUri = MetadataSchemaRecordUtil.getSchemaDocumentUri(updatedSchemaRecord); + LOG.trace("Set locationUri to '{}'", locationUri.toString()); + return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updatedSchemaRecord); + } + + @Override + public ResponseEntity deleteRecord(@PathVariable("schemaId") final String schemaId, + WebRequest request, + HttpServletResponse hsr) { + LOG.trace("Performing deleteRecord({}).", schemaId); + UnaryOperator getById; + getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, hsr)).toString(); + String eTag = ControllerUtils.getEtagFromHeader(request); + + MetadataSchemaRecordUtil.deleteMetadataSchemaRecord(schemaConfig, schemaId, eTag, getById); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public void contribute(Info.Builder builder) { + LOG.trace("Check for SchemaRepo actuator information..."); + + URL basePath = schemaConfig.getBasepath(); + Map details = ActuatorUtil.testDirectory(basePath); + + if (!details.isEmpty()) { + details.put("No of schema documents", Long.toString(MetadataSchemaRecordUtil.getNoOfSchemas())); + builder.withDetail("schemaRepo", details); + } + } + + private Specification addAuthenticationSpecification(Specification spec) { + if (schemaConfig.isAuthEnabled()) { + boolean isAdmin; + isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); + // Add authorization for non administrators + if (!isAdmin) { + List authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); + if (authorizationIdentities != null) { + LOG.trace("Creating (READ) permission specification."); + Specification permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); + spec = spec.and(permissionSpec); + } else { + LOG.trace("No permission information provided. Skip creating permission specification."); + } + } + } + return spec; + } + + /** + * Get URI for accessing schema document via schemaId and version. + * + * @param dataResourceRecord Record holding schemaId and version. + * @return URI for accessing schema document. + */ + public static final URI getSchemaDocumentUri(DataResource dataResourceRecord) { + return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImpl_v2.class).getSchemaDocumentById(dataResourceRecord.getId(), Long.parseLong(dataResourceRecord.getVersion()), null, null)).toUri(); + } +} From 892892c47795eac2527d07318a0fc073249fd353 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Feb 2024 16:14:51 +0100 Subject: [PATCH 003/181] First compilable version for schemas. --- .../{IMetadataController_v2.java => IMetadataControllerV2.java} | 0 ...egistryController_v2.java => ISchemaRegistryControllerV2.java} | 0 ...ControllerImpl_v2.java => SchemaRegistryControllerImplV2.java} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/main/java/edu/kit/datamanager/metastore2/web/{IMetadataController_v2.java => IMetadataControllerV2.java} (100%) rename src/main/java/edu/kit/datamanager/metastore2/web/{ISchemaRegistryController_v2.java => ISchemaRegistryControllerV2.java} (100%) rename src/main/java/edu/kit/datamanager/metastore2/web/impl/{SchemaRegistryControllerImpl_v2.java => SchemaRegistryControllerImplV2.java} (100%) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java similarity index 100% rename from src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController_v2.java rename to src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java similarity index 100% rename from src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController_v2.java rename to src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java similarity index 100% rename from src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl_v2.java rename to src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java From 29bad23bf4df941ec9b6444d483add49d5434e91 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Feb 2024 16:15:24 +0100 Subject: [PATCH 004/181] First compilable version for schemas. (part 2) --- .../metastore2/dao/ISchemaRecordDao.java | 2 - .../util/DataResourceRecordUtil.java | 649 ++++++----- .../util/MetadataSchemaRecordUtil.java | 2 +- .../metastore2/util/SchemaUtils.java | 30 +- .../metastore2/validation/IValidator.java | 11 + .../validation/impl/JsonValidator.java | 6 + .../validation/impl/XmlValidator.java | 6 + .../metastore2/web/IMetadataController.java | 2 + .../metastore2/web/IMetadataControllerV2.java | 2 +- .../web/ISchemaRegistryController.java | 3 + .../web/ISchemaRegistryControllerV2.java | 2 +- .../web/impl/MetadataControllerImpl_v2.java | 1012 ++++++++--------- .../impl/SchemaRegistryControllerImplV2.java | 15 +- ...erTestAccessWithAuthenticationEnabled.java | 40 + 14 files changed, 961 insertions(+), 821 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java index 7337e000..c847cd74 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java @@ -33,7 +33,5 @@ public interface ISchemaRecordDao extends JpaRepository, J List findBySchemaIdOrderByVersionDesc(String schemaId); - SchemaRecord findTopBySchemaIdOrderByVersionDesc(String schemaId); - SchemaRecord findFirstBySchemaIdOrderByVersionDesc(String schemaId); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 8fbf42ff..c9b17bba 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -45,7 +45,10 @@ import static edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil.fixRelativeURI; import edu.kit.datamanager.metastore2.validation.IValidator; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; +import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; @@ -88,6 +91,8 @@ import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -125,6 +130,7 @@ public class DataResourceRecordUtil { private static String guestToken = null; private static IDataRecordDao dataRecordDao; + private static IDataResourceDao dataResourceDao; private static ISchemaRecordDao schemaRecordDao; private static IMetadataFormatDao metadataFormatDao; @@ -151,7 +157,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio // Do some checks first. metadataRecord = checkParameters(recordDocument, document, true); - + Objects.requireNonNull(metadataRecord); if (metadataRecord.getId() == null) { String message = "Mandatory attribute 'id' not found in record. Returning HTTP BAD_REQUEST."; LOG.error(message); @@ -172,7 +178,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio } // End of parameter checks // validate schema document / determine type if not given - validateMetadataSchemaDocument(applicationProperties, schemaRecord, document); + validateMetadataSchemaDocument(applicationProperties, metadataRecord, document); // set internal parameters if (metadataRecord.getFormats().isEmpty()) { LOG.trace("No mimetype set! Try to determine..."); @@ -202,7 +208,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio schemaRecord.setVersion(applicationProperties.getAuditService().getCurrentVersion(dataResource.getId())); schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); schemaRecord.setDocumentHash(contentInformation.getHash()); - saveNewSchemaRecord(schemaRecord); + MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); // Settings for OAI PMH if (MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType())) { try { @@ -222,89 +228,89 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio return metadataRecord; } - /** - * Create a digital object from metadata record and metadata document. - * - * @param applicationProperties Configuration properties. - * @param recordDocument Metadata record. - * @param document Metadata document. - * @return Enriched metadata record. - */ - public static MetadataRecord createDataResource4MetadataDocument(MetastoreConfiguration applicationProperties, - MultipartFile recordDocument, MultipartFile document) { - DataResource metadataRecord; - long nano1 = System.nanoTime() / 1000000; - // Do some checks first. - metadataRecord = checkParameters(recordDocument, document, true); - - if (metadataRecord.getRelatedResource() == null || metadataRecord.getRelatedResource().getIdentifier() == null || metadataRecord.getSchema() == null || metadataRecord.getSchema().getIdentifier() == null) { - String message = "Mandatory attributes relatedResource and/or schema not found in record. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - // Test for schema version - if (metadataRecord.getSchemaVersion() == null) { - MetadataSchemaRecord currentSchemaRecord; - try { - currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(applicationProperties, metadataRecord.getSchema()); - } catch (ResourceNotFoundException rnfe) { - throw new UnprocessableEntityException("Unknown schema ID '" + metadataRecord.getSchema().getIdentifier() + "'!"); - } - metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); - } - - // validate document - long nano2 = System.nanoTime() / 1000000; - // validate schema document - validateMetadataDocument(applicationProperties, metadataRecord, document); - // set internal parameters - metadataRecord.setRecordVersion(1l); - - long nano3 = System.nanoTime() / 1000000; - // create record. - DataResource dataResource = migrateToDataResource(applicationProperties, metadataRecord); - // add id as internal identifier if exists - // Note: DataResourceUtils.createResource will ignore id of resource. - // id will be set to alternate identifier if exists. - if (dataResource.getId() != null) { - // check for valid identifier without any chars which may be encoded - try { - String originalId = dataResource.getId(); - String value = URLEncoder.encode(originalId, StandardCharsets.UTF_8.toString()); - if (!value.equals(originalId)) { - String message = "Not a valid id! Encoded: " + value; - LOG.error(message); - throw new BadArgumentException(message); - } - } catch (UnsupportedEncodingException ex) { - String message = "Error encoding id " + metadataRecord.getSchemaId(); - LOG.error(message); - throw new CustomInternalServerError(message); - } - - dataResource.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(dataResource.getId())); - } - long nano4 = System.nanoTime() / 1000000; - DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); - long nano5 = System.nanoTime() / 1000000; - // store document - ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); - long nano6 = System.nanoTime() / 1000000; - // Create additional metadata record for faster access - DataRecord dataRecord = new DataRecord(); - dataRecord.setMetadataId(createResource.getId()); - dataRecord.setVersion(metadataRecord.getRecordVersion()); - dataRecord.setSchemaId(metadataRecord.getSchema().getIdentifier()); - dataRecord.setSchemaVersion(metadataRecord.getSchemaVersion()); - dataRecord.setMetadataDocumentUri(contentInformation.getContentUri()); - dataRecord.setDocumentHash(contentInformation.getHash()); - dataRecord.setLastUpdate(dataResource.getLastUpdate()); - saveNewDataRecord(dataRecord); - long nano7 = System.nanoTime() / 1000000; - LOG.info("Create Record times, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); - - return migrateToMetadataRecord(applicationProperties, createResource, true); - } +// /** +// * Create a digital object from metadata record and metadata document. +// * +// * @param applicationProperties Configuration properties. +// * @param recordDocument Metadata record. +// * @param document Metadata document. +// * @return Enriched metadata record. +// */ +// public static MetadataRecord createDataResource4MetadataDocument(MetastoreConfiguration applicationProperties, +// MultipartFile recordDocument, MultipartFile document) { +// DataResource metadataRecord; +// long nano1 = System.nanoTime() / 1000000; +// // Do some checks first. +// metadataRecord = checkParameters(recordDocument, document, true); +// +// if (metadataRecord.getRelatedResource() == null || metadataRecord.getRelatedResource().getIdentifier() == null || metadataRecord.getSchema() == null || metadataRecord.getSchema().getIdentifier() == null) { +// String message = "Mandatory attributes relatedResource and/or schema not found in record. Returning HTTP BAD_REQUEST."; +// LOG.error(message); +// throw new BadArgumentException(message); +// } +// // Test for schema version +// if (metadataRecord.getSchemaVersion() == null) { +// MetadataSchemaRecord currentSchemaRecord; +// try { +// currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(applicationProperties, metadataRecord.getSchema()); +// } catch (ResourceNotFoundException rnfe) { +// throw new UnprocessableEntityException("Unknown schema ID '" + metadataRecord.getSchema().getIdentifier() + "'!"); +// } +// metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); +// } +// +// // validate document +// long nano2 = System.nanoTime() / 1000000; +// // validate schema document +// validateMetadataDocument(applicationProperties, metadataRecord, document); +// // set internal parameters +// metadataRecord.setRecordVersion(1l); +// +// long nano3 = System.nanoTime() / 1000000; +// // create record. +// DataResource dataResource = migrateToDataResource(applicationProperties, metadataRecord); +// // add id as internal identifier if exists +// // Note: DataResourceUtils.createResource will ignore id of resource. +// // id will be set to alternate identifier if exists. +// if (dataResource.getId() != null) { +// // check for valid identifier without any chars which may be encoded +// try { +// String originalId = dataResource.getId(); +// String value = URLEncoder.encode(originalId, StandardCharsets.UTF_8.toString()); +// if (!value.equals(originalId)) { +// String message = "Not a valid id! Encoded: " + value; +// LOG.error(message); +// throw new BadArgumentException(message); +// } +// } catch (UnsupportedEncodingException ex) { +// String message = "Error encoding id " + metadataRecord.getSchemaId(); +// LOG.error(message); +// throw new CustomInternalServerError(message); +// } +// +// dataResource.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(dataResource.getId())); +// } +// long nano4 = System.nanoTime() / 1000000; +// DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); +// long nano5 = System.nanoTime() / 1000000; +// // store document +// ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); +// long nano6 = System.nanoTime() / 1000000; +// // Create additional metadata record for faster access +// DataRecord dataRecord = new DataRecord(); +// dataRecord.setMetadataId(createResource.getId()); +// dataRecord.setVersion(metadataRecord.getRecordVersion()); +// dataRecord.setSchemaId(metadataRecord.getSchema().getIdentifier()); +// dataRecord.setSchemaVersion(metadataRecord.getSchemaVersion()); +// dataRecord.setMetadataDocumentUri(contentInformation.getContentUri()); +// dataRecord.setDocumentHash(contentInformation.getHash()); +// dataRecord.setLastUpdate(dataResource.getLastUpdate()); +// saveNewDataRecord(dataRecord); +// long nano7 = System.nanoTime() / 1000000; +// LOG.info("Create Record times, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); +// +// return migrateToMetadataRecord(applicationProperties, createResource, true); +// } /** * Update a digital object with given metadata record and/or metadata @@ -430,6 +436,52 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica return migrateToMetadataRecord(applicationProperties, dataResource, true); } + /** + * Delete schema document. + * + * @param applicationProperties Settings of repository. + * @param id ID of the schema document. + * @param eTag E-Tag of the current schema document. + * @param supplier Method for creating access URL. + */ + public static void deleteMetadataSchemaRecord(MetastoreConfiguration applicationProperties, + String id, + String eTag, + UnaryOperator supplier) { + // Find all versions for given id... + int pageNo = 0; + int pageSize = 10; + int totalNoOfPages; + Set uris = new HashSet<>(); + Pageable pgbl; + Page allVersionsOfResource; + do { + pgbl = PageRequest.of(pageNo, pageSize); + allVersionsOfResource = DataResourceUtils.readAllVersionsOfResource(applicationProperties, id, pgbl); + totalNoOfPages = allVersionsOfResource.getTotalPages(); + for (DataResource item : allVersionsOfResource.getContent()) { + uris.add(SchemaRegistryControllerImplV2.getSchemaDocumentUri(item).toString()); + } + pageNo++; + } while (pageNo < totalNoOfPages); + // Test for linked metadata documents + Specification spec = RelatedIdentifierSpec.toSpecification(uris.toArray(new String[]{})); + Optional findOne = dataResourceDao.findOne(spec); + // No references to this schema available -> Ready for deletion + if (findOne.isEmpty()) { + DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); + List listOfSchemaIds = schemaRecordDao.findBySchemaIdOrderByVersionDesc(id); + for (SchemaRecord item : listOfSchemaIds) { + LOG.trace("Delete entry for path '{}'", item.getSchemaDocumentUri()); + List findByPath = url2PathDao.findByPath(item.getSchemaDocumentUri()); + for (Url2Path entry : findByPath) { + url2PathDao.delete(entry); + } + } + schemaRecordDao.deleteAll(listOfSchemaIds); + } + } + /** * Delete a digital object with given identifier. * @@ -885,7 +937,6 @@ public static DataResource getRecordById(MetastoreConfiguration metastorePropert return getRecordByIdAndVersion(metastoreProperties, recordId, null); } - public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metastoreProperties, String recordId, Long version) throws ResourceNotFoundException { //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN @@ -913,26 +964,26 @@ public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metast LOG.info("getRecordByIdAndVersion {}, {}, {}", nano, (nano2 - nano), (nano3 - nano)); return findFirst.get(); } - - public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, - String recordId) throws ResourceNotFoundException { - return getMetadataDocumentByIdAndVersion(metastoreProperties, recordId, null); - } - - public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, - String recordId, Long version) throws ResourceNotFoundException { - LOG.trace("Obtaining metadata record with id {} and version {}.", recordId, version); - MetadataRecord metadataRecord = getRecordByIdAndVersion(metastoreProperties, recordId, version); - - URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); - - Path metadataDocumentPath = Paths.get(metadataDocumentUri); - if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { - LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); - throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); - } - return metadataDocumentPath; - } +// +// public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, +// String recordId) throws ResourceNotFoundException { +// return getMetadataDocumentByIdAndVersion(metastoreProperties, recordId, null); +// } +// +// public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, +// String recordId, Long version) throws ResourceNotFoundException { +// LOG.trace("Obtaining metadata record with id {} and version {}.", recordId, version); +// MetadataRecord metadataRecord = getRecordByIdAndVersion(metastoreProperties, recordId, version); +// +// URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); +// +// Path metadataDocumentPath = Paths.get(metadataDocumentUri); +// if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { +// LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); +// throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); +// } +// return metadataDocumentPath; +// } /** * Merge new metadata record in the existing one. @@ -1053,6 +1104,15 @@ public static void setDataRecordDao(IDataRecordDao aDataRecordDao) { dataRecordDao = aDataRecordDao; } + /** + * Set DAO for data record. + * + * @param aDataRecordDao the dataRecordDao to set + */ + public static void setDataResourceDao(IDataResourceDao aDataResourceDao) { + dataResourceDao = aDataResourceDao; + } + /** * Set the DAO for MetadataFormat. * @@ -1300,7 +1360,7 @@ public static final void check4validId(DataResource metadataRecord) { } } - private static void validateMetadataSchemaDocument(MetastoreConfiguration metastoreProperties, SchemaRecord schemaRecord, MultipartFile document) { + private static void validateMetadataSchemaDocument(MetastoreConfiguration metastoreProperties, DataResource schemaRecord, MultipartFile document) { LOG.debug("Validate metadata schema document..."); if (document == null || document.isEmpty()) { String message = "Missing metadata schema document in body. Returning HTTP BAD_REQUEST."; @@ -1316,7 +1376,7 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast } } - private static void validateMetadataSchemaDocument(MetastoreConfiguration metastoreProperties, SchemaRecord schemaRecord, byte[] document) { + private static void validateMetadataSchemaDocument(MetastoreConfiguration metastoreProperties, DataResource dataResource, byte[] document) { LOG.debug("Validate metadata schema document..."); if (document == null || document.length == 0) { String message = "Missing metadata schema document in body. Returning HTTP BAD_REQUEST."; @@ -1326,15 +1386,15 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast IValidator applicableValidator = null; try { - applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, document); + applicableValidator = getValidatorForRecord(metastoreProperties, dataResource, document); if (applicableValidator == null) { - String message = "No validator found for schema type " + schemaRecord.getType() + ". Returning HTTP UNPROCESSABLE_ENTITY."; + String message = "No validator found for schema type " + dataResource.getFormats().iterator().next() + ". Returning HTTP UNPROCESSABLE_ENTITY."; LOG.error(message); throw new UnprocessableEntityException(message); } else { LOG.trace("Validator found. Checking provided schema file."); - LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); + LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", dataResource.getId(), dataResource.getVersion(), applicableValidator); try (InputStream inputStream = new ByteArrayInputStream(document)) { if (!applicableValidator.isSchemaValid(inputStream)) { String message = "Metadata schema document validation failed. Returning HTTP UNPROCESSABLE_ENTITY."; @@ -1355,54 +1415,44 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast LOG.trace("Schema document is valid!"); } - private static IValidator getValidatorForRecord(MetastoreConfiguration metastoreProperties, SchemaRecord schemaRecord, byte[] schemaDocument) { + private static IValidator getValidatorForRecord(MetastoreConfiguration metastoreProperties, DataResource schemaRecord, byte[] schemaDocument) { IValidator applicableValidator = null; //obtain/guess record type - if (schemaRecord.getType() == null) { - schemaRecord.setType(SchemaUtils.guessType(schemaDocument)); - if (schemaRecord.getType() == null) { + if (schemaRecord.getFormats().isEmpty()) { + String formatDetected = SchemaUtils.guessMimetype(schemaDocument); + if (formatDetected == null) { String message = "Unable to detect schema type automatically. Please provide a valid type"; LOG.error(message); throw new UnprocessableEntityException(message); } else { - LOG.debug("Automatically detected schema type {}.", schemaRecord.getType()); + schemaRecord.getFormats().add(formatDetected); + LOG.debug("Automatically detected mimetype of schema: '{}'.", formatDetected); } } for (IValidator validator : metastoreProperties.getValidators()) { - if (validator.supportsSchemaType(schemaRecord.getType())) { - applicableValidator = validator.getInstance(); - LOG.trace("Found validator for schema: '{}'", schemaRecord.getType().name()); - break; + for (String mimetype : schemaRecord.getFormats()) { + if (validator.supportsMimetype(mimetype)) { + applicableValidator = validator.getInstance(); + LOG.trace("Found validator for schema: '{}'", mimetype); + return applicableValidator; + } } } return applicableValidator; } - private static void saveNewSchemaRecord(SchemaRecord schemaRecord) { - if (schemaRecordDao != null) { - try { - schemaRecordDao.save(schemaRecord); - } catch (Exception npe) { - LOG.error("Can't save schema record: " + schemaRecord, npe); - } - LOG.trace("Schema record saved: {}", schemaRecord); - } - } - private static DataResource checkParameters(MultipartFile dataResourceRecord, MultipartFile document, boolean bothRequired) { - boolean recordAvailable; - boolean documentAvailable; + boolean recordNotAvailable; + boolean documentNotAvailable; DataResource metadataRecord = null; - - recordAvailable = dataResourceRecord == null || dataResourceRecord.isEmpty(); - documentAvailable = document == null || document.isEmpty(); + + recordNotAvailable = dataResourceRecord == null || dataResourceRecord.isEmpty(); + documentNotAvailable = document == null || document.isEmpty(); String message = null; - if (bothRequired) { - if (!recordAvailable || !documentAvailable) { - message = "No data resource record and/or metadata document provided. Returning HTTP BAD_REQUEST."; - } + if (bothRequired && !recordNotAvailable && !documentNotAvailable) { + message = "No data resource record and/or metadata document provided. Returning HTTP BAD_REQUEST."; } else { - if (!(recordAvailable || documentAvailable)) { + if (recordNotAvailable && documentNotAvailable) { message = "Neither metadata record nor metadata document provided."; } } @@ -1411,7 +1461,7 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu throw new BadArgumentException(message); } // Do some checks first. - if (!recordAvailable) { + if (!recordNotAvailable) { try { metadataRecord = Json.mapper().readValue(dataResourceRecord.getInputStream(), DataResource.class); } catch (IOException ex) { @@ -1426,121 +1476,121 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu return metadataRecord; } - /** - * Validate metadata document with given schema. In case of an error a runtime - * exception is thrown. - * - * @param metastoreProperties Configuration properties. - * @param document Document to validate. - * @param schemaId SchemaId of schema. - * @param version Version of the document. - */ - public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, - MultipartFile document, - String schemaId, - Long version) { - LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); - if (schemaId == null) { - String message = "Missing schemaID. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - - long nano1 = System.nanoTime() / 1000000; - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(schemaId); - SchemaRecord schemaRecord = getSchemaRecord(resourceIdentifier, version); - long nano2 = System.nanoTime() / 1000000; - validateMetadataDocument(metastoreProperties, document, schemaRecord); - long nano3 = System.nanoTime() / 1000000; - LOG.info("Validate document(schemaId,version), {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1); - - cleanUp(schemaRecord); - } - - /** - * Validate metadata document with given schema. In case of an error a runtime - * exception is thrown. - * - * @param metastoreProperties Configuration properties. - * @param document Document to validate. - * @param schemaRecord Record of the schema. - */ - public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, - MultipartFile document, - SchemaRecord schemaRecord) { - LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaRecord, document); - - if (document == null || document.isEmpty()) { - String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - try { - try (InputStream inputStream = document.getInputStream()) { - validateMetadataDocument(metastoreProperties, inputStream, schemaRecord); - } - } catch (IOException ex) { - String message = LOG_ERROR_READ_METADATA_DOCUMENT; - LOG.error(message, ex); - throw new UnprocessableEntityException(message); - } - } - - /** - * Validate metadata document with given schema. In case of an error a runtime - * exception is thrown. - * - * @param metastoreProperties Configuration properties. - * @param inputStream Document to validate. - * @param schemaRecord Record of the schema. - */ - public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, - InputStream inputStream, - SchemaRecord schemaRecord) throws IOException { - LOG.trace("validateMetadataInputStream {},{}, {}", metastoreProperties, schemaRecord, inputStream); - - long nano1 = System.nanoTime() / 1000000; - if (schemaRecord == null || schemaRecord.getSchemaDocumentUri() == null || schemaRecord.getSchemaDocumentUri().trim().isEmpty()) { - String message = "Missing or invalid schema record. Returning HTTP BAD_REQUEST."; - LOG.error(message + " -> '{}'", schemaRecord); - throw new BadArgumentException(message); - } - long nano2 = System.nanoTime() / 1000000; - LOG.trace("Checking local schema file."); - Path schemaDocumentPath = Paths.get(URI.create(schemaRecord.getSchemaDocumentUri())); - - if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { - LOG.error("Schema document with schemaId '{}'at path {} either does not exist or is no file or is not readable.", schemaRecord.getSchemaId(), schemaDocumentPath); - throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); - } - LOG.trace("obtain validator for type"); - IValidator applicableValidator; - if (schemaRecord.getType() == null) { - byte[] schemaDocument = FileUtils.readFileToByteArray(schemaDocumentPath.toFile()); - applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, schemaDocument); - } else { - applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, null); - } - long nano3 = System.nanoTime() / 1000000; - - if (applicableValidator == null) { - String message = "No validator found for schema type " + schemaRecord.getType(); - LOG.error(message); - throw new UnprocessableEntityException(message); - } else { - LOG.trace("Validator found."); - - LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); - long nano4 = System.nanoTime() / 1000000; - if (!applicableValidator.validateMetadataDocument(schemaDocumentPath.toFile(), inputStream)) { - LOG.warn("Metadata document validation failed. -> " + applicableValidator.getErrorMessage()); - throw new UnprocessableEntityException(applicableValidator.getErrorMessage()); - } - long nano5 = System.nanoTime() / 1000000; - LOG.info("Validate document(schemaRecord), {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1); - } - LOG.trace("Metadata document validation succeeded."); - } +// /** +// * Validate metadata document with given schema. In case of an error a runtime +// * exception is thrown. +// * +// * @param metastoreProperties Configuration properties. +// * @param document Document to validate. +// * @param schemaId SchemaId of schema. +// * @param version Version of the document. +// */ +// public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, +// MultipartFile document, +// String schemaId, +// Long version) { +// LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); +// if (schemaId == null) { +// String message = "Missing schemaID. Returning HTTP BAD_REQUEST."; +// LOG.error(message); +// throw new BadArgumentException(message); +// } +// +// long nano1 = System.nanoTime() / 1000000; +// ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(schemaId); +// SchemaRecord schemaRecord = getSchemaRecord(resourceIdentifier, version); +// long nano2 = System.nanoTime() / 1000000; +// validateMetadataDocument(metastoreProperties, document, schemaRecord); +// long nano3 = System.nanoTime() / 1000000; +// LOG.info("Validate document(schemaId,version), {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1); +// +// cleanUp(schemaRecord); +// } +// +// /** +// * Validate metadata document with given schema. In case of an error a runtime +// * exception is thrown. +// * +// * @param metastoreProperties Configuration properties. +// * @param document Document to validate. +// * @param schemaRecord Record of the schema. +// */ +// public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, +// MultipartFile document, +// SchemaRecord schemaRecord) { +// LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaRecord, document); +// +// if (document == null || document.isEmpty()) { +// String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; +// LOG.error(message); +// throw new BadArgumentException(message); +// } +// try { +// try (InputStream inputStream = document.getInputStream()) { +// validateMetadataDocument(metastoreProperties, inputStream, schemaRecord); +// } +// } catch (IOException ex) { +// String message = LOG_ERROR_READ_METADATA_DOCUMENT; +// LOG.error(message, ex); +// throw new UnprocessableEntityException(message); +// } +// } +// +// /** +// * Validate metadata document with given schema. In case of an error a runtime +// * exception is thrown. +// * +// * @param metastoreProperties Configuration properties. +// * @param inputStream Document to validate. +// * @param schemaRecord Record of the schema. +// */ +// public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, +// InputStream inputStream, +// SchemaRecord schemaRecord) throws IOException { +// LOG.trace("validateMetadataInputStream {},{}, {}", metastoreProperties, schemaRecord, inputStream); +// +// long nano1 = System.nanoTime() / 1000000; +// if (schemaRecord == null || schemaRecord.getSchemaDocumentUri() == null || schemaRecord.getSchemaDocumentUri().trim().isEmpty()) { +// String message = "Missing or invalid schema record. Returning HTTP BAD_REQUEST."; +// LOG.error(message + " -> '{}'", schemaRecord); +// throw new BadArgumentException(message); +// } +// long nano2 = System.nanoTime() / 1000000; +// LOG.trace("Checking local schema file."); +// Path schemaDocumentPath = Paths.get(URI.create(schemaRecord.getSchemaDocumentUri())); +// +// if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { +// LOG.error("Schema document with schemaId '{}'at path {} either does not exist or is no file or is not readable.", schemaRecord.getSchemaId(), schemaDocumentPath); +// throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); +// } +// LOG.trace("obtain validator for type"); +// IValidator applicableValidator; +// if (schemaRecord.getType() == null) { +// byte[] schemaDocument = FileUtils.readFileToByteArray(schemaDocumentPath.toFile()); +// applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, schemaDocument); +// } else { +// applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, null); +// } +// long nano3 = System.nanoTime() / 1000000; +// +// if (applicableValidator == null) { +// String message = "No validator found for schema type " + schemaRecord.getType(); +// LOG.error(message); +// throw new UnprocessableEntityException(message); +// } else { +// LOG.trace("Validator found."); +// +// LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); +// long nano4 = System.nanoTime() / 1000000; +// if (!applicableValidator.validateMetadataDocument(schemaDocumentPath.toFile(), inputStream)) { +// LOG.warn("Metadata document validation failed. -> " + applicableValidator.getErrorMessage()); +// throw new UnprocessableEntityException(applicableValidator.getErrorMessage()); +// } +// long nano5 = System.nanoTime() / 1000000; +// LOG.info("Validate document(schemaRecord), {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1); +// } +// LOG.trace("Metadata document validation succeeded."); +// } /** * Gets SchemaRecord from identifier. Afterwards there should be a clean up. @@ -1577,7 +1627,8 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v case URL -> { schemaRecord = prepareResourceFromUrl(identifier, version); } - default -> throw new BadArgumentException("For schema document identifier type '" + identifier.getIdentifierType() + "' is not allowed!"); + default -> + throw new BadArgumentException("For schema document identifier type '" + identifier.getIdentifierType() + "' is not allowed!"); } if (schemaRecord != null) { LOG.trace("getSchemaRecord {},{}", schemaRecord.getSchemaDocumentUri(), schemaRecord.getVersion()); @@ -1588,31 +1639,32 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v } private static SchemaRecord prepareResourceFromUrl(ResourceIdentifier identifier, Long version) { - String url = identifier.getIdentifier(); - Path pathToFile; - MetadataSchemaRecord.SCHEMA_TYPE type = null; - Optional findByUrl = url2PathDao.findByUrl(url); - if (findByUrl.isPresent()) { - url = findByUrl.get().getPath(); - type = findByUrl.get().getType(); - pathToFile = Paths.get(URI.create(url)); - } else { - URI resourceUrl; - try { - resourceUrl = new URI(url); - } catch (URISyntaxException ex) { - String message = String.format("Invalid URL: '%s'", url); - LOG.error(message, ex); - throw new BadArgumentException(message); - } - Optional path = DownloadUtil.downloadResource(resourceUrl); - pathToFile = path.get(); - } - SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaDocumentUri(pathToFile.toUri().toString()); - schemaRecord.setType(type); - return schemaRecord; + String url = identifier.getIdentifier(); + Path pathToFile; + MetadataSchemaRecord.SCHEMA_TYPE type = null; + Optional findByUrl = url2PathDao.findByUrl(url); + if (findByUrl.isPresent()) { + url = findByUrl.get().getPath(); + type = findByUrl.get().getType(); + pathToFile = Paths.get(URI.create(url)); + } else { + URI resourceUrl; + try { + resourceUrl = new URI(url); + } catch (URISyntaxException ex) { + String message = String.format("Invalid URL: '%s'", url); + LOG.error(message, ex); + throw new BadArgumentException(message); + } + Optional path = DownloadUtil.downloadResource(resourceUrl); + pathToFile = path.get(); + } + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaDocumentUri(pathToFile.toUri().toString()); + schemaRecord.setType(type); + return schemaRecord; } + /** * Remove all downloaded files for schema Record. * @@ -1676,22 +1728,18 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, dataResource); - SchemaRecord schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(dataResource.getId()); if (metadataRecord != null) { - metadataRecord.setVersion(Long.toString(schemaRecord.getVersion())); - existingRecord = mergeRecords(existingRecord, metadataRecord); - mergeSchemaRecord(schemaRecord, existingRecord); - dataResource = migrateToDataResource(applicationProperties, existingRecord); + metadataRecord.setVersion(dataResource.getVersion()); + dataResource = metadataRecord; } else { dataResource = DataResourceUtils.copyDataResource(dataResource); } + ContentInformation info; + info = getContentInformationOfResource(applicationProperties, dataResource); if (schemaDocument != null) { // Get schema record for this schema - validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDocument); - - ContentInformation info; - info = getContentInformationOfResource(applicationProperties, dataResource); + validateMetadataSchemaDocument(applicationProperties, dataResource, schemaDocument); boolean noChanges = false; String fileName = schemaDocument.getOriginalFilename(); @@ -1727,24 +1775,23 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); } ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); - } else { - schemaRecordDao.delete(schemaRecord); } } else { - schemaRecordDao.delete(schemaRecord); // validate if document is still valid due to changed record settings. - metadataRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); - URI schemaDocumentUri = URI.create(metadataRecord.getSchemaDocumentUri()); + Objects.requireNonNull(info); + URI schemaDocumentUri = URI.create(info.getContentUri()); Path schemaDocumentPath = Paths.get(schemaDocumentUri); - if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { + if (!Files.exists(schemaDocumentPath) || + !Files.isRegularFile(schemaDocumentPath) || + !Files.isReadable(schemaDocumentPath)) { LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); } try { byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); - MetadataSchemaRecordUtil.validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDoc); + DataResourceRecordUtil.validateMetadataSchemaDocument(applicationProperties, dataResource, schemaDoc); } catch (IOException ex) { LOG.error("Error validating file!", ex); } @@ -1752,8 +1799,6 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app } dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); - return migrateToMetadataSchemaRecord(applicationProperties, dataResource, true); + return dataResource; } - - } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index f7c55088..73ce6703 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -1102,7 +1102,7 @@ private static SchemaRecord transformToSchemaRecord(MetadataSchemaRecord result) return schemaRecord; } - private static void saveNewSchemaRecord(SchemaRecord schemaRecord) { + public static void saveNewSchemaRecord(SchemaRecord schemaRecord) { if (schemaRecordDao != null) { try { schemaRecordDao.save(schemaRecord); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java b/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java index 6596d949..467f8f38 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java @@ -27,8 +27,8 @@ import javax.xml.parsers.ParserConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.xml.sax.SAXException; @@ -80,6 +80,34 @@ public static MetadataSchemaRecord.SCHEMA_TYPE guessType(byte[] schema) { return null; } + /** + * Guess type of schema document. + * + * @param schema schema document. + * @return Mimetype of document. + */ + public static String guessMimetype(byte[] schema) { + // Cut schema to a maximum of MAX_LENGTH_OF_HEADER characters. + if (schema != null) { + int length = schema.length > MAX_LENGTH_OF_HEADER ? MAX_LENGTH_OF_HEADER : schema.length; + String schemaAsString = new String(schema, 0, length, StandardCharsets.UTF_8); + LOG.trace("Guess type for '{}'", schemaAsString); + + Matcher m = JSON_FIRST_BYTE.matcher(schemaAsString); + if (schemaAsString.contains("{") && m.matches()) { + return MediaType.APPLICATION_JSON_VALUE; + } else { + if (schemaAsString.contains("<")) { + m = XML_FIRST_BYTE.matcher(schemaAsString); + if (m.matches()) { + return MediaType.APPLICATION_XML_VALUE; + } + } + } + } + return null; + } + /** * Determine target namespace from schema. * @param schema Schema document. diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java index 9295868a..6242e6bb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java @@ -42,8 +42,19 @@ default IValidator getInstance() { * * @return supports schema type or not. */ + @Deprecated boolean supportsSchemaType(MetadataSchemaRecord.SCHEMA_TYPE type); + /** + * Supports the given MIME type. + * + * @see https://www.iana.org/assignments/media-types/media-types.xhtml + * @param type Type of the schema. + * + * @return supports schema type or not. + */ + boolean supportsMimetype(String mimetype); + /** * Is given schema valid. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java index f9e6a2c6..e04db575 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java @@ -15,6 +15,7 @@ import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; /** @@ -37,6 +38,11 @@ public boolean supportsSchemaType(MetadataSchemaRecord.SCHEMA_TYPE type) { return MetadataSchemaRecord.SCHEMA_TYPE.JSON.equals(type); } + @Override + public boolean supportsMimetype(String type) { + return MediaType.APPLICATION_JSON_VALUE.equals(type); + } + @Override public boolean isSchemaValid(InputStream schemaStream) { LOG.trace("Checking JSON schema for validity."); diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java index 51607ed0..1d819e2c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java @@ -28,6 +28,7 @@ import org.apache.xerces.impl.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; @@ -49,6 +50,11 @@ public boolean supportsSchemaType(MetadataSchemaRecord.SCHEMA_TYPE type) { return MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(type); } + @Override + public boolean supportsMimetype(String type) { + return MediaType.APPLICATION_XML_VALUE.equals(type); + } + @Override public IValidator getInstance() { return new XmlValidator(); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java index 1c7c6788..78ec9eee 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java @@ -52,10 +52,12 @@ /** * Interface for metadata documents controller. + * @deprecated version 3 please use IMetadataController_v2 instead */ @ApiResponses(value = { @ApiResponse(responseCode = "401", description = "Unauthorized is returned if authorization in required but was not provided."), @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) +@Deprecated public interface IMetadataController extends InfoContributor { @Operation(summary = "Ingest a new metadata document and its record.", description = "This endpoint allows to create a new metadata record by providing the record metadata as JSON document as well as the actual metadata as file upload. The record metadata mainly contains " diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index 2dc2966d..d3301038 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -55,7 +55,7 @@ @ApiResponses(value = { @ApiResponse(responseCode = "401", description = "Unauthorized is returned if authorization in required but was not provided."), @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) -public interface IMetadataController_v2 extends InfoContributor { +public interface IMetadataControllerV2 extends InfoContributor { @Operation(summary = "Ingest a new metadata document and its record.", description = "This endpoint allows to create a new metadata record by providing the record metadata as JSON document as well as the actual metadata as file upload. The record metadata mainly contains " + "the resource identifier the record is associated with as well as the identifier of the schema which can be used to validate the provided metadata document. In the current version, both parameters are required. If no schema version is given (if 'INTERNAL' reference" diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java index f0c51619..01be4432 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java @@ -50,12 +50,15 @@ /** * Interface for schema document controller. + * + * @deprecated version 3 please use ISchemaRegistryController_v2 instead * * @author jejkal */ @ApiResponses(value = { @ApiResponse(responseCode = "401", description = "Unauthorized is returned if authorization in required but was not provided."), @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) + @Deprecated public interface ISchemaRegistryController extends InfoContributor { @Operation(summary = "Register a schema document and its record.", description = "This endpoint allows to register a schema document and its record. " diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java index e39d3115..c0c0c6ec 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java @@ -57,7 +57,7 @@ @ApiResponses(value = { @ApiResponse(responseCode = "401", description = "Unauthorized is returned if authorization in required but was not provided."), @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) -public interface ISchemaRegistryController_v2 extends InfoContributor { +public interface ISchemaRegistryControllerV2 extends InfoContributor { @Operation(summary = "Register a schema document and its record.", description = "This endpoint allows to register a schema document and its record. " + "The record must contain at least an unique identifier (schemaId) and the type of the schema (type).", diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java index f16d3116..b3093a1b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java @@ -1,506 +1,506 @@ -/* - * Copyright 2019 Karlsruhe Institute of Technology. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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 edu.kit.datamanager.metastore2.web.impl; - -import com.fasterxml.jackson.core.JsonParseException; -import edu.kit.datamanager.entities.PERMISSION; -import edu.kit.datamanager.entities.RepoUserRole; -import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; -import edu.kit.datamanager.exceptions.AccessForbiddenException; -import edu.kit.datamanager.exceptions.BadArgumentException; -import edu.kit.datamanager.exceptions.ResourceNotFoundException; -import edu.kit.datamanager.exceptions.UnprocessableEntityException; -import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; -import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; -import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; -import edu.kit.datamanager.metastore2.domain.AclRecord; -import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; -import edu.kit.datamanager.metastore2.util.ActuatorUtil; -import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; -import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; -import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; -import edu.kit.datamanager.metastore2.web.IMetadataController_v2; -import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; -import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; -import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; -import edu.kit.datamanager.repo.domain.DataResource; -import edu.kit.datamanager.repo.domain.RelatedIdentifier; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.service.IMessagingService; -import edu.kit.datamanager.service.impl.LogfileMessagingService; -import edu.kit.datamanager.util.AuthenticationHelper; -import edu.kit.datamanager.util.ControllerUtils; -import io.swagger.v3.core.util.Json; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.UnaryOperator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.info.Info; -import org.springframework.context.annotation.Bean; -import org.springframework.core.io.FileSystemResource; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * Controller for metadata documents. - */ -@Controller -@RequestMapping(value = "/api/v2/metadata") -@Tag(name = "Metadata Repository") -@Schema(description = "Metadata Resource Management") -public class MetadataControllerImpl_v2 implements IMetadataController_v2 { - - public static final String POST_FILTER = "post_filter"; - /** - * Placeholder string for id of resource. (landingpage) - */ - public static final String PLACEHOLDER_ID = "$(id)"; - /** - * Placeholder string for version of resource. (landingpage) - */ - public static final String PLACEHOLDER_VERSION = "$(version)"; - - private static final Logger LOG = LoggerFactory.getLogger(MetadataControllerImpl_v2.class); - - private final ApplicationProperties applicationProperties; - - private final ILinkedMetadataRecordDao metadataRecordDao; - - private final MetastoreConfiguration metadataConfig; - - private final IDataResourceDao dataResourceDao; - - /** - * Optional messagingService bean may or may not be available, depending on a - * service's configuration. If messaging capabilities are disabled, this bean - * should be not available. In that case, messages are only logged. - */ - @Autowired - private Optional messagingService; - - private final String guestToken; - - /** - * Constructor for metadata documents controller. - * - * @param applicationProperties Configuration for controller. - * @param metadataConfig Configuration for metadata documents repository. - * @param metadataRecordDao DAO for metadata records. - * @param dataResourceDao DAO for data resources. - */ - public MetadataControllerImpl_v2(ApplicationProperties applicationProperties, - MetastoreConfiguration metadataConfig, - ILinkedMetadataRecordDao metadataRecordDao, - IDataResourceDao dataResourceDao) { - this.applicationProperties = applicationProperties; - this.metadataConfig = metadataConfig; - this.metadataRecordDao = metadataRecordDao; - this.dataResourceDao = dataResourceDao; - LOG.info("------------------------------------------------------"); - LOG.info("------{}", this.metadataConfig); - LOG.info("------------------------------------------------------"); - LOG.trace("Create guest token"); - guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken("guest", RepoUserRole.GUEST). - addSimpleClaim("email", "metastore@localhost"). - addSimpleClaim("loginFailures", 0). - addSimpleClaim("active", true). - addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); - MetadataRecordUtil.setToken(guestToken); - } - - @Override - public ResponseEntity createRecord( - @RequestPart(name = "record") final MultipartFile recordDocument, - @RequestPart(name = "document") final MultipartFile document, - HttpServletRequest request, - HttpServletResponse response, - UriComponentsBuilder uriBuilder) throws URISyntaxException { - - long nano1 = System.nanoTime() / 1000000; - LOG.trace("Performing createRecord({},...).", recordDocument); - DataResource metadataRecord; - if (recordDocument == null || recordDocument.isEmpty()) { - String message = "No data resource record provided. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - try { - metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); - } catch (IOException ex) { - String message = "No valid data resource record provided. Returning HTTP BAD_REQUEST."; - if (ex instanceof JsonParseException) { - message = message + " Reason: " + ex.getMessage(); - } - LOG.error("Error parsing json: ", ex); - throw new BadArgumentException(message); - } - long nano2 = System.nanoTime() / 1000000; - - DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(metadataRecord); - - LOG.debug("Test for existing metadata record for given schema and resource"); - RelatedIdentifier schemaIdentifier; - try { - schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); - } catch (ResourceNotFoundException rnfe) { - LOG.debug("Error checking for existing relations.", rnfe); - throw new UnprocessableEntityException("Schema ID seems to be invalid"); - } - boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId(metadataRecord.getRelatedResource().getIdentifier(), schemaIdentifier.getIdentifier()); - long nano3 = System.nanoTime() / 1000000; - - if (recordAlreadyExists) { - String message = String.format("Conflict! There is already a metadata document with " - + "the same schema ('%s') and the same related resource ('%s')", - metadataRecord.getSchemaId(), - metadataRecord.getRelatedResource().getIdentifier()); - LOG.error(message); - return ResponseEntity.status(HttpStatus.CONFLICT).body(message); - } - DataResource result = MetadataRecordUtil.createDataResource(metadataConfig, recordDocument, document); - // Successfully created metadata record. - long nano4 = System.nanoTime() / 1000000; - LOG.trace("Metadata record successfully persisted. Returning result."); - MetadataRecordUtil.fixMetadataDocumentUri(result); - long nano5 = System.nanoTime() / 1000000; - metadataRecordDao.save(new LinkedMetadataRecord(result)); - long nano6 = System.nanoTime() / 1000000; - - URI locationUri; - locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), result.getRecordVersion(), null, null)).toUri(); - long nano7 = System.nanoTime() / 1000000; - LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); - - LOG.trace("Sending CREATE event."); - messagingService.orElse(new LogfileMessagingService()). - send(MetadataResourceMessage.factoryCreateMetadataMessage(result, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); - - return ResponseEntity.created(locationUri).eTag("\"" + result.getEtag() + "\"").body(result); - } - - @Override - public ResponseEntity getRecordById( - @PathVariable(value = "id") String id, - @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr - ) { - LOG.trace("Performing getRecordById({}, {}).", id, version); - - LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); - DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); - LOG.trace("Metadata record found. Prepare response."); - //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN - LOG.trace("Get ETag of DataResource."); - String etag = metadataRecord.getEtag(); - MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); - - return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); - } - - @Override - public ResponseEntity getAclById( - @PathVariable(value = "id") String id, - @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr - ) { - LOG.trace("Performing getAclById({}, {}).", id, version); - if (!AuthenticationHelper.isAuthenticatedAsService()) { - throw new AccessForbiddenException("Only for services!"); - } - - DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); - MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); - AclRecord aclRecord = new AclRecord(); - aclRecord.setAcl(metadataRecord.getAcl()); - aclRecord.setDataResource(metadataRecord); - - return ResponseEntity.ok().body(aclRecord); - } - - @Override - public ResponseEntity getMetadataDocumentById( - @PathVariable(value = "id") String id, - @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr - ) { - LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); - - Path metadataDocumentPath = MetadataRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); - - return ResponseEntity. - ok(). - header(HttpHeaders.CONTENT_LENGTH, String.valueOf(metadataDocumentPath.toFile().length())). - body(new FileSystemResource(metadataDocumentPath.toFile())); - } - - @Override - public ModelAndView getLandingpageById( - @PathVariable(value = "id") String id, - @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr) { - LOG.trace("Performing Landing page for metadata document with ({}, {}).", id, version); - String redirectUrl = applicationProperties.getMetadataLandingPage(); - redirectUrl = redirectUrl.replace(PLACEHOLDER_ID, id); - String versionString = ""; - if (version != null) { - versionString = version.toString(); - } - redirectUrl = "redirect:" + redirectUrl.replace(PLACEHOLDER_VERSION, versionString); - - LOG.trace("Redirect to '{}'", redirectUrl); - - return new ModelAndView(redirectUrl); - } - - public ResponseEntity> getAllVersions( - @PathVariable(value = "id") String id, - Pageable pgbl - ) { - LOG.trace("Performing getAllVersions({}).", id); - // Search for resource type of MetadataSchemaRecord - - //if security is enabled, include principal in query - LOG.debug("Performing query for records."); - DataResource recordByIdAndVersion = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id); - List recordList = new ArrayList<>(); - long totalNoOfElements = recordByIdAndVersion.getRecordVersion(); - for (long version = totalNoOfElements - pgbl.getOffset(), size = 0; version > 0 && size < pgbl.getPageSize(); version--, size++) { - recordList.add(MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version)); - } - - LOG.trace("Transforming Dataresource to DataResource"); - List metadataList = new ArrayList<>(); - recordList.forEach(metadataRecord -> { - MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); - metadataList.add(metadataRecord); - }); - - String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); - - return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); - } - - @Override - public ResponseEntity> getRecords( - @RequestParam(value = "id", required = false) String id, - @RequestParam(value = "resourceId", required = false) List relatedIds, - @RequestParam(value = "schemaId", required = false) List schemaIds, - @RequestParam(name = "from", required = false) Instant updateFrom, - @RequestParam(name = "until", required = false) Instant updateUntil, - Pageable pgbl, - WebRequest wr, - HttpServletResponse hsr, - UriComponentsBuilder ucb - ) { - LOG.trace("Performing getRecords({}, {}, {}, {}).", relatedIds, schemaIds, updateFrom, updateUntil); - if (id != null) { - return getAllVersions(id, pgbl); - } - // Search for resource type of MetadataSchemaRecord - Specification spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResource.RESOURCE_TYPE)); - // Add authentication if enabled - if (metadataConfig.isAuthEnabled()) { - boolean isAdmin; - isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); - // Add authorization for non administrators - if (!isAdmin) { - List authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - if (authorizationIdentities != null) { - LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); - Specification permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); - spec = spec.and(permissionSpec); - } else { - LOG.trace("No permission information provided. Skip creating permission specification."); - } - } - } - List allRelatedIdentifiersSchema = new ArrayList<>(); - List allRelatedIdentifiersResource = new ArrayList<>(); - -// File file = new File(new URIoa) - if (schemaIds != null) { - for (String schemaId : schemaIds) { - MetadataSchemaRecord currentSchemaRecord; - try { - currentSchemaRecord = MetadataRecordUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); - // Test for internal URI -> Transform to global URI. - if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { - ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); - currentSchemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); - } - allRelatedIdentifiersSchema.add(currentSchemaRecord.getSchemaDocumentUri()); - } catch (Exception rnfe) { - // schemaID not found set version to 1 - currentSchemaRecord = new MetadataSchemaRecord(); - currentSchemaRecord.setSchemaVersion(1l); - allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); - } - for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { - MetadataSchemaRecord schemaRecord = MetadataRecordUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); - // Test for internal URI -> Transform to global URI. - if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { - ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); - schemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); - } - allRelatedIdentifiersSchema.add(schemaRecord.getSchemaDocumentUri()); - } - } - Specification schemaSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersSchema.toArray(new String[allRelatedIdentifiersSchema.size()])); - spec = spec.and(schemaSpecification); - } - if (relatedIds != null) { - allRelatedIdentifiersResource.addAll(relatedIds); - Specification relResourceSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersResource.toArray(new String[allRelatedIdentifiersResource.size()])); - spec = spec.and(relResourceSpecification); - } - if ((updateFrom != null) || (updateUntil != null)) { - spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); - } - - // Hide revoked and gone data resources. - DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; - List stateList = Arrays.asList(states); - spec = spec.and(StateSpecification.toSpecification(stateList)); - - if (LOG.isTraceEnabled()) { - Page records = dataResourceDao.findAll(pgbl); - LOG.trace("List all data resources..."); - LOG.trace("-----------------------------------------------"); - for (DataResource item : records.getContent()) { - LOG.trace("- '{}'", item); - } - LOG.trace("-----------------------------------------------"); - LOG.trace("Specification: '{}'", spec); - } - LOG.debug("Performing query for records."); - Page records = dataResourceDao.findAll(spec, pgbl); - - LOG.trace("Transforming Dataresource to DataResource"); - List recordList = records.getContent(); - List metadataList = new ArrayList<>(); - recordList.forEach(metadataRecord -> { - DataResource item = MetadataRecordUtil.migrateToDataResource(metadataConfig, metadataRecord, false); - MetadataRecordUtil.fixMetadataDocumentUri(item); - metadataList.add(item); - }); - - String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); - - return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); - } - - @Override - public ResponseEntity updateRecord( - @PathVariable("id") String id, - @RequestPart(name = "record", required = false) MultipartFile metadataRecord, - @RequestPart(name = "document", required = false) final MultipartFile document, - WebRequest request, - HttpServletResponse response, - UriComponentsBuilder uriBuilder - ) { - LOG.trace("Performing updateRecord({}, {}, {}).", id, metadataRecord, "#document"); - UnaryOperator getById; - getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); - String eTag = ControllerUtils.getEtagFromHeader(request); - DataResource updateDataResource = MetadataRecordUtil.updateDataResource(metadataConfig, id, eTag, metadataRecord, document, getById); - - LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); - String etag = updateDataResource.getEtag(); - MetadataRecordUtil.fixMetadataDocumentUri(updateDataResource); - - URI locationUri; - locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(updateDataResource.getId(), updateDataResource.getRecordVersion(), null, null)).toUri(); - - LOG.trace("Sending UPDATE event."); - messagingService.orElse(new LogfileMessagingService()). - send(MetadataResourceMessage.factoryUpdateMetadataMessage(updateDataResource, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); - - return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updateDataResource); - } - - @Override - public ResponseEntity deleteRecord( - @PathVariable(value = "id") String id, - WebRequest wr, - HttpServletResponse hsr - ) { - LOG.trace("Performing deleteRecord({}).", id); - UnaryOperator getById; - getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, wr, hsr)).toString(); - - String eTag = ControllerUtils.getEtagFromHeader(wr); - MetadataRecordUtil.deleteDataResource(metadataConfig, id, eTag, getById); - - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - - @Override - public void contribute(Info.Builder builder) { - LOG.trace("Check for MetadataRepo actuator information..."); - - URL basePath = metadataConfig.getBasepath(); - Map details = ActuatorUtil.testDirectory(basePath); - - if (!details.isEmpty()) { - details.put("No of metadata documents", Long.toString(MetadataRecordUtil.getNoOfDocuments())); - builder.withDetail("metadataRepo", details); - } - } - - @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); - } -} +///* +// * Copyright 2019 Karlsruhe Institute of Technology. +// * +// * 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 +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * 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 edu.kit.datamanager.metastore2.web.impl; +// +//import com.fasterxml.jackson.core.JsonParseException; +//import edu.kit.datamanager.entities.PERMISSION; +//import edu.kit.datamanager.entities.RepoUserRole; +//import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; +//import edu.kit.datamanager.exceptions.AccessForbiddenException; +//import edu.kit.datamanager.exceptions.BadArgumentException; +//import edu.kit.datamanager.exceptions.ResourceNotFoundException; +//import edu.kit.datamanager.exceptions.UnprocessableEntityException; +//import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +//import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +//import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; +//import edu.kit.datamanager.metastore2.domain.AclRecord; +//import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; +//import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +//import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +//import edu.kit.datamanager.metastore2.util.ActuatorUtil; +//import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +//import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; +//import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; +//import edu.kit.datamanager.metastore2.web.IMetadataController_v2; +//import edu.kit.datamanager.repo.dao.IDataResourceDao; +//import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; +//import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; +//import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; +//import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; +//import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; +//import edu.kit.datamanager.repo.domain.DataResource; +//import edu.kit.datamanager.repo.domain.RelatedIdentifier; +//import edu.kit.datamanager.repo.domain.ResourceType; +//import edu.kit.datamanager.service.IMessagingService; +//import edu.kit.datamanager.service.impl.LogfileMessagingService; +//import edu.kit.datamanager.util.AuthenticationHelper; +//import edu.kit.datamanager.util.ControllerUtils; +//import io.swagger.v3.core.util.Json; +//import io.swagger.v3.oas.annotations.media.Schema; +//import io.swagger.v3.oas.annotations.tags.Tag; +//import jakarta.servlet.http.HttpServletRequest; +//import jakarta.servlet.http.HttpServletResponse; +//import java.io.IOException; +//import java.net.URI; +//import java.net.URISyntaxException; +//import java.net.URL; +//import java.nio.file.Path; +//import java.time.Instant; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.List; +//import java.util.Map; +//import java.util.Optional; +//import java.util.function.UnaryOperator; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.actuate.info.Info; +//import org.springframework.context.annotation.Bean; +//import org.springframework.core.io.FileSystemResource; +//import org.springframework.data.domain.Page; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.jpa.domain.Specification; +//import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +//import org.springframework.http.HttpHeaders; +//import org.springframework.http.HttpStatus; +//import org.springframework.http.ResponseEntity; +//import org.springframework.stereotype.Controller; +//import org.springframework.web.bind.annotation.PathVariable; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RequestParam; +//import org.springframework.web.bind.annotation.RequestPart; +//import org.springframework.web.client.RestTemplate; +//import org.springframework.web.context.request.WebRequest; +//import org.springframework.web.multipart.MultipartFile; +//import org.springframework.web.servlet.ModelAndView; +//import org.springframework.web.util.UriComponentsBuilder; +// +///** +// * Controller for metadata documents. +// */ +//@Controller +//@RequestMapping(value = "/api/v2/metadata") +//@Tag(name = "Metadata Repository") +//@Schema(description = "Metadata Resource Management") +//public class MetadataControllerImpl_v2 implements IMetadataController_v2 { +// +// public static final String POST_FILTER = "post_filter"; +// /** +// * Placeholder string for id of resource. (landingpage) +// */ +// public static final String PLACEHOLDER_ID = "$(id)"; +// /** +// * Placeholder string for version of resource. (landingpage) +// */ +// public static final String PLACEHOLDER_VERSION = "$(version)"; +// +// private static final Logger LOG = LoggerFactory.getLogger(MetadataControllerImpl_v2.class); +// +// private final ApplicationProperties applicationProperties; +// +// private final ILinkedMetadataRecordDao metadataRecordDao; +// +// private final MetastoreConfiguration metadataConfig; +// +// private final IDataResourceDao dataResourceDao; +// +// /** +// * Optional messagingService bean may or may not be available, depending on a +// * service's configuration. If messaging capabilities are disabled, this bean +// * should be not available. In that case, messages are only logged. +// */ +// @Autowired +// private Optional messagingService; +// +// private final String guestToken; +// +// /** +// * Constructor for metadata documents controller. +// * +// * @param applicationProperties Configuration for controller. +// * @param metadataConfig Configuration for metadata documents repository. +// * @param metadataRecordDao DAO for metadata records. +// * @param dataResourceDao DAO for data resources. +// */ +// public MetadataControllerImpl_v2(ApplicationProperties applicationProperties, +// MetastoreConfiguration metadataConfig, +// ILinkedMetadataRecordDao metadataRecordDao, +// IDataResourceDao dataResourceDao) { +// this.applicationProperties = applicationProperties; +// this.metadataConfig = metadataConfig; +// this.metadataRecordDao = metadataRecordDao; +// this.dataResourceDao = dataResourceDao; +// LOG.info("------------------------------------------------------"); +// LOG.info("------{}", this.metadataConfig); +// LOG.info("------------------------------------------------------"); +// LOG.trace("Create guest token"); +// guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken("guest", RepoUserRole.GUEST). +// addSimpleClaim("email", "metastore@localhost"). +// addSimpleClaim("loginFailures", 0). +// addSimpleClaim("active", true). +// addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); +// MetadataRecordUtil.setToken(guestToken); +// } +// +// @Override +// public ResponseEntity createRecord( +// @RequestPart(name = "record") final MultipartFile recordDocument, +// @RequestPart(name = "document") final MultipartFile document, +// HttpServletRequest request, +// HttpServletResponse response, +// UriComponentsBuilder uriBuilder) throws URISyntaxException { +// +// long nano1 = System.nanoTime() / 1000000; +// LOG.trace("Performing createRecord({},...).", recordDocument); +// DataResource metadataRecord; +// if (recordDocument == null || recordDocument.isEmpty()) { +// String message = "No data resource record provided. Returning HTTP BAD_REQUEST."; +// LOG.error(message); +// throw new BadArgumentException(message); +// } +// try { +// metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); +// } catch (IOException ex) { +// String message = "No valid data resource record provided. Returning HTTP BAD_REQUEST."; +// if (ex instanceof JsonParseException) { +// message = message + " Reason: " + ex.getMessage(); +// } +// LOG.error("Error parsing json: ", ex); +// throw new BadArgumentException(message); +// } +// long nano2 = System.nanoTime() / 1000000; +// +// DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(metadataRecord); +// +// LOG.debug("Test for existing metadata record for given schema and resource"); +// RelatedIdentifier schemaIdentifier; +// try { +// schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); +// } catch (ResourceNotFoundException rnfe) { +// LOG.debug("Error checking for existing relations.", rnfe); +// throw new UnprocessableEntityException("Schema ID seems to be invalid"); +// } +// boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId(metadataRecord.getRelatedResource().getIdentifier(), schemaIdentifier.getIdentifier()); +// long nano3 = System.nanoTime() / 1000000; +// +// if (recordAlreadyExists) { +// String message = String.format("Conflict! There is already a metadata document with " +// + "the same schema ('%s') and the same related resource ('%s')", +// metadataRecord.getSchemaId(), +// metadataRecord.getRelatedResource().getIdentifier()); +// LOG.error(message); +// return ResponseEntity.status(HttpStatus.CONFLICT).body(message); +// } +// DataResource result = MetadataRecordUtil.createDataResource(metadataConfig, recordDocument, document); +// // Successfully created metadata record. +// long nano4 = System.nanoTime() / 1000000; +// LOG.trace("Metadata record successfully persisted. Returning result."); +// MetadataRecordUtil.fixMetadataDocumentUri(result); +// long nano5 = System.nanoTime() / 1000000; +// metadataRecordDao.save(new LinkedMetadataRecord(result)); +// long nano6 = System.nanoTime() / 1000000; +// +// URI locationUri; +// locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), result.getRecordVersion(), null, null)).toUri(); +// long nano7 = System.nanoTime() / 1000000; +// LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); +// +// LOG.trace("Sending CREATE event."); +// messagingService.orElse(new LogfileMessagingService()). +// send(MetadataResourceMessage.factoryCreateMetadataMessage(result, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); +// +// return ResponseEntity.created(locationUri).eTag("\"" + result.getEtag() + "\"").body(result); +// } +// +// @Override +// public ResponseEntity getRecordById( +// @PathVariable(value = "id") String id, +// @RequestParam(value = "version", required = false) Long version, +// WebRequest wr, +// HttpServletResponse hsr +// ) { +// LOG.trace("Performing getRecordById({}, {}).", id, version); +// +// LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); +// DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); +// LOG.trace("Metadata record found. Prepare response."); +// //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN +// LOG.trace("Get ETag of DataResource."); +// String etag = metadataRecord.getEtag(); +// MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); +// +// return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); +// } +// +// @Override +// public ResponseEntity getAclById( +// @PathVariable(value = "id") String id, +// @RequestParam(value = "version", required = false) Long version, +// WebRequest wr, +// HttpServletResponse hsr +// ) { +// LOG.trace("Performing getAclById({}, {}).", id, version); +// if (!AuthenticationHelper.isAuthenticatedAsService()) { +// throw new AccessForbiddenException("Only for services!"); +// } +// +// DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); +// MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); +// AclRecord aclRecord = new AclRecord(); +// aclRecord.setAcl(metadataRecord.getAcl()); +// aclRecord.setDataResource(metadataRecord); +// +// return ResponseEntity.ok().body(aclRecord); +// } +// +// @Override +// public ResponseEntity getMetadataDocumentById( +// @PathVariable(value = "id") String id, +// @RequestParam(value = "version", required = false) Long version, +// WebRequest wr, +// HttpServletResponse hsr +// ) { +// LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); +// +// Path metadataDocumentPath = MetadataRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); +// +// return ResponseEntity. +// ok(). +// header(HttpHeaders.CONTENT_LENGTH, String.valueOf(metadataDocumentPath.toFile().length())). +// body(new FileSystemResource(metadataDocumentPath.toFile())); +// } +// +// @Override +// public ModelAndView getLandingpageById( +// @PathVariable(value = "id") String id, +// @RequestParam(value = "version", required = false) Long version, +// WebRequest wr, +// HttpServletResponse hsr) { +// LOG.trace("Performing Landing page for metadata document with ({}, {}).", id, version); +// String redirectUrl = applicationProperties.getMetadataLandingPage(); +// redirectUrl = redirectUrl.replace(PLACEHOLDER_ID, id); +// String versionString = ""; +// if (version != null) { +// versionString = version.toString(); +// } +// redirectUrl = "redirect:" + redirectUrl.replace(PLACEHOLDER_VERSION, versionString); +// +// LOG.trace("Redirect to '{}'", redirectUrl); +// +// return new ModelAndView(redirectUrl); +// } +// +// public ResponseEntity> getAllVersions( +// @PathVariable(value = "id") String id, +// Pageable pgbl +// ) { +// LOG.trace("Performing getAllVersions({}).", id); +// // Search for resource type of MetadataSchemaRecord +// +// //if security is enabled, include principal in query +// LOG.debug("Performing query for records."); +// DataResource recordByIdAndVersion = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id); +// List recordList = new ArrayList<>(); +// long totalNoOfElements = recordByIdAndVersion.getRecordVersion(); +// for (long version = totalNoOfElements - pgbl.getOffset(), size = 0; version > 0 && size < pgbl.getPageSize(); version--, size++) { +// recordList.add(MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version)); +// } +// +// LOG.trace("Transforming Dataresource to DataResource"); +// List metadataList = new ArrayList<>(); +// recordList.forEach(metadataRecord -> { +// MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); +// metadataList.add(metadataRecord); +// }); +// +// String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); +// +// return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); +// } +// +// @Override +// public ResponseEntity> getRecords( +// @RequestParam(value = "id", required = false) String id, +// @RequestParam(value = "resourceId", required = false) List relatedIds, +// @RequestParam(value = "schemaId", required = false) List schemaIds, +// @RequestParam(name = "from", required = false) Instant updateFrom, +// @RequestParam(name = "until", required = false) Instant updateUntil, +// Pageable pgbl, +// WebRequest wr, +// HttpServletResponse hsr, +// UriComponentsBuilder ucb +// ) { +// LOG.trace("Performing getRecords({}, {}, {}, {}).", relatedIds, schemaIds, updateFrom, updateUntil); +// if (id != null) { +// return getAllVersions(id, pgbl); +// } +// // Search for resource type of MetadataSchemaRecord +// Specification spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResource.RESOURCE_TYPE)); +// // Add authentication if enabled +// if (metadataConfig.isAuthEnabled()) { +// boolean isAdmin; +// isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); +// // Add authorization for non administrators +// if (!isAdmin) { +// List authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); +// if (authorizationIdentities != null) { +// LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); +// Specification permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); +// spec = spec.and(permissionSpec); +// } else { +// LOG.trace("No permission information provided. Skip creating permission specification."); +// } +// } +// } +// List allRelatedIdentifiersSchema = new ArrayList<>(); +// List allRelatedIdentifiersResource = new ArrayList<>(); +// +//// File file = new File(new URIoa) +// if (schemaIds != null) { +// for (String schemaId : schemaIds) { +// MetadataSchemaRecord currentSchemaRecord; +// try { +// currentSchemaRecord = MetadataRecordUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); +// // Test for internal URI -> Transform to global URI. +// if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { +// ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); +// currentSchemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); +// } +// allRelatedIdentifiersSchema.add(currentSchemaRecord.getSchemaDocumentUri()); +// } catch (Exception rnfe) { +// // schemaID not found set version to 1 +// currentSchemaRecord = new MetadataSchemaRecord(); +// currentSchemaRecord.setSchemaVersion(1l); +// allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); +// } +// for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { +// MetadataSchemaRecord schemaRecord = MetadataRecordUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); +// // Test for internal URI -> Transform to global URI. +// if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { +// ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); +// schemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); +// } +// allRelatedIdentifiersSchema.add(schemaRecord.getSchemaDocumentUri()); +// } +// } +// Specification schemaSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersSchema.toArray(new String[allRelatedIdentifiersSchema.size()])); +// spec = spec.and(schemaSpecification); +// } +// if (relatedIds != null) { +// allRelatedIdentifiersResource.addAll(relatedIds); +// Specification relResourceSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersResource.toArray(new String[allRelatedIdentifiersResource.size()])); +// spec = spec.and(relResourceSpecification); +// } +// if ((updateFrom != null) || (updateUntil != null)) { +// spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); +// } +// +// // Hide revoked and gone data resources. +// DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; +// List stateList = Arrays.asList(states); +// spec = spec.and(StateSpecification.toSpecification(stateList)); +// +// if (LOG.isTraceEnabled()) { +// Page records = dataResourceDao.findAll(pgbl); +// LOG.trace("List all data resources..."); +// LOG.trace("-----------------------------------------------"); +// for (DataResource item : records.getContent()) { +// LOG.trace("- '{}'", item); +// } +// LOG.trace("-----------------------------------------------"); +// LOG.trace("Specification: '{}'", spec); +// } +// LOG.debug("Performing query for records."); +// Page records = dataResourceDao.findAll(spec, pgbl); +// +// LOG.trace("Transforming Dataresource to DataResource"); +// List recordList = records.getContent(); +// List metadataList = new ArrayList<>(); +// recordList.forEach(metadataRecord -> { +// DataResource item = MetadataRecordUtil.migrateToDataResource(metadataConfig, metadataRecord, false); +// MetadataRecordUtil.fixMetadataDocumentUri(item); +// metadataList.add(item); +// }); +// +// String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); +// +// return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); +// } +// +// @Override +// public ResponseEntity updateRecord( +// @PathVariable("id") String id, +// @RequestPart(name = "record", required = false) MultipartFile metadataRecord, +// @RequestPart(name = "document", required = false) final MultipartFile document, +// WebRequest request, +// HttpServletResponse response, +// UriComponentsBuilder uriBuilder +// ) { +// LOG.trace("Performing updateRecord({}, {}, {}).", id, metadataRecord, "#document"); +// UnaryOperator getById; +// getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); +// String eTag = ControllerUtils.getEtagFromHeader(request); +// DataResource updateDataResource = MetadataRecordUtil.updateDataResource(metadataConfig, id, eTag, metadataRecord, document, getById); +// +// LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); +// String etag = updateDataResource.getEtag(); +// MetadataRecordUtil.fixMetadataDocumentUri(updateDataResource); +// +// URI locationUri; +// locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(updateDataResource.getId(), updateDataResource.getRecordVersion(), null, null)).toUri(); +// +// LOG.trace("Sending UPDATE event."); +// messagingService.orElse(new LogfileMessagingService()). +// send(MetadataResourceMessage.factoryUpdateMetadataMessage(updateDataResource, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); +// +// return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updateDataResource); +// } +// +// @Override +// public ResponseEntity deleteRecord( +// @PathVariable(value = "id") String id, +// WebRequest wr, +// HttpServletResponse hsr +// ) { +// LOG.trace("Performing deleteRecord({}).", id); +// UnaryOperator getById; +// getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, wr, hsr)).toString(); +// +// String eTag = ControllerUtils.getEtagFromHeader(wr); +// MetadataRecordUtil.deleteDataResource(metadataConfig, id, eTag, getById); +// +// return new ResponseEntity<>(HttpStatus.NO_CONTENT); +// } +// +// @Override +// public void contribute(Info.Builder builder) { +// LOG.trace("Check for MetadataRepo actuator information..."); +// +// URL basePath = metadataConfig.getBasepath(); +// Map details = ActuatorUtil.testDirectory(basePath); +// +// if (!details.isEmpty()) { +// details.put("No of metadata documents", Long.toString(MetadataRecordUtil.getNoOfDocuments())); +// builder.withDetail("metadataRepo", details); +// } +// } +// +// @Bean +// public RestTemplate restTemplate() { +// return new RestTemplate(); +// } +//} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index f390c2df..e695f41b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -24,7 +24,6 @@ import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; -import edu.kit.datamanager.metastore2.web.ISchemaRegistryController; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; @@ -72,6 +71,7 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import edu.kit.datamanager.metastore2.web.ISchemaRegistryControllerV2; /** * Controller for schema documents. @@ -80,9 +80,9 @@ @RequestMapping(value = "/api/v2/schemas") @Tag(name = "Schema Registry") @Schema(description = "Schema Registry") -public class SchemaRegistryControllerImpl_v2 implements ISchemaRegistryController { +public class SchemaRegistryControllerImplV2 implements ISchemaRegistryControllerV2 { - private static final Logger LOG = LoggerFactory.getLogger(SchemaRegistryControllerImpl_v2.class); + private static final Logger LOG = LoggerFactory.getLogger(SchemaRegistryControllerImplV2.class); private final ApplicationProperties applicationProperties; @@ -97,12 +97,13 @@ public class SchemaRegistryControllerImpl_v2 implements ISchemaRegistryControlle * @param schemaConfig Configuration for metadata documents repository. * @param dataResourceDao DAO for data resources. */ - public SchemaRegistryControllerImpl_v2(ApplicationProperties applicationProperties, + public SchemaRegistryControllerImplV2(ApplicationProperties applicationProperties, MetastoreConfiguration schemaConfig, IDataResourceDao dataResourceDao) { this.applicationProperties = applicationProperties; this.schemaConfig = schemaConfig; this.dataResourceDao = dataResourceDao; + DataResourceRecordUtil.setDataResourceDao(dataResourceDao); LOG.info("------------------------------------------------------"); LOG.info("------{}", schemaConfig); LOG.info("------------------------------------------------------"); @@ -125,7 +126,7 @@ public ResponseEntity createRecord( LOG.trace("Schema record successfully persisted."); URI locationUri; - locationUri = SchemaRegistryControllerImpl_v2.getSchemaDocumentUri(dataResourceRecord); + locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(dataResourceRecord); LOG.warn("location uri " + locationUri); return ResponseEntity.created(locationUri).eTag("\"" + etag + "\"").body(dataResourceRecord); } @@ -225,7 +226,7 @@ public ResponseEntity validate(@PathVariable(value = "schemaId") String schemaId WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing validate({}, {}, {}).", schemaId, version, "#document"); - DataResourceRecordUtil.validateMetadataDocument(schemaConfig, document, schemaId, version); +// DataResourceRecordUtil.validateMetadataDocument(schemaConfig, document, schemaId, version); LOG.trace("Metadata document validation succeeded. Returning HTTP NOT_CONTENT."); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } @@ -357,6 +358,6 @@ private Specification addAuthenticationSpecification(Specification * @return URI for accessing schema document. */ public static final URI getSchemaDocumentUri(DataResource dataResourceRecord) { - return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImpl_v2.class).getSchemaDocumentById(dataResourceRecord.getId(), Long.parseLong(dataResourceRecord.getVersion()), null, null)).toUri(); + return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(dataResourceRecord.getId(), Long.parseLong(dataResourceRecord.getVersion()), null, null)).toUri(); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java index 05ff9038..d9b0046c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java @@ -217,6 +217,46 @@ public void setUp() throws Exception { } } + @Test + public void testCreateRecordWithoutAuthentication() throws Exception { + String schemaId = "no_authentication"; + MetadataSchemaRecord record = new MetadataSchemaRecord(); + record.setSchemaId(schemaId); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + file(recordFile). + file(schemaFile)). + // Test with no authentication +// header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isUnauthorized()); + } + + @Test + public void testCreateRecordAsGuestOnly() throws Exception { + String schemaId = "guest_authentication"; + MetadataSchemaRecord record = new MetadataSchemaRecord(); + record.setSchemaId(schemaId); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + file(recordFile). + file(schemaFile). + // Test with guest rights only + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()). + andExpect(status().isUnauthorized()); + } + @Test public void testAccessRecordListAdmin() throws Exception { ObjectMapper mapper = new ObjectMapper(); From 43feb2f6abb3a67ace8b45ab70a03b2508328068 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 21 Feb 2024 15:53:50 +0100 Subject: [PATCH 005/181] First step to createSchema --- .../util/DataResourceRecordUtil.java | 64 ++++++++++++------- .../web/ISchemaRegistryControllerV2.java | 14 ++-- .../impl/SchemaRegistryControllerImpl.java | 4 +- .../impl/SchemaRegistryControllerImplV2.java | 17 +++-- ...erTestAccessWithAuthenticationEnabled.java | 6 +- 5 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index c9b17bba..f60584c3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -163,19 +163,15 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio LOG.error(message); throw new BadArgumentException(message); } + metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(metadataRecord.getId())); // Check if id is lower case and URL encodable. + // and save as alternate identifier. (In case of + // upper letters in both versions (with and without + // upper letters) DataResourceRecordUtil.check4validId(metadataRecord); - // Create schema record - SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(metadataRecord.getId()); - if (!metadataRecord.getFormats().isEmpty()) { - String mimeType = metadataRecord.getFormats().iterator().next().toLowerCase(); - if (mimeType.contains("json")) { - schemaRecord.setType(JSON); - } else if (mimeType.contains("xml")) { - schemaRecord.setType(XML); - } - } + + // Add resource type for schema if not already defined + DataResourceRecordUtil.check4validResourceType(metadataRecord); // End of parameter checks // validate schema document / determine type if not given validateMetadataSchemaDocument(applicationProperties, metadataRecord, document); @@ -185,30 +181,40 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio if (document.getContentType() != null) { LOG.trace("Set mimetype determined from document: '{}'", document.getContentType()); metadataRecord.getFormats().add(document.getContentType()); + } + } + // Create schema record + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(metadataRecord.getId()); + if (!metadataRecord.getFormats().isEmpty()) { + for (String mimetype : metadataRecord.getFormats()) { + mimetype = mimetype.toLowerCase(); + if (mimetype.contains("json")) { + schemaRecord.setType(JSON); + break; } else { - LOG.trace("Set mimetype according to type '{}'.", schemaRecord.getType()); - switch (schemaRecord.getType()) { - case JSON: - metadataRecord.getFormats().add(MediaType.APPLICATION_JSON_VALUE); + if (mimetype.contains("xml")) { + schemaRecord.setType(XML); break; - case XML: - metadataRecord.getFormats().add(MediaType.APPLICATION_XML_VALUE); - break; - default: - throw new BadArgumentException("Please provide mimetype for type '" + schemaRecord.getType() + "'"); } } } + } else { + throw new BadArgumentException("Please provide format for data resource '" + schemaRecord.getSchemaId() + "'"); + } + metadataRecord.setVersion(Long.toString(1)); // create record. DataResource dataResource = metadataRecord; DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); // store document ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); + schemaRecord.setVersion(applicationProperties.getAuditService().getCurrentVersion(dataResource.getId())); schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); schemaRecord.setDocumentHash(contentInformation.getHash()); MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); + // Settings for OAI PMH if (MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType())) { try { @@ -1338,11 +1344,21 @@ public static final void check4validSchemaId(DataResource metadataRecord) { check4validId(metadataRecord); String id = metadataRecord.getId(); String lowerCaseId = id.toLowerCase(); - // schema id should be lower case due to elasticsearch if (!lowerCaseId.equals(id)) { metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(id)); - metadataRecord.setId(lowerCaseId); } + // schema id should be lower case due to elasticsearch + metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); + } + + /** + * Overwrite setting for resource type with + * "Schema" and type "MODEL". + * + * @param metadataRecord Datacite Record. + */ + public static final void check4validResourceType(DataResource metadataRecord) { + metadataRecord.setResourceType(ResourceType.createResourceType("Schema", ResourceType.TYPE_GENERAL.MODEL)); } public static final void check4validId(DataResource metadataRecord) { @@ -1449,10 +1465,10 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu recordNotAvailable = dataResourceRecord == null || dataResourceRecord.isEmpty(); documentNotAvailable = document == null || document.isEmpty(); String message = null; - if (bothRequired && !recordNotAvailable && !documentNotAvailable) { + if (bothRequired && (recordNotAvailable || documentNotAvailable)) { message = "No data resource record and/or metadata document provided. Returning HTTP BAD_REQUEST."; } else { - if (recordNotAvailable && documentNotAvailable) { + if (!bothRequired && recordNotAvailable && documentNotAvailable) { message = "Neither metadata record nor metadata document provided."; } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java index c0c0c6ec..643a76d8 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java @@ -59,7 +59,9 @@ @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) public interface ISchemaRegistryControllerV2 extends InfoContributor { - @Operation(summary = "Register a schema document and its record.", description = "This endpoint allows to register a schema document and its record. " + @Operation(operationId = "createSchema", + summary = "Register a schema document and its record.", + description = "This endpoint allows to register a schema document and its (datacite) record. " + "The record must contain at least an unique identifier (schemaId) and the type of the schema (type).", responses = { @ApiResponse(responseCode = "201", description = "Created is returned only if the record has been validated, persisted and the document was successfully validated and stored.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @@ -67,14 +69,16 @@ public interface ISchemaRegistryControllerV2 extends InfoContributor { @ApiResponse(responseCode = "409", description = "A Conflict is returned, if there is already a record for the provided schema id.")}) @RequestMapping(value = {"", "/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody - public ResponseEntity createRecord( + public ResponseEntity createRecord( @Parameter(description = "Json representation of the schema record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile schemaRecord, @Parameter(description = "The metadata schema document associated with the record.", required = true) @RequestPart(name = "schema", required = true) final MultipartFile document, final HttpServletRequest request, final HttpServletResponse response, final UriComponentsBuilder uriBuilder); - @Operation(summary = "Get schema record by schema id (and version).", description = "Obtain is single schema record by its schema id. " + @Operation(operationId = "getDataCiteRecordOfSchema", + summary = "Get schema record by schema id (and version).", + description = "Obtain is single schema record by its schema id. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + "Furthermore, a specific version of the record can be returned by providing a version number as request parameter. If no version is specified, the most recent version is returned.", responses = { @@ -82,7 +86,7 @@ public ResponseEntity createRecord( @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.schema-record+json"}) @ResponseBody - public ResponseEntity getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + public ResponseEntity getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, WebRequest wr, HttpServletResponse hsr); @@ -162,7 +166,7 @@ public ResponseEntity> getRecords( @Parameters({ @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) }) - ResponseEntity updateRecord( + ResponseEntity updateRecord( @Parameter(description = "The schema id.", required = true) @PathVariable("schemaId") final String schemaId, @Parameter(description = "Json representation of the schema record.", required = false) @RequestPart(name = "record", required = false) final MultipartFile schemaRecord, @Parameter(description = "The metadata schema document associated with the record.", required = false) @RequestPart(name = "schema", required = false) final MultipartFile document, diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index f40d708a..9293c641 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -62,6 +62,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -117,7 +118,8 @@ public ResponseEntity createRecord( LOG.trace("Performing createRecord({},....", recordDocument); BiFunction getSchemaDocumentById; getSchemaDocumentById = (schema, version) -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getSchemaDocumentById(schema, version, null, null)).toString(); - + Authentication authentication = AuthenticationHelper.getAuthentication(); + authentication.isAuthenticated(); MetadataSchemaRecord schemaRecord = MetadataSchemaRecordUtil.createMetadataSchemaRecord(schemaConfig, recordDocument, document, getSchemaDocumentById); LOG.trace("Schema record successfully persisted. Returning result."); String etag = schemaRecord.getEtag(); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index e695f41b..69f14535 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -110,7 +110,7 @@ public SchemaRegistryControllerImplV2(ApplicationProperties applicationPropertie } @Override - public ResponseEntity createRecord( + public ResponseEntity createRecord( @RequestPart(name = "record") final MultipartFile recordDocument, @RequestPart(name = "schema") MultipartFile document, HttpServletRequest request, @@ -127,12 +127,12 @@ public ResponseEntity createRecord( LOG.trace("Schema record successfully persisted."); URI locationUri; locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(dataResourceRecord); - LOG.warn("location uri " + locationUri); + LOG.trace("Set locationUri to '{}'", locationUri.toString()); return ResponseEntity.created(locationUri).eTag("\"" + etag + "\"").body(dataResourceRecord); } @Override - public ResponseEntity getRecordById( + public ResponseEntity getRecordById( @PathVariable(value = "schemaId") String schemaId, @RequestParam(value = "version", required = false) Long version, WebRequest wr, @@ -283,7 +283,7 @@ public ResponseEntity> getRecords(@RequestParam(value = "sche } @Override - public ResponseEntity updateRecord(@PathVariable("schemaId") final String schemaId, + public ResponseEntity updateRecord(@PathVariable("schemaId") final String schemaId, @RequestPart(name = "record", required = false) MultipartFile schemaRecord, @RequestPart(name = "schema", required = false) final MultipartFile document, final WebRequest request, final HttpServletResponse response) { @@ -291,16 +291,15 @@ public ResponseEntity updateRecord(@PathVariable("schemaId") final String schema UnaryOperator getById; getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); String eTag = ControllerUtils.getEtagFromHeader(request); - MetadataSchemaRecord updatedSchemaRecord = MetadataSchemaRecordUtil.updateMetadataSchemaRecord(schemaConfig, schemaId, eTag, schemaRecord, document, getById); + DataResource updatedSchemaRecord = DataResourceRecordUtil.updateMetadataSchemaRecord(schemaConfig, schemaId, eTag, schemaRecord, document, getById); - LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); + LOG.trace("DataResource record successfully persisted. Updating document URI and returning result."); String etag = updatedSchemaRecord.getEtag(); - MetadataSchemaRecordUtil.fixSchemaDocumentUri(updatedSchemaRecord, true); // Fix Url for OAI PMH entry - MetadataSchemaRecordUtil.updateMetadataFormat(updatedSchemaRecord); +// MetadataSchemaRecordUtil.updateMetadataFormat(updatedSchemaRecord); URI locationUri; - locationUri = MetadataSchemaRecordUtil.getSchemaDocumentUri(updatedSchemaRecord); + locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(updatedSchemaRecord); LOG.trace("Set locationUri to '{}'", locationUri.toString()); return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updatedSchemaRecord); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java index d9b0046c..2a27b7d8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabled.java @@ -35,6 +35,7 @@ import org.hamcrest.Matchers; import org.javers.core.Javers; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -169,8 +170,8 @@ public void setUp() throws Exception { addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(guestPrincipal, RepoUserRole.GUEST). - addSimpleClaim("email", "thomas.jejkal@kit.edu"). - addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("email", "guest@kit.edu"). + addSimpleClaim("orcid", "0123-4567-89AB-CDEF"). addSimpleClaim("loginFailures", 0). addSimpleClaim("active", true). addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); @@ -238,6 +239,7 @@ public void testCreateRecordWithoutAuthentication() throws Exception { } @Test + @Ignore public void testCreateRecordAsGuestOnly() throws Exception { String schemaId = "guest_authentication"; MetadataSchemaRecord record = new MetadataSchemaRecord(); From a09a4d26931e0281283fd3effd2344ad8783687d Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Fri, 23 Feb 2024 11:25:14 +0100 Subject: [PATCH 006/181] First version handling both versions of createSchema identically. --- .../util/DataResourceRecordUtil.java | 105 ++++++++---------- .../util/MetadataSchemaRecordUtil.java | 26 ++++- 2 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index f60584c3..852dfb1f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -136,6 +136,9 @@ public class DataResourceRecordUtil { private static IUrl2PathDao url2PathDao; + public static final String SCHEMA_SUFFIX = "_Schema"; + public static final String METADATA_SUFFIX = "_Metadata"; + DataResourceRecordUtil() { //Utility class } @@ -163,20 +166,16 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio LOG.error(message); throw new BadArgumentException(message); } - metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(metadataRecord.getId())); // Check if id is lower case and URL encodable. // and save as alternate identifier. (In case of // upper letters in both versions (with and without // upper letters) - DataResourceRecordUtil.check4validId(metadataRecord); - - // Add resource type for schema if not already defined - DataResourceRecordUtil.check4validResourceType(metadataRecord); + DataResourceRecordUtil.check4validSchemaId(metadataRecord); // End of parameter checks // validate schema document / determine type if not given validateMetadataSchemaDocument(applicationProperties, metadataRecord, document); // set internal parameters - if (metadataRecord.getFormats().isEmpty()) { + if (metadataRecord.getResourceType() == null) { LOG.trace("No mimetype set! Try to determine..."); if (document.getContentType() != null) { LOG.trace("Set mimetype determined from document: '{}'", document.getContentType()); @@ -186,23 +185,17 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio // Create schema record SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(metadataRecord.getId()); - if (!metadataRecord.getFormats().isEmpty()) { - for (String mimetype : metadataRecord.getFormats()) { - mimetype = mimetype.toLowerCase(); - if (mimetype.contains("json")) { - schemaRecord.setType(JSON); - break; + String type = metadataRecord.getResourceType().getValue(); + if (type.equals(JSON + SCHEMA_SUFFIX)) { + schemaRecord.setType(JSON); + } else { + if (type.equals(XML + SCHEMA_SUFFIX)) { + schemaRecord.setType(XML); + } else { - if (mimetype.contains("xml")) { - schemaRecord.setType(XML); - break; - } + throw new BadArgumentException("Please provide resource type for data resource '" + schemaRecord.getSchemaId() + "'"); } } - } else { - throw new BadArgumentException("Please provide format for data resource '" + schemaRecord.getSchemaId() + "'"); - } - metadataRecord.setVersion(Long.toString(1)); // create record. DataResource dataResource = metadataRecord; @@ -214,7 +207,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); schemaRecord.setDocumentHash(contentInformation.getHash()); MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); - + // Settings for OAI PMH if (MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType())) { try { @@ -317,7 +310,6 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio // // return migrateToMetadataRecord(applicationProperties, createResource, true); // } - /** * Update a digital object with given metadata record and/or metadata * document. @@ -457,14 +449,14 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application // Find all versions for given id... int pageNo = 0; int pageSize = 10; - int totalNoOfPages; + int totalNoOfPages; Set uris = new HashSet<>(); Pageable pgbl; Page allVersionsOfResource; do { pgbl = PageRequest.of(pageNo, pageSize); - allVersionsOfResource = DataResourceUtils.readAllVersionsOfResource(applicationProperties, id, pgbl); - totalNoOfPages = allVersionsOfResource.getTotalPages(); + allVersionsOfResource = DataResourceUtils.readAllVersionsOfResource(applicationProperties, id, pgbl); + totalNoOfPages = allVersionsOfResource.getTotalPages(); for (DataResource item : allVersionsOfResource.getContent()) { uris.add(SchemaRegistryControllerImplV2.getSchemaDocumentUri(item).toString()); } @@ -475,16 +467,16 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application Optional findOne = dataResourceDao.findOne(spec); // No references to this schema available -> Ready for deletion if (findOne.isEmpty()) { - DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); + DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); List listOfSchemaIds = schemaRecordDao.findBySchemaIdOrderByVersionDesc(id); - for (SchemaRecord item : listOfSchemaIds) { - LOG.trace("Delete entry for path '{}'", item.getSchemaDocumentUri()); - List findByPath = url2PathDao.findByPath(item.getSchemaDocumentUri()); - for (Url2Path entry : findByPath) { - url2PathDao.delete(entry); + for (SchemaRecord item : listOfSchemaIds) { + LOG.trace("Delete entry for path '{}'", item.getSchemaDocumentUri()); + List findByPath = url2PathDao.findByPath(item.getSchemaDocumentUri()); + for (Url2Path entry : findByPath) { + url2PathDao.delete(entry); + } } - } - schemaRecordDao.deleteAll(listOfSchemaIds); + schemaRecordDao.deleteAll(listOfSchemaIds); } } @@ -1344,21 +1336,11 @@ public static final void check4validSchemaId(DataResource metadataRecord) { check4validId(metadataRecord); String id = metadataRecord.getId(); String lowerCaseId = id.toLowerCase(); + // schema id should be lower case due to elasticsearch + metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); if (!lowerCaseId.equals(id)) { metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(id)); } - // schema id should be lower case due to elasticsearch - metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); - } - - /** - * Overwrite setting for resource type with - * "Schema" and type "MODEL". - * - * @param metadataRecord Datacite Record. - */ - public static final void check4validResourceType(DataResource metadataRecord) { - metadataRecord.setResourceType(ResourceType.createResourceType("Schema", ResourceType.TYPE_GENERAL.MODEL)); } public static final void check4validId(DataResource metadataRecord) { @@ -1405,7 +1387,7 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast applicableValidator = getValidatorForRecord(metastoreProperties, dataResource, document); if (applicableValidator == null) { - String message = "No validator found for schema type " + dataResource.getFormats().iterator().next() + ". Returning HTTP UNPROCESSABLE_ENTITY."; + String message = "No validator found for schema type " + dataResource.getResourceType().getValue() + ". Returning HTTP UNPROCESSABLE_ENTITY."; LOG.error(message); throw new UnprocessableEntityException(message); } else { @@ -1434,24 +1416,30 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast private static IValidator getValidatorForRecord(MetastoreConfiguration metastoreProperties, DataResource schemaRecord, byte[] schemaDocument) { IValidator applicableValidator = null; //obtain/guess record type - if (schemaRecord.getFormats().isEmpty()) { + if ((schemaRecord.getResourceType() == null) + || (schemaRecord.getResourceType().getValue() == null)) { String formatDetected = SchemaUtils.guessMimetype(schemaDocument); if (formatDetected == null) { String message = "Unable to detect schema type automatically. Please provide a valid type"; LOG.error(message); throw new UnprocessableEntityException(message); } else { - schemaRecord.getFormats().add(formatDetected); - LOG.debug("Automatically detected mimetype of schema: '{}'.", formatDetected); + String type; + if (formatDetected.contains("json")) { + type = JSON + SCHEMA_SUFFIX; + } else { + type = XML + SCHEMA_SUFFIX; + } + schemaRecord.setResourceType(ResourceType.createResourceType(type, ResourceType.TYPE_GENERAL.MODEL)); + LOG.debug("Automatically detected mimetype of schema: '{}' -> '{}'.", formatDetected, type); } } + String schemaType = schemaRecord.getResourceType().getValue().replace(SCHEMA_SUFFIX, ""); for (IValidator validator : metastoreProperties.getValidators()) { - for (String mimetype : schemaRecord.getFormats()) { - if (validator.supportsMimetype(mimetype)) { - applicableValidator = validator.getInstance(); - LOG.trace("Found validator for schema: '{}'", mimetype); - return applicableValidator; - } + if (validator.supportsSchemaType(MetadataSchemaRecord.SCHEMA_TYPE.valueOf(schemaType))) { + applicableValidator = validator.getInstance(); + LOG.trace("Found validator for schema: '{}'", schemaType); + return applicableValidator; } } return applicableValidator; @@ -1607,7 +1595,6 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu // } // LOG.trace("Metadata document validation succeeded."); // } - /** * Gets SchemaRecord from identifier. Afterwards there should be a clean up. * @@ -1798,9 +1785,9 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app URI schemaDocumentUri = URI.create(info.getContentUri()); Path schemaDocumentPath = Paths.get(schemaDocumentUri); - if (!Files.exists(schemaDocumentPath) || - !Files.isRegularFile(schemaDocumentPath) || - !Files.isReadable(schemaDocumentPath)) { + if (!Files.exists(schemaDocumentPath) + || !Files.isRegularFile(schemaDocumentPath) + || !Files.isReadable(schemaDocumentPath)) { LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 73ce6703..347d0abb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -542,14 +542,36 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecord(RepoBaseConfigu } metadataSchemaRecord.setSchemaId(dataResource.getId()); nano2 = System.nanoTime() / 1000000; + String message = null; + String type = null; try { - MetadataSchemaRecord.SCHEMA_TYPE schemaType = MetadataSchemaRecord.SCHEMA_TYPE.valueOf(dataResource.getFormats().iterator().next()); + type = dataResource.getFormats().iterator().next(); + MetadataSchemaRecord.SCHEMA_TYPE schemaType = MetadataSchemaRecord.SCHEMA_TYPE.valueOf(type); metadataSchemaRecord.setType(schemaType); } catch (Exception ex) { - String message = "Not a schema resource id. Returning HTTP BAD_REQUEST."; + message = "Format '" + type + "' is not a valid schema type. Returning HTTP BAD_REQUEST."; + // Test for new schema version + ResourceType resourceType = dataResource.getResourceType(); + if (resourceType.getTypeGeneral().equals(ResourceType.TYPE_GENERAL.MODEL) && + resourceType.getValue().endsWith(DataResourceRecordUtil.SCHEMA_SUFFIX)) { + type = resourceType.getValue().replace(DataResourceRecordUtil.SCHEMA_SUFFIX, ""); + try { + metadataSchemaRecord.setType(SCHEMA_TYPE.valueOf(type)); + // new + message = null; + } catch (Exception ex2) { + message = "Format '" + type + "' is not a valid schema type. Returning HTTP BAD_REQUEST."; + } + + } + + } finally { + if (message != null) { LOG.error(message); throw new BadArgumentException(message); + } + } nano3 = System.nanoTime() / 1000000; metadataSchemaRecord.setMimeType(dataResource.getTitles().iterator().next().getValue()); nano4 = System.nanoTime() / 1000000; From 35008372175faa527f9aa579de25c78a5174dbeb Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 28 Feb 2024 08:29:05 +0100 Subject: [PATCH 007/181] Refactor tests for schema controller. --- .../test/SchemaRegistryControllerTest.java | 419 ++++++++---------- 1 file changed, 178 insertions(+), 241 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index 11ba5db6..e6aaebc5 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -111,8 +111,13 @@ @TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/schematest/schema"}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) public class SchemaRegistryControllerTest { + + private static final String API_BASE_PATH = "/api/v1"; + private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; + private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; + private static final String API_METADATA_PATH = API_BASE_PATH + "/metadata/"; - private static final String TEMP_DIR_4_ALL = "/tmp/metastore2/schematest/"; + private static final String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/schematest/"; private static final String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; private static final String PID = "anyPID"; private static final ResourceIdentifier.IdentifierType PID_TYPE = ResourceIdentifier.IdentifierType.HANDLE; @@ -196,61 +201,40 @@ public void setUp() throws Exception { @Test public void testCreateSchemaRecord() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + MetadataSchemaRecord record = createMetadataRecord("my_dc"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } @Test public void testCreateSchemaRecordWithAlternateEndpoint() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + MetadataSchemaRecord record = createMetadataRecord("my_dc_alternate"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(ALTERNATE_API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } @Test public void testCreateSchemaRecordWithCapitalLetter() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); String schemaIDWithCapitalLetters = "myFirstTest"; - record.setSchemaId(schemaIDWithCapitalLetters); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + MetadataSchemaRecord record = createMetadataRecord(schemaIDWithCapitalLetters); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataSchemaRecord ms_record = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord.class); @@ -262,21 +246,14 @@ public void testCreateSchemaRecordWithCapitalLetter() throws Exception { @Test public void testCreateRegisterSchemaRecordWithSameIdButCapitalLetter() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - String schemaIDWithCapitalLetters = "myFirstTest"; - record.setSchemaId(schemaIDWithCapitalLetters); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + String schemaIDWithCapitalLetters = "mySecondTest"; + MetadataSchemaRecord record = createMetadataRecord(schemaIDWithCapitalLetters); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataSchemaRecord ms_record = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord.class); @@ -285,92 +262,69 @@ public void testCreateRegisterSchemaRecordWithSameIdButCapitalLetter() throws Ex Assert.assertEquals(record.getSchemaId(), ms_record.getSchemaId()); Assert.assertNotEquals(schemaIDWithCapitalLetters, ms_record.getSchemaId()); - record.setSchemaId("MyFirstTest"); + record.setSchemaId("MySecondTest"); recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); } @Test public void testCreateSchemaRecordWithIdentifierWithoutType() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc_without_type"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("my_dc_without_type"); ResourceIdentifier ri = new ResourceIdentifier(); ri.setIdentifier("any"); record.setPid(ri); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testCreateSchemaRecordWithoutMimeType() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc_2"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + MetadataSchemaRecord record = createMetadataRecord("my_dc_2"); + record.setMimeType(null); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } @Test public void testCreateSchemaRecordWithoutContentType() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc_3"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + MetadataSchemaRecord record = createMetadataRecord("my_dc_3"); + record.setMimeType(null); + record.setType(null); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", null, KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } @Test public void testCreateSchemaRecordWithLocationUri() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc_new"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - Set aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + MetadataSchemaRecord record = createMetadataRecord("my_dc_new"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=1")).andReturn(); String locationUri = result.getResponse().getHeader("Location"); @@ -384,26 +338,20 @@ public void testCreateSchemaRecordWithLocationUri() throws Exception { @Test public void testCreateInvalidSchemaRecord() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId(INVALID_SCHEMA_ID); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord(INVALID_SCHEMA_ID); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testCreateSchemaRecordWithEmptyAclSid() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("my_dc_empty_sid"); Set aclEntries = new HashSet<>(); aclEntries.add(new AclEntry(null, PERMISSION.ADMINISTRATE)); record.setAcl(aclEntries); @@ -412,7 +360,7 @@ public void testCreateSchemaRecordWithEmptyAclSid() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); @@ -426,12 +374,12 @@ public void testCreateInvalidMetadataSchemaRecord() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", wrongTypeJson.getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); String wrongFormatJson = "dcXML"; recordFile = new MockMultipartFile("record", "record.json", "application/json", wrongFormatJson.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); @@ -443,22 +391,19 @@ public void testCreateEmptyMetadataSchemaRecord() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", (byte[]) null); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); recordFile = new MockMultipartFile("record", "record.json", "application/json", " ".getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } // @Test public void testCreateSchemaRecordFromExternal() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("my_dc_from_extern"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -470,56 +415,52 @@ public MockHttpServletRequest postProcessRequest(MockHttpServletRequest mhsr) { } }; - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile).with(remoteAddr("any.external.domain"))).andDo(print()).andExpect(status().isCreated()).andReturn(); } //@Test @ToDo Set external remote address. public void testCreateSchemaRecordUpdateFromExternal() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dcExt"); + MetadataSchemaRecord record = createMetadataRecord("my_dcExt"); record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); record.setMimeType(MediaType.APPLICATION_XML.toString()); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile).with(remoteAddr("any.domain.com"))).andDo(print()).andExpect(status().isCreated()).andReturn(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile).with(remoteAddr("www.google.com"))).andDo(print()).andExpect(status().isCreated()).andReturn(); } @Test public void testCreateSchemaRecordWrongType() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("my_dc"); record.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test public void testCreateSchemaRecordGuessingType() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("my_dc"); + record.setType(null); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); record = mapper.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); @@ -528,15 +469,14 @@ record = mapper.readValue(res.getResponse().getContentAsString(), MetadataSchema @Test public void testCreateSchemaRecordGuessingTypeFails() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("my_dc"); + record.setType(null); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "?".getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); @@ -544,32 +484,26 @@ public void testCreateSchemaRecordGuessingTypeFails() throws Exception { @Test public void testCreateSchemaRecordWithBadSchema() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("bad_schema"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "<>".getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test public void testCreateSchemaRecordWithEmptySchema() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("empty_schema"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "".getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @@ -577,20 +511,17 @@ public void testCreateSchemaRecordWithEmptySchema() throws Exception { @Test public void testCreateSchemaRecordWithoutRecord() throws Exception { MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testCreateSchemaRecordWithoutSchema() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("without_schema"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @@ -598,38 +529,32 @@ public void testCreateSchemaRecordWithoutSchema() throws Exception { public void testCreateSchemaRecordWithBadRecord() throws Exception { ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = new MetadataSchemaRecord(); - //schemaId is missing - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord(null); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testCreateTwoVersionsOfSchemaRecord() throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("my_dc_with_version"); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); + MetadataSchemaRecord record = createMetadataRecord("my_dc_with_version"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataSchemaRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); Assert.assertEquals(result.getSchemaVersion(), Long.valueOf(1l)); // Can't create same resource twice -> Conflict - res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); } @@ -638,7 +563,7 @@ public void testCreateTwoVersionsOfSchemaRecord() throws Exception { public void testGetSchemaRecordByIdWithoutVersion() throws Exception { ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get("/api/v1/schemas/dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); MetadataSchemaRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); Assert.assertNotNull(result); @@ -651,7 +576,7 @@ public void testGetSchemaRecordByIdWithoutVersion() throws Exception { public void testGetSchemaRecordByIdWithVersion() throws Exception { ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get("/api/v1/schemas/dc").param("version", "1").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").param("version", "1").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); MetadataSchemaRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); Assert.assertNotNull(result); @@ -662,7 +587,7 @@ public void testGetSchemaRecordByIdWithVersion() throws Exception { @Test public void testGetSchemaRecordByIdWithInvalidId() throws Exception { ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/cd"). + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "cd"). header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). andDo(print()). andExpect(status().isNotFound()). @@ -674,7 +599,7 @@ public void testGetSchemaRecordByIdWithInvalidId() throws Exception { public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { String schemaId = SCHEMA_ID; ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId). + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). param("version", "13"). header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). andDo(print()). @@ -686,7 +611,7 @@ public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { @Test public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get("/api/v1/schemas").param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); @@ -696,7 +621,7 @@ public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { @Test public void testFindRecordsBySchemaId() throws Exception { ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get("/api/v1/schemas/").param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); @@ -706,7 +631,7 @@ public void testFindRecordsBySchemaId() throws Exception { @Test public void testFindRecordsByMimeType() throws Exception { ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get("/api/v1/schemas/").param("mimeType", MediaType.APPLICATION_XML.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("mimeType", MediaType.APPLICATION_XML.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); @@ -716,7 +641,7 @@ public void testFindRecordsByMimeType() throws Exception { @Test public void testFindRecordsByInvalidMimeType() throws Exception { ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get("/api/v1/schemas/").param("mimeType", "invalid")).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("mimeType", "invalid")).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); @@ -726,7 +651,7 @@ public void testFindRecordsByInvalidMimeType() throws Exception { @Test public void testFindRecordsByUnknownSchemaId() throws Exception { ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get("/api/v1/schemas/"). + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH). param("schemaId", "schema_id_which_is_not_known")). andDo(print()). andExpect(status().isOk()). @@ -740,7 +665,7 @@ public void testFindRecordsByUnknownSchemaId() throws Exception { @Test public void testGetSchemaDocument() throws Exception { ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/dc")).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc")).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA, content); @@ -754,56 +679,56 @@ public void testGetSchemaDocumentWithMissingSchemaFile() throws Exception { URI uri = new URI(contentUri); Files.delete(Paths.get(uri)); - this.mockMvc.perform(get("/api/v1/schemas/dc")).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); + this.mockMvc.perform(get(API_SCHEMA_PATH + "dc")).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); } @Test public void testValidate() throws Exception { ingestSchemaRecord(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test public void testValidateUnknownVersion() throws Exception { ingestSchemaRecord(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate?version=666").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=666").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testValidateKnownVersion() throws Exception { ingestSchemaRecord(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test public void testValidateUnknownSchemaId() throws Exception { ingestSchemaRecord(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + INVALID_SCHEMA_ID + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + INVALID_SCHEMA_ID + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); } @Test public void testValidateWithInvalidDocument() throws Exception { ingestSchemaRecord(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test public void testValidateWithEmptyDocument() throws Exception { ingestSchemaRecord(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testValidateWithoutDocument() throws Exception { - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate")).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate")).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testValidateWithoutValidator() throws Exception { ingestSchemaRecord(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test @@ -815,7 +740,7 @@ public void testValidateWithMissingSchemaFile() throws Exception { URI uri = new URI(contentUri); Files.delete(Paths.get(uri)); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); } // Update only record @@ -825,7 +750,7 @@ public void testUpdateRecord() throws Exception { String newComment = "new comment"; String newLabel = "label changed!"; ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -840,7 +765,7 @@ public void testUpdateRecord() throws Exception { record.setLabel(newLabel); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); @@ -871,7 +796,7 @@ public void testUpdateRecordRemovingLabel() throws Exception { String newComment = "new comment"; String newLabel = "label changed!"; ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -886,7 +811,7 @@ public void testUpdateRecordRemovingLabel() throws Exception { record.setLabel(null); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); @@ -914,7 +839,7 @@ public void testUpdateRecordRemovingLabel() throws Exception { public void testUpdateRecordIgnoreACL() throws Exception { String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -934,7 +859,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { record.setLabel("label changed"); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile). header("If-Match", etag). with(putMultipart())). @@ -968,7 +893,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { String schemaId = "updateMimetypeOfRecord".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -977,7 +902,7 @@ public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { record.setMimeType(MediaType.APPLICATION_JSON.toString()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); // Should not fail as invalid mimetype is not used validating schema! - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).header("If-Match", etag). with(putMultipart())). andDo(print()). @@ -989,7 +914,7 @@ public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { String schemaId = "updateTypeOfRecord".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -998,7 +923,7 @@ public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { record.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); // Should fail due to invalid type! - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile). header("If-Match", etag). with(putMultipart())). @@ -1010,7 +935,7 @@ public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { public void testUpdateRecordWithoutChanges() throws Exception { String schemaId = "updateRecordWithoutChanges".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1018,7 +943,7 @@ public void testUpdateRecordWithoutChanges() throws Exception { MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); @@ -1040,7 +965,7 @@ public void testUpdateRecordWithoutChanges() throws Exception { public void testUpdateRecordAndDocument() throws Exception { String schemaId = "updateRecordAndDocument".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1051,7 +976,7 @@ public void testUpdateRecordAndDocument() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); @@ -1070,7 +995,7 @@ public void testUpdateRecordAndDocument() throws Exception { } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Test also document for update - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA_V2, content); @@ -1081,7 +1006,7 @@ public void testUpdateRecordAndDocument() throws Exception { public void testUpdateRecordAndDocumentWithLicense() throws Exception { String schemaId = "updateRecordAndDocumentWithLicense".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1096,7 +1021,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile). file(schemaFile). header("If-Match", etag). @@ -1123,7 +1048,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Test also document for update - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA_V2, content); @@ -1131,7 +1056,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { record2.setLicenseUri(null); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile). header("If-Match", etag). with(putMultipart())). @@ -1160,7 +1085,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { String schemaId = "updateRecordAndDocumentWithWrongVersion".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1172,7 +1097,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); @@ -1189,12 +1114,12 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Test also document for update - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA_V2, content); // Test also old document - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA, content); } @@ -1203,7 +1128,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { public void testUpdateOnlyDocument() throws Exception { String schemaId = "updateRecordDocumentOnly".toLowerCase(Locale.getDefault()); ingestSchemaRecord(schemaId); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1211,7 +1136,7 @@ public void testUpdateOnlyDocument() throws Exception { MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(schemaFile). header("If-Match", etag). with(putMultipart())). @@ -1233,7 +1158,7 @@ public void testUpdateOnlyDocument() throws Exception { } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Test also document for update - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA_V2, content); @@ -1251,13 +1176,13 @@ public void testUpdateRecordWithSmallChangesInDocument() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.XML_SCHEMA_V1.getBytes()); //// - MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); -// MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); +// MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.XML_SCHEMA_V1_TYPO.getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(schemaFile). header("If-Match", etag). with(putMultipart())). @@ -1284,7 +1209,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); String etag = result.getResponse().getHeader("ETag"); @@ -1295,7 +1220,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String mimeTypeBefore = record1.getMimeType(); record1.setMimeType(MediaType.APPLICATION_JSON.toString()); recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record1).getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); @@ -1316,37 +1241,37 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { @Test public void testUpdateRecordWithoutETag() throws Exception { ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); record.setMimeType(MediaType.APPLICATION_JSON.toString()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). file(recordFile).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionRequired()).andReturn(); } @Test public void testUpdateRecordWithWrongETag() throws Exception { ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag") + "unknown"; String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/dc"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionFailed()).andReturn(); } @Test public void testUpdateRecordWithoutBody() throws Exception { ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); - this.mockMvc.perform(put("/api/v1/schemas/dc").header("If-Match", etag).contentType(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).content("{}")).andDo(print()).andExpect(status().isUnsupportedMediaType()).andReturn(); + this.mockMvc.perform(put(API_SCHEMA_PATH + "dc").header("If-Match", etag).contentType(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).content("{}")).andDo(print()).andExpect(status().isUnsupportedMediaType()).andReturn(); } @Test @@ -1365,14 +1290,14 @@ public void testCreateSchemaRecordWithUpdateWithoutChanges() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", SCHEMA_V3.getBytes()); - MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); MetadataSchemaRecord record1 = mapper.readValue(body, MetadataSchemaRecord.class); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + record.getSchemaId()). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + record.getSchemaId()). file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); @@ -1397,21 +1322,21 @@ public void testDeleteSchemaRecord() throws Exception { ingestSchemaRecord(schemaId); // Get a list of all records - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/"). + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH). header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord[].class).length; - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); String etag = result.getResponse().getHeader("ETag"); - this.mockMvc.perform(delete("/api/v1/schemas/" + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); + this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); // create should return conflict SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(schemaId); @@ -1420,21 +1345,21 @@ public void testDeleteSchemaRecord() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); //delete second time // should be really deleted -> gone - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); etag = result.getResponse().getHeader("ETag"); - this.mockMvc.perform(delete("/api/v1/schemas/" + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); + this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); //try to create after deletion (Should return HTTP GONE) - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isGone()).andReturn(); // List of records should be smaller afterwards - result = this.mockMvc.perform(get("/api/v1/schemas/").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord[].class).length; Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); } @@ -1447,7 +1372,7 @@ public void testGetAllVersionsOfRecord() throws Exception { ingestSchemaWithVersion(schemaId, version); // Get version of record as array // Read all versions - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/").param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); ObjectMapper mapper = new ObjectMapper(); CollectionType mapCollectionType = mapper.getTypeFactory() .constructCollectionType(List.class, MetadataSchemaRecord.class); @@ -1483,7 +1408,7 @@ public void testGetAllVersionsOfRecord() throws Exception { } else { resultMatcher = status().isUnprocessableEntity(); } - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId + "/validate").file("document", xmlDocument)).andDo(print()).andExpect(resultMatcher).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", xmlDocument)).andDo(print()).andExpect(resultMatcher).andReturn(); } } @@ -1505,7 +1430,7 @@ public void testGetAllVersionsOfRecord() throws Exception { } ResultMatcher resultMatcher = status().isNoContent(); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId + "/validate?version=" + document).file("document", xmlDocument)).andDo(print()).andExpect(resultMatcher).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate?version=" + document).file("document", xmlDocument)).andDo(print()).andExpect(resultMatcher).andReturn(); } } @@ -1549,67 +1474,67 @@ public void testIssue52() throws Exception { ingestSchemaWithVersion(schemaId, version); // Test get record with one version // Read all versions - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/").param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); Assert.assertTrue("Reference to '" + COMMENT + version + "' is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/schemas/").param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/schemas/").param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); ingestSchemaWithVersion(schemaId, 2); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/schemas/").param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available (if version > 2) - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/schemas/").param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); String dcSchema = SCHEMA_V1; // Assert.assertEquals(dcMetadata, content); - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()).andReturn(); content = result.getResponse().getContentAsString(); Assert.assertNotEquals(dcSchema, content); Assert.assertEquals("Length must differ!", SCHEMA_V2.length(), content.length()); // check for higher versions which should be not available (if version > 2) - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); - this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); } @Test public void testLandingPage4SchemaUnknownID() throws Exception { - MvcResult andReturn = this.mockMvc.perform(get("/api/v1/schemas/anything") + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + "anything") .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) @@ -1628,7 +1553,7 @@ public void testLandingPage4SchemaWithMetadataDocumentId() throws Exception { ingestSchemaRecord(schemaId); String documentId = createKitMetadataRecord(schemaId); - MvcResult andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + documentId) + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + documentId) .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) @@ -1643,7 +1568,7 @@ public void testLandingPage4SchemaWithMetadataDocumentId() throws Exception { @Test public void testLandingPage4SchemaWrongVersion() throws Exception { - MvcResult andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + SCHEMA_ID) + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + SCHEMA_ID) .queryParam("version", "2") .accept("text/html")) .andDo(print()) @@ -1662,7 +1587,7 @@ public void testLandingPage4Schema() throws Exception { String schemaId = "landingpage"; ingestSchemaWithVersion(schemaId, 1); - MvcResult andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId) + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) @@ -1673,7 +1598,7 @@ public void testLandingPage4Schema() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().isOk()); - andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId) + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) .queryParam("version", "1") .accept("text/html")) .andDo(print()) @@ -1687,7 +1612,7 @@ public void testLandingPage4Schema() throws Exception { .andExpect(status().isOk()); // Ingest a second version... ingestSchemaWithVersion(schemaId, 2); - andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId) + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) .queryParam("version", "2") .accept("text/html")) .andDo(print()) @@ -1699,7 +1624,7 @@ public void testLandingPage4Schema() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().isOk()); - andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId) + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) @@ -1725,7 +1650,7 @@ private void ingestSchemaRecord(String schemaId) throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } @@ -1815,13 +1740,13 @@ private void ingestSchemaWithVersion(String schemaId, long version) throws Excep MvcResult result; if (version > 1) { // Read ETag - result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile). file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); } else { - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } @@ -1834,7 +1759,7 @@ record = mapper.readValue(body, MetadataSchemaRecord.class); } private void ingestNewSchemaRecord(String schemaId, long version) throws Exception { - MvcResult result = this.mockMvc.perform(get("/api/v1/schemas/" + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1843,7 +1768,7 @@ private void ingestNewSchemaRecord(String schemaId, long version) throws Excepti record.setComment(COMMENT + version); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/" + schemaId). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile). header("If-Match", etag). with(putMultipart())). @@ -1851,6 +1776,18 @@ private void ingestNewSchemaRecord(String schemaId, long version) throws Excepti andExpect(status().isOk()). andReturn(); } + + private MetadataSchemaRecord createMetadataRecord(String id) { + MetadataSchemaRecord record = new MetadataSchemaRecord(); + record.setSchemaId(id); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + record.setMimeType(MediaType.APPLICATION_XML.toString()); + Set aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + return record; + } private String createKitMetadataRecord(String schemaId) throws Exception { MetadataRecord record = new MetadataRecord(); @@ -1866,7 +1803,7 @@ private String createKitMetadataRecord(String schemaId) throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); - MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/metadata/"). + MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); From d118c94e3d433ba23678619f007c5c220d18112a Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Fri, 1 Mar 2024 15:29:19 +0100 Subject: [PATCH 008/181] Fix tests for version 2 of schema management --- .../datamanager/metastore2/Application.java | 9 + .../util/DataResourceRecordUtil.java | 5 +- .../test/SchemaRegistryControllerTestV2.java | 2104 +++++++++++++++++ 3 files changed, 2116 insertions(+), 2 deletions(-) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/Application.java b/src/main/java/edu/kit/datamanager/metastore2/Application.java index 39e7408a..ae24f1f1 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/Application.java +++ b/src/main/java/edu/kit/datamanager/metastore2/Application.java @@ -28,12 +28,14 @@ import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.validation.IValidator; import edu.kit.datamanager.repo.configuration.DateBasedStorageProperties; import edu.kit.datamanager.repo.configuration.IdBasedStorageProperties; import edu.kit.datamanager.repo.configuration.StorageServiceProperties; +import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.service.IContentInformationService; @@ -105,6 +107,8 @@ public class Application { @Autowired private IDataRecordDao dataRecordDao; @Autowired + private IDataResourceDao dataResourceDao; + @Autowired private IUrl2PathDao url2PathDao; @Autowired private IMetadataFormatDao metadataFormatDao; @@ -273,6 +277,11 @@ public MetastoreConfiguration schemaConfig() { MetadataSchemaRecordUtil.setMetadataFormatDao(metadataFormatDao); MetadataSchemaRecordUtil.setUrl2PathDao(url2PathDao); MetadataSchemaRecordUtil.setDataRecordDao(dataRecordDao); + DataResourceRecordUtil.setDataRecordDao(dataRecordDao); + DataResourceRecordUtil.setDataResourceDao(dataResourceDao); + DataResourceRecordUtil.setMetadataFormatDao(metadataFormatDao); + DataResourceRecordUtil.setSchemaRecordDao(schemaRecordDao); + DataResourceRecordUtil.setSchemaConfig(rbc); fixBasePath(rbc); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 852dfb1f..0135da3a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -1337,10 +1337,11 @@ public static final void check4validSchemaId(DataResource metadataRecord) { String id = metadataRecord.getId(); String lowerCaseId = id.toLowerCase(); // schema id should be lower case due to elasticsearch - metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); + // alternate identifier is used to set id to a given id. + metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); if (!lowerCaseId.equals(id)) { metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(id)); - } + } } public static final void check4validId(DataResource metadataRecord) { diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java new file mode 100644 index 00000000..3c4ec757 --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -0,0 +1,2104 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import edu.kit.datamanager.entities.Identifier; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtilTest; +import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.Agent; +import edu.kit.datamanager.repo.domain.ContentInformation; +import edu.kit.datamanager.repo.domain.Contributor; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.Date; +import edu.kit.datamanager.repo.domain.Description; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.repo.domain.Scheme; +import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Calendar; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import org.junit.Assert; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + * + * @author Torridity + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41429"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_schema_v2_xsd;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/schematest/schema"}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class SchemaRegistryControllerTestV2 { + + private static final String API_BASE_PATH = "/api/v2"; + private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; + private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; + private static final String API_METADATA_PATH = API_BASE_PATH + "/metadata/"; + + private static final String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/schematest/"; + private static final String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private static final String PID = "anyPID"; + private static final ResourceIdentifier.IdentifierType PID_TYPE = ResourceIdentifier.IdentifierType.HANDLE; + private static final String SCHEMA_ID = "dc"; + private static final String INVALID_SCHEMA_ID = "invalid/my_dc"; + private static final String LABEL = "any unique label for test"; + private static final String DEFINITION = "any unique definition for test"; + private static final String COMMENT = "any unique comment for test"; + private static final String KIT_SCHEMA = CreateSchemaUtil.KIT_SCHEMA; + private static final String APACHE_2_LICENSE = "https://spdx.org/licenses/Apache-2.0"; + private static final String KIT_SCHEMA_V2 = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " "; + + private static final String KIT_DOCUMENT = CreateSchemaUtil.KIT_DOCUMENT; + private static final String INVALID_KIT_DOCUMENT = CreateSchemaUtil.KIT_DOCUMENT_INVALID_1; + private static final String SCHEMA_V1 = CreateSchemaUtil.XML_SCHEMA_V1; + private static final String SCHEMA_V2 = CreateSchemaUtil.XML_SCHEMA_V2; + private static final String SCHEMA_V3 = CreateSchemaUtil.XML_SCHEMA_V3; + private static final String XML_DOCUMENT_V1 = CreateSchemaUtil.XML_DOCUMENT_V1; + private static final String XML_DOCUMENT_V2 = CreateSchemaUtil.XML_DOCUMENT_V2; + private static final String XML_DOCUMENT_V3 = CreateSchemaUtil.XML_DOCUMENT_V3; + private static final String JSON_DOCUMENT = "{\"title\":\"any string\",\"date\": \"2020-10-16\"}"; + private static final String RELATED_RESOURCE_STRING = "anyResourceId"; + private static final ResourceIdentifier RELATED_RESOURCE = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING); + + private MockMvc mockMvc; + @Autowired + private WebApplicationContext context; + + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + + @Autowired + private MetastoreConfiguration schemaConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + @Before + public void setUp() throws Exception { + + System.out.println("------SchemaRegistryControllerTest--------------------"); + System.out.println("------" + this.schemaConfig); + System.out.println("------------------------------------------------------"); + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + schemaRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + try { + try (Stream walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation)) + .build(); + } + + @Test + public void testCreateSchemaRecord() throws Exception { + DataResource record = createDataResource("my_dc"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithAlternateEndpoint() throws Exception { + DataResource record = createDataResource("my_dc_alternate"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(ALTERNATE_API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithCapitalLetter() throws Exception { + String schemaIDWithCapitalLetters = "myFirstTest"; + DataResource record = createDataResource(schemaIDWithCapitalLetters); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + DataResource ms_record = mapper.readValue(result.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(record.getResourceType().getValue(), ms_record.getResourceType().getValue()); + Assert.assertEquals(record.getResourceType().getTypeGeneral(), ms_record.getResourceType().getTypeGeneral()); + Assert.assertEquals(record.getFormats(), ms_record.getFormats()); + Assert.assertEquals(record.getId(), ms_record.getId()); + Assert.assertNotEquals(schemaIDWithCapitalLetters, ms_record.getId()); + } + + @Test + public void testCreateRegisterSchemaRecordWithSameIdButCapitalLetter() throws Exception { + String schemaIDWithCapitalLetters = "mySecondTest"; + DataResource record = createDataResource(schemaIDWithCapitalLetters); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + DataResource ms_record = mapper.readValue(result.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(record.getResourceType().getValue(), ms_record.getResourceType().getValue()); + Assert.assertEquals(record.getResourceType().getTypeGeneral(), ms_record.getResourceType().getTypeGeneral()); + Assert.assertEquals(record.getFormats(), ms_record.getFormats()); + Assert.assertEquals(record.getId().toLowerCase(), ms_record.getId()); + Assert.assertNotEquals(schemaIDWithCapitalLetters, ms_record.getId()); + + record.setId("MySecondTest"); + recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); + } + + //@Test + public void testCreateSchemaRecordWithIdentifierWithoutType() throws Exception { + DataResource record = createDataResource("my_dc_without_type"); + Identifier ri = Identifier.factoryIdentifier("any", null); + record.getAlternateIdentifiers().add(ri); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithoutMimeType() throws Exception { + DataResource record = createDataResource("my_dc_2"); + record.getFormats().clear(); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithoutContentType() throws Exception { + DataResource record = createDataResource("my_dc_3"); + record.getFormats().clear(); + record.setResourceType(null); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", null, KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithLocationUri() throws Exception { + DataResource record = createDataResource("my_dc_new"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=1")).andReturn(); + String locationUri = result.getResponse().getHeader("Location"); + String content = result.getResponse().getContentAsString(); + + MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content2 = result2.getResponse().getContentAsString(); + + validateDataResources(mapper.readValue(content, DataResource.class), mapper.readValue(content2, DataResource.class)); + Assert.assertEquals(content, content2); + } + + @Test + public void testCreateInvalidSchemaRecord() throws Exception { + DataResource record = createDataResource(INVALID_SCHEMA_ID); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithEmptyAclSid() throws Exception { + DataResource record = createDataResource("my_dc_empty_sid"); + Set aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry(null, PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + Assert.assertTrue(res.getResponse().getContentAsString().contains("Subject ID of ACL entry must not be null.")); + } + + @Test + public void testCreateInvalidMetadataSchemaRecord() throws Exception { + String wrongTypeJson = "{\"schemaId\":\"dc\",\"type\":\"Something totally strange!\"}"; + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", wrongTypeJson.getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + String wrongFormatJson = "dcXML"; + recordFile = new MockMultipartFile("record", "record.json", "application/json", wrongFormatJson.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + } + + @Test + public void testCreateEmptyMetadataSchemaRecord() throws Exception { + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", (byte[]) null); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + recordFile = new MockMultipartFile("record", "record.json", "application/json", " ".getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + // @Test + public void testCreateSchemaRecordFromExternal() throws Exception { + DataResource record = createDataResource("my_dc_from_extern"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + RequestPostProcessor rpp = new RequestPostProcessor() { + @Override + public MockHttpServletRequest postProcessRequest(MockHttpServletRequest mhsr) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + }; + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile).with(remoteAddr("any.external.domain"))).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + //@Test @ToDo Set external remote address. + public void testCreateSchemaRecordUpdateFromExternal() throws Exception { + DataResource record = createDataResource("my_dcExt"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile).with(remoteAddr("any.domain.com"))).andDo(print()).andExpect(status().isCreated()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile).with(remoteAddr("www.google.com"))).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWrongType() throws Exception { + DataResource record = createDataResource("my_dc"); + record.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.JSON + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testCreateSchemaRecordGuessingType() throws Exception { + DataResource record = createDataResource("my_dc"); + record.setResourceType(null); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + record = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(MetadataSchemaRecord.SCHEMA_TYPE.XML + "_Schema", record.getResourceType().getValue()); + } + + @Test + public void testCreateSchemaRecordGuessingTypeFails() throws Exception { + DataResource record = createDataResource("my_dc"); + record.setResourceType(null); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "?".getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + + } + + @Test + public void testCreateSchemaRecordWithBadSchema() throws Exception { + DataResource record = createDataResource("bad_schema"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "<>".getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithEmptySchema() throws Exception { + DataResource record = createDataResource("empty_schema"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "".getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithoutRecord() throws Exception { + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithoutSchema() throws Exception { + DataResource record = createDataResource("without_schema"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithBadRecord() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + DataResource record = createDataResource(null); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateTwoVersionsOfSchemaRecord() throws Exception { + DataResource record = createDataResource("my_dc_with_version"); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + + MetadataSchemaRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); + Assert.assertEquals(result.getSchemaVersion(), Long.valueOf(1l)); + // Can't create same resource twice -> Conflict + res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); + } + + @Test + public void testGetSchemaRecordByIdWithoutVersion() throws Exception { + ingestSchemaRecord(); + + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataSchemaRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); + Assert.assertNotNull(result); + Assert.assertEquals(SCHEMA_ID, result.getSchemaId()); + //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema + Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); + } + + @Test + public void testGetSchemaRecordByIdWithVersion() throws Exception { + ingestSchemaRecord(); + + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").param("version", "1").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataSchemaRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); + Assert.assertNotNull(result); + Assert.assertEquals(SCHEMA_ID, result.getSchemaId()); + Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); + } + + @Test + public void testGetSchemaRecordByIdWithInvalidId() throws Exception { + ingestSchemaRecord(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "cd"). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + Assert.assertTrue("Try to access invalid schema ID!", result.getResponse().getContentAsString().contains("Schema document with ID 'cd' doesn't exist!")); + } + + @Test + public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { + String schemaId = SCHEMA_ID; + ingestSchemaRecord(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + param("version", "13"). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + Assert.assertTrue("Try to access invalid version!", result.getResponse().getContentAsString().contains("Version '13' of ID '" + schemaId + "' doesn't exist!")); + } + + @Test + public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { + ingestSchemaRecord(); + MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + + Assert.assertTrue(result.length > 0); + } + + @Test + public void testFindRecordsBySchemaId() throws Exception { + ingestSchemaRecord(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + + Assert.assertTrue(result.length > 0); + } + + @Test + public void testFindRecordsByMimeType() throws Exception { + ingestSchemaRecord(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("mimeType", MediaType.APPLICATION_XML.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + + Assert.assertEquals(1, result.length); + } + + @Test + public void testFindRecordsByInvalidMimeType() throws Exception { + ingestSchemaRecord(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("mimeType", "invalid")).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindRecordsByUnknownSchemaId() throws Exception { + ingestSchemaRecord(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("schemaId", "schema_id_which_is_not_known")). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testGetSchemaDocument() throws Exception { + ingestSchemaRecord(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc")).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + Assert.assertEquals(KIT_SCHEMA, content); + } + + @Test + public void testGetSchemaDocumentWithMissingSchemaFile() throws Exception { + ingestSchemaRecord(); + String contentUri = contentInformationDao.findAll(PageRequest.of(0, 2)).getContent().get(0).getContentUri(); + //delete schema file + URI uri = new URI(contentUri); + Files.delete(Paths.get(uri)); + + this.mockMvc.perform(get(API_SCHEMA_PATH + "dc")).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); + } + + @Test + public void testValidate() throws Exception { + + ingestSchemaRecord(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + } + + @Test + public void testValidateUnknownVersion() throws Exception { + ingestSchemaRecord(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=666").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testValidateKnownVersion() throws Exception { + ingestSchemaRecord(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + } + + @Test + public void testValidateUnknownSchemaId() throws Exception { + ingestSchemaRecord(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + INVALID_SCHEMA_ID + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); + } + + @Test + public void testValidateWithInvalidDocument() throws Exception { + ingestSchemaRecord(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testValidateWithEmptyDocument() throws Exception { + ingestSchemaRecord(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testValidateWithoutDocument() throws Exception { + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate")).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testValidateWithoutValidator() throws Exception { + ingestSchemaRecord(); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testValidateWithMissingSchemaFile() throws Exception { + ingestSchemaRecord(); + // Get location of schema file. + String contentUri = contentInformationDao.findAll(PageRequest.of(0, 2)).getContent().get(0).getContentUri(); + //delete schema file + URI uri = new URI(contentUri); + Files.delete(Paths.get(uri)); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); + } + + // Update only record + @Test + public void testUpdateRecord() throws Exception { + String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); + String newComment = "new comment"; + String newLabel = "label changed!"; + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + String mimeTypeBefore = record.getMimeType(); + Assert.assertEquals(DEFINITION, record.getDefinition()); + Assert.assertEquals(LABEL, record.getLabel()); + Assert.assertEquals(COMMENT, record.getComment()); + record.setDefinition(null); + record.setComment(newComment); + record.setLabel(newLabel); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type is not allowed to be changed. + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + // Version shouldn't be updated + Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + Assert.assertEquals("Check label: ", record.getLabel(), record2.getLabel()); + Assert.assertEquals("Check comment: ", record.getComment(), record2.getComment()); + Assert.assertEquals("Check definition: ", record.getDefinition(), record2.getDefinition()); + Assert.assertEquals("Check label: ", newLabel, record2.getLabel()); + Assert.assertEquals("Check comment: ", newComment, record2.getComment()); + Assert.assertNull("Check definition for 'null'", record2.getDefinition()); + } + + // Update only record + @Test + public void testUpdateRecordRemovingLabel() throws Exception { + String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); + String newComment = "new comment"; + String newLabel = "label changed!"; + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + String mimeTypeBefore = record.getMimeType(); + Assert.assertEquals(DEFINITION, record.getDefinition()); + Assert.assertEquals(LABEL, record.getLabel()); + Assert.assertEquals(COMMENT, record.getComment()); + record.setDefinition(null); + record.setComment(null); + record.setLabel(null); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type is not allowed to be changed. + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + // Version shouldn't be updated + Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + Assert.assertEquals("Check label: ", record.getLabel(), record2.getLabel()); + Assert.assertEquals("Check comment: ", record.getComment(), record2.getComment()); + Assert.assertEquals("Check definition: ", record.getDefinition(), record2.getComment()); + Assert.assertNull("Check label: ", record2.getLabel()); + Assert.assertNull("Check comment: ", record2.getComment()); + Assert.assertNull("Check definition for 'null'", record2.getDefinition()); + } + + @Test + public void testUpdateRecordIgnoreACL() throws Exception { + String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord oldRecord = mapper.readValue(body, MetadataSchemaRecord.class); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + // Set all ACL to WRITE + for (AclEntry entry : record.getAcl()) { + entry.setPermission(PERMISSION.WRITE); + } + String mimeTypeBefore = record.getMimeType(); + String definitionBefore = record.getDefinition(); + String labelBefore = record.getLabel(); + String commentBefore = record.getComment(); + record.setDefinition(""); + record.setComment("new comment"); + record.setLabel("label changed"); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type is not allowed to be changed. + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + // Version shouldn't be updated + Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(isSameSetOfAclEntries(record.getAcl(), record2.getAcl())); + Assert.assertFalse(isSameSetOfAclEntries(oldRecord.getAcl(), record.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + Assert.assertEquals("Check label: ", record.getLabel(), record2.getLabel()); + Assert.assertEquals("Check comment: ", record.getComment(), record2.getComment()); + Assert.assertNotEquals("Check label: ", labelBefore, record2.getLabel()); + Assert.assertNotEquals("Check comment: ", commentBefore, record2.getComment()); + Assert.assertNull("Check definition for 'null'", record2.getDefinition()); + } + + @Test + public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { + String schemaId = "updateMimetypeOfRecord".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + record.setMimeType(MediaType.APPLICATION_JSON.toString()); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + // Should not fail as invalid mimetype is not used validating schema! + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile).header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")); + } + + @Test + public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { + String schemaId = "updateTypeOfRecord".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + // Should fail due to invalid type! + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isUnprocessableEntity()); + } + + @Test + public void testUpdateRecordWithoutChanges() throws Exception { + String schemaId = "updateRecordWithoutChanges".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertEquals(record.getMimeType(), record2.getMimeType());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + // Version shouldn't be updated + Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testUpdateRecordAndDocument() throws Exception { + String schemaId = "updateRecordAndDocument".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + String mimeTypeBefore = record.getMimeType(); + record.setMimeType(MediaType.APPLICATION_JSON.toString()); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertNull(record2.getLicenseUri()); + Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); + Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); + Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Test also document for update + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + Assert.assertEquals(KIT_SCHEMA_V2, content); + } + + @Test + public void testUpdateRecordAndDocumentWithLicense() throws Exception { + String schemaId = "updateRecordAndDocumentWithLicense".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + String mimeTypeBefore = record.getMimeType(); + record.setMimeType(MediaType.APPLICATION_JSON.toString()); + record.setLicenseUri(APACHE_2_LICENSE); + System.out.println("****************************************************************************************"); + System.out.println("****************************************************************************************"); + System.out.println(mapper.writeValueAsString(record)); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile). + file(schemaFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andReturn(); + body = result.getResponse().getContentAsString(); + etag = result.getResponse().getHeader("ETag"); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertNotNull(record2.getLicenseUri()); + Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); + Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); + Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Test also document for update + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + Assert.assertEquals(KIT_SCHEMA_V2, content); + // Remove license + record2.setLicenseUri(null); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record3 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertNull(record3.getLicenseUri()); + Assert.assertEquals(record2.getMimeType(), record3.getMimeType());//mime type was changed by update + Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); + Assert.assertEquals(record2.getSchemaDocumentUri(), record3.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); + Assert.assertEquals(record2.getSchemaHash(), record3.getSchemaHash()); + Assert.assertEquals(record2.getSchemaId(), record3.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); + } + Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + } + + @Test + public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { + String schemaId = "updateRecordAndDocumentWithWrongVersion".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + record.setSchemaVersion(0l); + String mimeTypeBefore = record.getMimeType(); + record.setMimeType(MediaType.APPLICATION_JSON.toString()); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); + Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals(2l, (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Test also document for update + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + Assert.assertEquals(KIT_SCHEMA_V2, content); + // Test also old document + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); + content = result.getResponse().getContentAsString(); + Assert.assertEquals(KIT_SCHEMA, content); + } + + @Test + public void testUpdateOnlyDocument() throws Exception { + String schemaId = "updateRecordDocumentOnly".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(schemaFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertEquals(record.getMimeType(), record2.getMimeType());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Test also document for update + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + Assert.assertEquals(KIT_SCHEMA_V2, content); + } + + @Test + public void testUpdateRecordWithSmallChangesInDocument() throws Exception { + String schemaId = "updateRecordWithSmallChanges".toLowerCase(Locale.getDefault()); + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(schemaId); + schemaRecord.setVersion(1l); + schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.XML_SCHEMA_V1.getBytes()); +//// + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); +// MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.XML_SCHEMA_V1_TYPO.getBytes()); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(schemaFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andExpect(redirectedUrlPattern("http://*:*/**/" + schemaRecord.getSchemaId() + "?version=*")). + andReturn(); + } + + @Test + public void testUpdateRecordWithoutExplizitGet() throws Exception { + String schemaId = "updateWithoutGet".toLowerCase(Locale.getDefault()); + MetadataSchemaRecord record = new MetadataSchemaRecord(); + record.setSchemaId(schemaId); + record.setMimeType(MediaType.APPLICATION_XML.toString()); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + Set aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + mapper = new ObjectMapper(); + MetadataSchemaRecord record1 = mapper.readValue(body, MetadataSchemaRecord.class); + String mimeTypeBefore = record1.getMimeType(); + record1.setMimeType(MediaType.APPLICATION_JSON.toString()); + recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record1).getBytes()); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); + // Version shouldn't be updated + Assert.assertEquals(record1.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + Assert.assertEquals(record1.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record1.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record1.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record1.getAcl() != null) { + Assert.assertTrue(record1.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record1.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testUpdateRecordWithoutETag() throws Exception { + ingestSchemaRecord(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String body = result.getResponse().getContentAsString(); + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + record.setMimeType(MediaType.APPLICATION_JSON.toString()); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). + file(recordFile).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionRequired()).andReturn(); + } + + @Test + public void testUpdateRecordWithWrongETag() throws Exception { + ingestSchemaRecord(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag") + "unknown"; + String body = result.getResponse().getContentAsString(); + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionFailed()).andReturn(); + } + + @Test + public void testUpdateRecordWithoutBody() throws Exception { + ingestSchemaRecord(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + + this.mockMvc.perform(put(API_SCHEMA_PATH + "dc").header("If-Match", etag).contentType(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).content("{}")).andDo(print()).andExpect(status().isUnsupportedMediaType()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithUpdateWithoutChanges() throws Exception { + // Test with a schema missing schema property. + MetadataSchemaRecord record = new MetadataSchemaRecord(); + record.setSchemaId("updateWithoutChanges_xsd".toLowerCase(Locale.getDefault())); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + record.setMimeType(MediaType.APPLICATION_XML.toString()); + Set aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", SCHEMA_V3.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record1 = mapper.readValue(body, MetadataSchemaRecord.class); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + record.getSchemaId()). + file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); + Assert.assertEquals(record1.getMimeType(), record2.getMimeType());//mime type was changed by update + Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); + // Version shouldn't be updated + Assert.assertEquals(record1.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + Assert.assertEquals(record1.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record1.getSchemaId(), record2.getSchemaId()); + Assert.assertEquals((long) record1.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update + if (record1.getAcl() != null) { + Assert.assertTrue(record1.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record1.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testDeleteSchemaRecord() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String schemaId = "testDelete".toLowerCase(Locale.getDefault()); + ingestSchemaRecord(schemaId); + + // Get a list of all records + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord[].class).length; + + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + + this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); + // create should return conflict + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(schemaId); + schemaRecord.setVersion(1l); + schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); + //delete second time // should be really deleted -> gone + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + etag = result.getResponse().getHeader("ETag"); + this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); + + //try to create after deletion (Should return HTTP GONE) + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isGone()).andReturn(); + + // List of records should be smaller afterwards + result = this.mockMvc.perform(get(API_SCHEMA_PATH).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord[].class).length; + Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); + } + + @Test + public void testGetAllVersionsOfRecord() throws Exception { + String schemaId = "testWithVersion".toLowerCase(Locale.getDefault()); + for (long version = 1; version <= 3; version++) { + // Create a new version + ingestSchemaWithVersion(schemaId, version); + // Get version of record as array + // Read all versions + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, MetadataSchemaRecord.class); + List resultList = mapper.readValue(result.getResponse().getContentAsString(), mapCollectionType); + HashSet versions = new HashSet<>(); + for (MetadataSchemaRecord item : resultList) { + versions.add(item.getSchemaVersion()); + } + Assert.assertEquals(version, versions.size()); + for (long index = 1; index <= version; index++) { + Assert.assertTrue("Test for version: " + index, versions.contains(index)); + } + // Validate document with last version + byte[] xmlDocument = null; + for (int document = 1; document <= version; document++) { + switch (document) { + case 1: + xmlDocument = XML_DOCUMENT_V1.getBytes(); + break; + case 2: + xmlDocument = XML_DOCUMENT_V2.getBytes(); + break; + case 3: + xmlDocument = XML_DOCUMENT_V3.getBytes(); + break; + default: + Assert.assertTrue("Unknown document: '" + document + "'", false); + } + + ResultMatcher resultMatcher = null; + if (version == document) { + resultMatcher = status().isNoContent(); + } else { + resultMatcher = status().isUnprocessableEntity(); + } + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", xmlDocument)).andDo(print()).andExpect(resultMatcher).andReturn(); + + } + } + // Separate test of each document with its specific version + for (int document = 1; document <= 3; document++) { + byte[] xmlDocument = null; + switch (document) { + case 1: + xmlDocument = XML_DOCUMENT_V1.getBytes(); + break; + case 2: + xmlDocument = XML_DOCUMENT_V2.getBytes(); + break; + case 3: + xmlDocument = XML_DOCUMENT_V3.getBytes(); + break; + default: + Assert.assertTrue("Unknown document: '" + document + "'", false); + } + + ResultMatcher resultMatcher = status().isNoContent(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate?version=" + document).file("document", xmlDocument)).andDo(print()).andExpect(resultMatcher).andReturn(); + } + } + + /** + * ************************************************************************** + * Moved tests from MetadataSchemaRecordUtilTest + * ************************************************************************** + */ + /** + * Test of migrateToDataResource method, of class MetadataSchemaRecordUtil. + */ + @Test + public void testMigrateToDataResource() { + System.out.println("migrateToDataResource"); + RepoBaseConfiguration applicationProperties = schemaConfig; + // Test with all possible values PID shouldn't be an URL + MetadataSchemaRecord metadataSchemaRecord = new MetadataSchemaRecordUtilTest().createSchemaRecord(5, 7, 11, 12); + MetadataSchemaRecord expResult = null; + DataResource result = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataSchemaRecord); + expResult = MetadataSchemaRecordUtil.migrateToMetadataSchemaRecord(applicationProperties, result, false); + metadataSchemaRecord.setPid(null); + assertEquals(metadataSchemaRecord, expResult); + // Test with all possible values containing valid PID. + metadataSchemaRecord = new MetadataSchemaRecordUtilTest().createSchemaRecord(5, 7, 11, 12); + ResourceIdentifier correctPid = ResourceIdentifier.factoryResourceIdentifier(PID, PID_TYPE); + metadataSchemaRecord.setPid(correctPid); + result = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataSchemaRecord); + expResult = MetadataSchemaRecordUtil.migrateToMetadataSchemaRecord(applicationProperties, result, false); + assertEquals(metadataSchemaRecord, expResult); + // Test skipping pid + metadataSchemaRecord = new MetadataSchemaRecordUtilTest().createSchemaRecord(5, 7, 10, 11, 12); + result = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataSchemaRecord); + expResult = MetadataSchemaRecordUtil.migrateToMetadataSchemaRecord(applicationProperties, result, false); + assertEquals(metadataSchemaRecord, expResult); + } + + @Test + public void testIssue52() throws Exception { + String schemaId = "test4Issue52".toLowerCase(Locale.getDefault()); + int version = 1; + ingestSchemaWithVersion(schemaId, version); + // Test get record with one version + // Read all versions + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); + Assert.assertTrue("Reference to '" + COMMENT + version + "' is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); + // check for higher versions which should be not available + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + version++; + ingestNewSchemaRecord(schemaId, version); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); + // check for higher versions which should be not available + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + version++; + ingestNewSchemaRecord(schemaId, version); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); + // check for higher versions which should be not available + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + ingestSchemaWithVersion(schemaId, 2); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); + // check for higher versions which should be not available (if version > 2) + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + version++; + ingestNewSchemaRecord(schemaId, version); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); + + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcSchema = SCHEMA_V1; + +// Assert.assertEquals(dcMetadata, content); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()).andReturn(); + content = result.getResponse().getContentAsString(); + + Assert.assertNotEquals(dcSchema, content); + Assert.assertEquals("Length must differ!", SCHEMA_V2.length(), content.length()); + // check for higher versions which should be not available (if version > 2) + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + } + + @Test + public void testLandingPage4SchemaUnknownID() throws Exception { + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + "anything") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=anything&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testLandingPage4SchemaWithMetadataDocumentId() throws Exception { + String schemaId = "metadata_document_id"; + ingestSchemaRecord(schemaId); + String documentId = createKitMetadataRecord(schemaId); + + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + documentId) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + documentId + "&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @Test + public void testLandingPage4SchemaWrongVersion() throws Exception { + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + SCHEMA_ID) + .queryParam("version", "2") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + SCHEMA_ID + "&version=2")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testLandingPage4Schema() throws Exception { + String schemaId = "landingpage"; + ingestSchemaWithVersion(schemaId, 1); + + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + schemaId + "&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) + .queryParam("version", "1") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + schemaId + "&version=1")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + // Ingest a second version... + ingestSchemaWithVersion(schemaId, 2); + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) + .queryParam("version", "2") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + schemaId + "&version=2")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + schemaId + "&version=")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + } + + private void ingestSchemaRecord(String schemaId) throws Exception { + MetadataSchemaRecord schemaRecord = new MetadataSchemaRecord(); + schemaRecord.setSchemaId(schemaId); + schemaRecord.setLabel(LABEL); + schemaRecord.setDefinition(DEFINITION); + schemaRecord.setComment(COMMENT); + schemaRecord.setSchemaVersion(1l); + schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + private void ingestSchemaRecord() throws Exception { + DataResource dataResource = DataResource.factoryNewDataResource(SCHEMA_ID); + dataResource.getCreators().add(Agent.factoryAgent(null, "SELF")); + dataResource.getTitles().add(Title.factoryTitle(MediaType.APPLICATION_XML.toString(), Title.TYPE.OTHER)); + dataResource.setPublisher("SELF"); + Instant now = Instant.now(); + dataResource.setPublicationYear(Integer.toString(Calendar.getInstance().get(Calendar.YEAR))); + dataResource.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.XML + DataResourceRecordUtil.SCHEMA_SUFFIX)); + dataResource.getDates().add(Date.factoryDate(now, Date.DATE_TYPE.CREATED)); + dataResource.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.XML.name()); + dataResource.setLastUpdate(now); + dataResource.setState(DataResource.State.VOLATILE); + dataResource.setVersion("1"); + Set aclEntries = dataResource.getAcls(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + Set descriptions = dataResource.getDescriptions(); + descriptions.add(Description.factoryDescription("other", Description.TYPE.OTHER)); + descriptions.add(Description.factoryDescription("abstract", Description.TYPE.ABSTRACT)); + descriptions.add(Description.factoryDescription("technical info", Description.TYPE.TECHNICAL_INFO)); + descriptions.add(Description.factoryDescription("not used yet", Description.TYPE.METHODS)); + ContentInformation ci = ContentInformation.createContentInformation( + SCHEMA_ID, "schema.xsd", (String[]) null); + ci.setVersion(1); + ci.setFileVersion("1"); + ci.setVersioningService("simple"); + ci.setDepth(1); + ci.setContentUri("file:///tmp/schema_dc.xsd"); + ci.setUploader("SELF"); + ci.setMediaType("text/plain"); + ci.setHash("sha1:400dfe162fd702a619c4d11ddfb3b7550cb9dec7"); + ci.setSize(1097); + + schemaConfig.getDataResourceService().create(dataResource, "SELF"); +// dataResource = dataResourceDao.save(dataResource); + ci = contentInformationDao.save(ci); + + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(dataResource.getId()); + schemaRecord.setVersion(1l); + schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.valueOf(dataResource.getFormats().iterator().next())); + schemaRecord.setSchemaDocumentUri(ci.getContentUri()); + schemaRecord.setDocumentHash(ci.getHash()); + schemaRecordDao.save(schemaRecord); + + File dcFile = new File("/tmp/schema_dc.xsd"); + if (!dcFile.exists()) { + try (FileOutputStream fout = new FileOutputStream(dcFile)) { + fout.write(KIT_SCHEMA.getBytes()); + fout.flush(); + } + } + } + + private void ingestSchemaWithVersion(String schemaId, long version) throws Exception { + MetadataSchemaRecord record = new MetadataSchemaRecord(); + record.setComment(COMMENT + version); + record.setSchemaId(schemaId); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + record.setMimeType(MediaType.APPLICATION_XML.toString()); + Set aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + byte[] schemaContent = null; + switch ((int) version) { + case 1: + schemaContent = SCHEMA_V1.getBytes(); + break; + case 2: + schemaContent = SCHEMA_V2.getBytes(); + break; + case 3: + schemaContent = SCHEMA_V3.getBytes(); + break; + default: + Assert.assertTrue("Unknown version: '" + version + "'", false); + } + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", schemaContent); + MvcResult result; + if (version > 1) { + // Read ETag + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile). + file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + } else { + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + String body = result.getResponse().getContentAsString(); + + record = mapper.readValue(body, MetadataSchemaRecord.class); + Long versionAfter = record.getSchemaVersion(); + Assert.assertEquals("Wrong version created!", version, (long) versionAfter); + + } + + private void ingestNewSchemaRecord(String schemaId, long version) throws Exception { + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + record.setComment(COMMENT + version); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + file(recordFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + } + + private DataResource createDataResource(String id) { + DataResource record = new DataResource(); + record.setId(id); + // mandatory element title has to be set + record.getTitles().add(Title.factoryTitle(id)); + record.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.XML + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + record.getFormats().add(MediaType.APPLICATION_XML.toString()); + Set aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); + return record; + } + + private String createKitMetadataRecord(String schemaId) throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(schemaId)); + record.setRelatedResource(RELATED_RESOURCE); + Set aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); + aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + + return result.getId(); + } + + private static RequestPostProcessor remoteAddr(final String remoteAddr) { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setRemoteAddr(remoteAddr); + return request; + }; + } + + private static RequestPostProcessor putMultipart() { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setMethod("PUT"); + return request; + }; + } + + private void testForNextVersion(String first, String second) { + int index = first.lastIndexOf("="); + int firstVersion = Integer.parseInt(first.substring(index + 1)); + int secondVersion = Integer.parseInt(second.substring(index + 1)); + Assert.assertEquals(firstVersion + 1, secondVersion); + Assert.assertEquals(first.substring(0, index), second.substring(0, index)); + } + + public static boolean isSameSetOfAclEntries(Set firstSet, Set secondSet) { + boolean isSameSet = false; + if (firstSet.size() == secondSet.size()) { + isSameSet = true; + for (AclEntry item : firstSet) { + if (!isPartOfAclEntries(item, secondSet)) { + isSameSet = false; + break; + } + } + } + return isSameSet; + } + + public static boolean isPartOfAclEntries(AclEntry entry, Set allEntries) { + boolean isPart = false; + for (AclEntry item : allEntries) { + if (item.getSid().equals(entry.getSid()) && item.getPermission().equals(entry.getPermission())) { + isPart = true; + break; + } + } + return isPart; + } + + private static void validateDataResources(DataResource first, DataResource second) { + if (first == second) { + return; + } + if (first.equals(second)) { + return; + } + Assert.assertEquals(first.getId(), second.getId()); + Assert.assertEquals(first.getEmbargoDate(), second.getEmbargoDate()); + Assert.assertEquals(first.getIdentifier().getValue(), second.getIdentifier().getValue()); + Assert.assertEquals(first.getIdentifier().getIdentifierType(), second.getIdentifier().getIdentifierType()); + Assert.assertEquals(first.getLanguage(), second.getLanguage()); + Assert.assertEquals(first.getLastUpdate(), second.getLastUpdate()); + Assert.assertEquals(first.getPublicationYear(), second.getPublicationYear()); + Assert.assertEquals(first.getPublisher(), second.getPublisher()); + Assert.assertEquals(first.getResourceType().getValue(), second.getResourceType().getValue()); + Assert.assertEquals(first.getResourceType().getTypeGeneral(), second.getResourceType().getTypeGeneral()); + Assert.assertEquals(first.getState(), second.getState()); + Assert.assertEquals(first.getVersion(), second.getVersion()); + validateSets(first.getAcls(), second.getAcls()); + validateIdentifierSets(first.getAlternateIdentifiers(), second.getAlternateIdentifiers()); + validateContributors(first.getContributors(), second.getContributors()); + validateCreators(first.getCreators(), second.getCreators()); + validateDates(first.getDates(), second.getDates()); + validateDescriptions(first.getDescriptions(), second.getDescriptions()); + validateStrings( + first.getFormats(), second.getFormats()); + validateRights(first.getRights(), second.getRights()); + validateStrings(first.getSizes(), second.getSizes()); + validateTitles(first.getTitles(), second.getTitles()); + // not used yet + //first.getFundingReferences(); + first.getGeoLocations(); + first.getRelatedIdentifiers(); + first.getSubjects(); + } + + private static void validateSets(Set first, Set second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (AclEntry item : first) { + identical = false; + for (AclEntry item2 : copy) { + identical = (item.getPermission() == item2.getPermission()) + && (item.getSid().equals(item2.getSid())); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + + private static void validateIdentifierSets(Set first, Set second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (Identifier item : first) { + identical = false; + for (Identifier item2 : copy) { + identical = (item.getIdentifierType() == item2.getIdentifierType()) + && (item.getValue().equals(item2.getValue())); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + + private static void validateContributors(Set first, Set second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (Contributor item : first) { + identical = false; + for (Contributor item2 : copy) { + identical = (item.getContributionType() == item2.getContributionType()) + && (item.getUser().getFamilyName().equals(item2.getUser().getFamilyName())) + && (item.getUser().getGivenName().equals(item2.getUser().getGivenName())); + if (identical) { + copy.remove(item2); + } + break; + } + Assert.assertTrue(identical); + } + } + + private static void validateCreators(Set first, Set second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (Agent item : first) { + identical = false; + for (Agent item2 : copy) { + identical = (item.getFamilyName().equals(item2.getFamilyName())) + && (item.getGivenName().equals(item2.getGivenName())) + && validateStrings(item.getAffiliations(), item2.getAffiliations()); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + + private static void validateDates(Set first, Set second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (Date item : first) { + identical = false; + for (Date item2 : copy) { + identical = (item.getType() == item2.getType()) + && (item.getValue().equals(item2.getValue())); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + + private static void validateDescriptions(Set first, Set second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (Description item : first) { + identical = false; + for (Description item2 : copy) { + identical = (item.getDescription().equals(item2.getDescription())) + && (item.getLang().equals(item2.getLang())) + && (item.getType() == item2.getType()); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + + private static boolean validateStrings(Set first, Set second) { + if (first == second) { + return true; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (String item : first) { + identical = false; + for (String item2 : copy) { + identical = item.equals(item2); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + return true; + } + + private static void validateRights(Set first, Set second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (Scheme item : first) { + identical = false; + for (Scheme item2 : copy) { + identical = (item.getSchemeId().equals(item2.getSchemeId())) + && (item.getSchemeUri().equals(item2.getSchemeUri())); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + + private static void validateTitles(Set first, Set<Title> second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set<Title> copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (Title item : first) { + identical = false; + for (Title item2 : copy) { + identical = (item.getTitleType() == item2.getTitleType()) + && (item.getValue().equals(item2.getValue())); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + +} From 7f49f7811ea0f71a5116eb663f200de27f237174 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 5 Mar 2024 13:03:40 +0100 Subject: [PATCH 009/181] Tests for schema management. Work in progress. --- .../util/DataResourceRecordUtil.java | 51 +- .../impl/SchemaRegistryControllerImplV2.java | 44 +- .../test/SchemaRegistryControllerTestV2.java | 878 +++++++++++------- 3 files changed, 618 insertions(+), 355 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 0135da3a..30233281 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -137,7 +137,14 @@ public class DataResourceRecordUtil { private static IUrl2PathDao url2PathDao; public static final String SCHEMA_SUFFIX = "_Schema"; + public static final String XML_SCHEMA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.XML + SCHEMA_SUFFIX; + public static final String JSON_SCHEMA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.JSON + SCHEMA_SUFFIX; + public static final String METADATA_SUFFIX = "_Metadata"; + public static final String XML_METADATA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.XML + METADATA_SUFFIX; + public static final String JSON_METADATA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.JSON + METADATA_SUFFIX; + + DataResourceRecordUtil() { //Utility class @@ -873,7 +880,7 @@ private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentif * @param document document */ private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, - MetadataRecord metadataRecord, + DataResource metadataRecord, MultipartFile document) { LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, metadataRecord, document); if (document == null || document.isEmpty()) { @@ -887,7 +894,7 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro LOG.trace(LOG_SCHEMA_REGISTRY); if (schemaConfig != null) { try { - MetadataSchemaRecordUtil.validateMetadataDocument(schemaConfig, document, metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); + validateMetadataDocument(schemaConfig, document, metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); validationSuccess = true; } catch (Exception ex) { String message = "Error validating document!"; @@ -962,26 +969,17 @@ public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metast LOG.info("getRecordByIdAndVersion {}, {}, {}", nano, (nano2 - nano), (nano3 - nano)); return findFirst.get(); } -// -// public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, -// String recordId) throws ResourceNotFoundException { -// return getMetadataDocumentByIdAndVersion(metastoreProperties, recordId, null); -// } -// -// public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, -// String recordId, Long version) throws ResourceNotFoundException { -// LOG.trace("Obtaining metadata record with id {} and version {}.", recordId, version); -// MetadataRecord metadataRecord = getRecordByIdAndVersion(metastoreProperties, recordId, version); -// -// URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); -// -// Path metadataDocumentPath = Paths.get(metadataDocumentUri); -// if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { -// LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); -// throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); -// } -// return metadataDocumentPath; -// } + + public static ContentInformation getContentInformationByIdAndVersion(MetastoreConfiguration metastoreProperties, + String recordId) throws ResourceNotFoundException { + return getContentInformationByIdAndVersion(metastoreProperties, recordId, null); + } + + public static ContentInformation getContentInformationByIdAndVersion(MetastoreConfiguration metastoreProperties, + String recordId, Long version) throws ResourceNotFoundException { + LOG.trace("Obtaining metadata record with id {} and version {}.", recordId, version); + return metastoreProperties.getContentInformationService().getContentInformation(recordId, null, version); + } /** * Merge new metadata record in the existing one. @@ -1338,10 +1336,10 @@ public static final void check4validSchemaId(DataResource metadataRecord) { String lowerCaseId = id.toLowerCase(); // schema id should be lower case due to elasticsearch // alternate identifier is used to set id to a given id. - metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); + metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); if (!lowerCaseId.equals(id)) { - metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(id)); - } + metadataRecord.getAlternateIdentifiers().add(Identifier.factoryIdentifier(id, Identifier.IDENTIFIER_TYPE.OTHER)); + } } public static final void check4validId(DataResource metadataRecord) { @@ -1734,6 +1732,7 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app ControllerUtils.checkEtag(eTag, dataResource); if (metadataRecord != null) { metadataRecord.setVersion(dataResource.getVersion()); + metadataRecord.setId(dataResource.getId()); dataResource = metadataRecord; } else { dataResource = DataResourceUtils.copyDataResource(dataResource); @@ -1801,7 +1800,7 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app } } - dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); + dataResource = DataResourceUtils.updateResource(applicationProperties, dataResource.getId(), dataResource, eTag, supplier); return dataResource; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 69f14535..f9408524 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -41,7 +41,6 @@ import java.net.URI; import java.net.URL; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; import java.util.ArrayList; @@ -72,6 +71,8 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; import edu.kit.datamanager.metastore2.web.ISchemaRegistryControllerV2; +import edu.kit.datamanager.repo.domain.ContentInformation; +import java.nio.file.Path; /** * Controller for schema documents. @@ -175,11 +176,11 @@ public ResponseEntity getSchemaDocumentById( LOG.trace("Performing getSchemaDocumentById({}, {}).", schemaId, version); LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); - MetadataSchemaRecord schemaRecord = MetadataSchemaRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); - URI schemaDocumentUri = URI.create(schemaRecord.getSchemaDocumentUri()); - - MediaType contentType = MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType()) ? MediaType.APPLICATION_XML : MediaType.APPLICATION_JSON; - Path schemaDocumentPath = Paths.get(schemaDocumentUri); + DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); + ContentInformation contentInfo = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaRecord.getId(), Long.valueOf(schemaRecord.getVersion())); + MediaType contentType = MediaType.valueOf(contentInfo.getMediaType()); + URI pathToFile = URI.create(contentInfo.getContentUri()); + Path schemaDocumentPath = Paths.get(pathToFile); if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { LOG.trace("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Schema document on server either does not exist or is no file or is not readable."); @@ -226,7 +227,7 @@ public ResponseEntity validate(@PathVariable(value = "schemaId") String schemaId WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing validate({}, {}, {}).", schemaId, version, "#document"); -// DataResourceRecordUtil.validateMetadataDocument(schemaConfig, document, schemaId, version); + DataResourceRecordUtil.validateMetadataDocument(schemaConfig, document, schemaId, version); LOG.trace("Metadata document validation succeeded. Returning HTTP NOT_CONTENT."); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } @@ -246,13 +247,32 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche return getAllVersions(schemaId, pgbl); } // Search for resource type of MetadataSchemaRecord - Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE)); + boolean searchForJson = true; + boolean searchForXml = true; + ResourceType resourceType = ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); + if (mimeTypes != null) { + searchForJson = false; + searchForXml = false; + for (String mimeType : mimeTypes) { + if (mimeType.contains("json")) { + searchForJson = true; + } + if (mimeType.contains("xml")) { + searchForXml = true; + } + } + if (searchForJson && !searchForXml) { + resourceType = ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + } + if (!searchForJson && searchForXml) { + resourceType = ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + } + if (!searchForJson && !searchForXml) + resourceType = ResourceType.createResourceType("unknown"); + } + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); // Add authentication if enabled spec = addAuthenticationSpecification(spec); - //one of given mimetypes. - if ((mimeTypes != null) && !mimeTypes.isEmpty()) { - spec = spec.and(TitleSpec.toSpecification(mimeTypes.toArray(new String[mimeTypes.size()]))); - } if ((updateFrom != null) || (updateUntil != null)) { spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 3c4ec757..47353bd8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -5,6 +5,7 @@ */ package edu.kit.datamanager.metastore2.test; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.CollectionType; import edu.kit.datamanager.entities.Identifier; @@ -158,6 +159,24 @@ public class SchemaRegistryControllerTestV2 { private static final String JSON_DOCUMENT = "{\"title\":\"any string\",\"date\": \"2020-10-16\"}"; private static final String RELATED_RESOURCE_STRING = "anyResourceId"; private static final ResourceIdentifier RELATED_RESOURCE = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING); + private final static String JSON_SCHEMA = "{\n" + + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; private MockMvc mockMvc; @Autowired @@ -245,8 +264,8 @@ public void testCreateSchemaRecordWithCapitalLetter() throws Exception { Assert.assertEquals(record.getResourceType().getValue(), ms_record.getResourceType().getValue()); Assert.assertEquals(record.getResourceType().getTypeGeneral(), ms_record.getResourceType().getTypeGeneral()); Assert.assertEquals(record.getFormats(), ms_record.getFormats()); - Assert.assertEquals(record.getId(), ms_record.getId()); - Assert.assertNotEquals(schemaIDWithCapitalLetters, ms_record.getId()); + Assert.assertNotEquals(record.getId(), ms_record.getId()); + Assert.assertEquals(schemaIDWithCapitalLetters.toLowerCase(), ms_record.getId()); } @Test @@ -338,8 +357,8 @@ public void testCreateSchemaRecordWithLocationUri() throws Exception { MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String content2 = result2.getResponse().getContentAsString(); - validateDataResources(mapper.readValue(content, DataResource.class), mapper.readValue(content2, DataResource.class)); - Assert.assertEquals(content, content2); + validateDataResources(content, content2); +// Assert.assertEquals(content, content2); } @Test @@ -555,8 +574,8 @@ public void testCreateTwoVersionsOfSchemaRecord() throws Exception { file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); - MetadataSchemaRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); - Assert.assertEquals(result.getSchemaVersion(), Long.valueOf(1l)); + DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(result.getVersion(), Long.toString(1l)); // Can't create same resource twice -> Conflict res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). @@ -569,11 +588,11 @@ public void testGetSchemaRecordByIdWithoutVersion() throws Exception { MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataSchemaRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); + DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); - Assert.assertEquals(SCHEMA_ID, result.getSchemaId()); + Assert.assertEquals(SCHEMA_ID, result.getId()); //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema - Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); +// Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); } @Test @@ -582,10 +601,10 @@ public void testGetSchemaRecordByIdWithVersion() throws Exception { MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").param("version", "1").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataSchemaRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); + DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); - Assert.assertEquals(SCHEMA_ID, result.getSchemaId()); - Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); + Assert.assertEquals(SCHEMA_ID, result.getId()); +// Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); } @Test @@ -617,7 +636,7 @@ public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { ingestSchemaRecord(); MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertTrue(result.length > 0); } @@ -627,19 +646,62 @@ public void testFindRecordsBySchemaId() throws Exception { ingestSchemaRecord(); MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertTrue(result.length > 0); } @Test public void testFindRecordsByMimeType() throws Exception { - ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("mimeType", MediaType.APPLICATION_XML.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); + ingestXmlDataResource("byMimeType"); + ingestJsonDataResource("byMimeTypeJson"); ObjectMapper map = new ObjectMapper(); - MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + // Search without mimetype + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + Assert.assertEquals(2, result.length); + // Search only for xml schemas + res = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("mimeType", MediaType.APPLICATION_XML_VALUE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); + + // Search only for json schemas + res = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("mimeType", MediaType.APPLICATION_JSON_VALUE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + Assert.assertEquals(1, result.length); + + // Search for both mimetypes. + res = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("mimeType", MediaType.APPLICATION_XML_VALUE). + param("mimeType", MediaType.APPLICATION_JSON_VALUE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(2, result.length); + // Search for unkown mimetypes. (ignore them) + res = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("mimeType", MediaType.TEXT_PLAIN_VALUE). + param("mimeType", MediaType.TEXT_HTML_VALUE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(0, result.length); } @Test @@ -647,7 +709,7 @@ public void testFindRecordsByInvalidMimeType() throws Exception { ingestSchemaRecord(); MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("mimeType", "invalid")).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -661,7 +723,7 @@ public void testFindRecordsByUnknownSchemaId() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -677,7 +739,7 @@ public void testGetSchemaDocument() throws Exception { @Test public void testGetSchemaDocumentWithMissingSchemaFile() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("dc"); String contentUri = contentInformationDao.findAll(PageRequest.of(0, 2)).getContent().get(0).getContentUri(); //delete schema file URI uri = new URI(contentUri); @@ -689,37 +751,37 @@ public void testGetSchemaDocumentWithMissingSchemaFile() throws Exception { @Test public void testValidate() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("testValidate"); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test public void testValidateUnknownVersion() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("validateUnknownVersion"); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=666").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testValidateKnownVersion() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("validateKnownVersion"); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test public void testValidateUnknownSchemaId() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("testValidateUnknownSchemaId"); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + INVALID_SCHEMA_ID + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); } @Test public void testValidateWithInvalidDocument() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("testValidateWithInvalidDocument"); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test public void testValidateWithEmptyDocument() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("testValidateWithEmptyDocument"); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @@ -730,67 +792,74 @@ public void testValidateWithoutDocument() throws Exception { @Test public void testValidateWithoutValidator() throws Exception { - ingestSchemaRecord(); + ingestXmlDataResource("testValidateWithoutValidator"); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test public void testValidateWithMissingSchemaFile() throws Exception { - ingestSchemaRecord(); + String schemaId = "testValidateWithMissingSchemaFile"; + ingestXmlDataResource(schemaId); // Get location of schema file. - String contentUri = contentInformationDao.findAll(PageRequest.of(0, 2)).getContent().get(0).getContentUri(); + DataResource dataRes = DataResource.factoryNewDataResource(); + dataRes.setId(schemaId.toLowerCase()); + String contentUri = contentInformationDao.findByParentResource(dataRes,PageRequest.of(0, 2)).getContent().get(0).getContentUri(); //delete schema file URI uri = new URI(contentUri); Files.delete(Paths.get(uri)); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); } // Update only record @Test public void testUpdateRecord() throws Exception { - String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); + String schemaId = "updateRecord"; String newComment = "new comment"; String newLabel = "label changed!"; - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - String mimeTypeBefore = record.getMimeType(); - Assert.assertEquals(DEFINITION, record.getDefinition()); - Assert.assertEquals(LABEL, record.getLabel()); - Assert.assertEquals(COMMENT, record.getComment()); - record.setDefinition(null); - record.setComment(newComment); - record.setLabel(newLabel); + DataResource record = mapper.readValue(body, DataResource.class); + String mimeTypeBefore = record.getFormats().iterator().next(); + validateDescriptions(record, schemaId, DEFINITION, COMMENT); +// Assert.assertEquals(DEFINITION, record.getDesDefinition()); +// Assert.assertEquals(LABEL, record.getTitle()); +// Assert.assertEquals(COMMENT, record.getComment()); + setDefinition(record, null); + setComment(record, newComment); + setTitle(record, newLabel); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). - file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type is not allowed to be changed. - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type is not allowed to be changed. + +// Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + validateCreateDates(record.getDates(), record2.getDates()); // Version shouldn't be updated - Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); - Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } +// Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getId(), record2.getId()); + Assert.assertEquals((long) Long.parseLong(record.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + validateSets(record.getAcls(), record2.getAcls()); +// if (record.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - Assert.assertEquals("Check label: ", record.getLabel(), record2.getLabel()); - Assert.assertEquals("Check comment: ", record.getComment(), record2.getComment()); - Assert.assertEquals("Check definition: ", record.getDefinition(), record2.getDefinition()); - Assert.assertEquals("Check label: ", newLabel, record2.getLabel()); - Assert.assertEquals("Check comment: ", newComment, record2.getComment()); - Assert.assertNull("Check definition for 'null'", record2.getDefinition()); + Assert.assertEquals("Check label: ", getTitle(record), getTitle(record2)); + Assert.assertEquals("Check comment: ", getComment(record), getComment(record2)); + Assert.assertEquals("Check definition: ", getDefinition(record), getDefinition(record2)); + Assert.assertEquals("Check label: ", newLabel, getTitle(record2)); + Assert.assertEquals("Check comment: ", newComment, getComment(record2)); + Assert.assertNull("Check definition for 'null'", getDefinition(record2)); } // Update only record @@ -799,68 +868,72 @@ public void testUpdateRecordRemovingLabel() throws Exception { String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); String newComment = "new comment"; String newLabel = "label changed!"; - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - String mimeTypeBefore = record.getMimeType(); - Assert.assertEquals(DEFINITION, record.getDefinition()); - Assert.assertEquals(LABEL, record.getLabel()); - Assert.assertEquals(COMMENT, record.getComment()); - record.setDefinition(null); - record.setComment(null); - record.setLabel(null); + DataResource record = mapper.readValue(body, DataResource.class); + String mimeTypeBefore = record.getFormats().iterator().next(); + Assert.assertEquals(DEFINITION, getDefinition(record)); + Assert.assertEquals(schemaId, getTitle(record)); + Assert.assertEquals(COMMENT, getComment(record)); + setDefinition(record, null); + setComment(record, null); + setTitle(record, null); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). - file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type is not allowed to be changed. - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type is not allowed to be changed. + // Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); validateCreateDates(record.getDates(), record2.getDates()); // Version shouldn't be updated - Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); - Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } - Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - Assert.assertEquals("Check label: ", record.getLabel(), record2.getLabel()); - Assert.assertEquals("Check comment: ", record.getComment(), record2.getComment()); - Assert.assertEquals("Check definition: ", record.getDefinition(), record2.getComment()); - Assert.assertNull("Check label: ", record2.getLabel()); - Assert.assertNull("Check comment: ", record2.getComment()); - Assert.assertNull("Check definition for 'null'", record2.getDefinition()); +// Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getId(), record2.getId()); + Assert.assertEquals((long) Long.parseLong(record.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + validateSets(record.getAcls(), record2.getAcls()); +// if (recUpdateord.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } +// Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + validateUpdateDates(record.getDates(), record2.getDates()); + validateDescriptions(record.getDescriptions(), record2.getDescriptions()); + validateTitles(record.getTitles(), record2.getTitles()); +// Assert.assertEquals(" label: ", record.getTitle(), record2.getTitle()); +// Assert.assertEquals("Check comment: ", record.getComment(), record2.getComment()); +// Assert.assertEquals("ChCheckeck definition: ", record.getDefinition(), record2.getComment()); +// Assert.assertNull("Check label: ", record2.getTitle()); +// Assert.assertNull("Check comment: ", record2.getComment()); +// Assert.assertNull("Check definition for 'null'", record2.getDefinition()); } @Test public void testUpdateRecordIgnoreACL() throws Exception { String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord oldRecord = mapper.readValue(body, MetadataSchemaRecord.class); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + DataResource oldRecord = mapper.readValue(body, DataResource.class); + DataResource record = mapper.readValue(body, DataResource.class); // Set all ACL to WRITE - for (AclEntry entry : record.getAcl()) { + for (AclEntry entry : record.getAcls()) { entry.setPermission(PERMISSION.WRITE); } - String mimeTypeBefore = record.getMimeType(); - String definitionBefore = record.getDefinition(); - String labelBefore = record.getLabel(); - String commentBefore = record.getComment(); - record.setDefinition(""); - record.setComment("new comment"); - record.setLabel("label changed"); + String mimeTypeBefore = record.getFormats().iterator().next(); + String definitionBefore = getDefinition(record); + String labelBefore = getTitle(record); + String commentBefore = getComment(record); + setDefinition(record, ""); + setComment(record, "new comment"); + setTitle(record, "label changed"); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -869,41 +942,43 @@ public void testUpdateRecordIgnoreACL() throws Exception { with(putMultipart())). andDo(print()). andExpect(status().isOk()). - andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")). andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type is not allowed to be changed. - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type is not allowed to be changed. +// Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + validateCreateDates(record.getDates(), record2.getDates()); // Version shouldn't be updated - Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); - Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(isSameSetOfAclEntries(record.getAcl(), record2.getAcl())); - Assert.assertFalse(isSameSetOfAclEntries(oldRecord.getAcl(), record.getAcl())); +// Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getId(), record2.getId()); + Assert.assertEquals((long) Long.parseLong(record.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + if (record.getAcls() != null) { + Assert.assertTrue(isSameSetOfAclEntries(record.getAcls(), record2.getAcls())); + Assert.assertFalse(isSameSetOfAclEntries(oldRecord.getAcls(), record.getAcls())); } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - Assert.assertEquals("Check label: ", record.getLabel(), record2.getLabel()); - Assert.assertEquals("Check comment: ", record.getComment(), record2.getComment()); - Assert.assertNotEquals("Check label: ", labelBefore, record2.getLabel()); - Assert.assertNotEquals("Check comment: ", commentBefore, record2.getComment()); - Assert.assertNull("Check definition for 'null'", record2.getDefinition()); + Assert.assertEquals("Check label: ", getTitle(record), getTitle(record2)); + Assert.assertEquals("Check comment: ", getComment(record), getComment(record2)); + Assert.assertNotEquals("Check label: ", labelBefore, getTitle(record2)); + Assert.assertNotEquals("Check comment: ", commentBefore, getComment(record2)); + Assert.assertNull("Check definition for 'null'", getDefinition(record2)); } @Test public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { String schemaId = "updateMimetypeOfRecord".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - record.setMimeType(MediaType.APPLICATION_JSON.toString()); + DataResource record = mapper.readValue(body, DataResource.class); + record.getFormats().clear(); + record.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); // Should not fail as invalid mimetype is not used validating schema! this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -911,20 +986,20 @@ public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { with(putMultipart())). andDo(print()). andExpect(status().isOk()). - andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")); + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")); } @Test public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { String schemaId = "updateTypeOfRecord".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); + DataResource record = mapper.readValue(body, DataResource.class); + record.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); // Should fail due to invalid type! this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -938,66 +1013,70 @@ public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { @Test public void testUpdateRecordWithoutChanges() throws Exception { String schemaId = "updateRecordWithoutChanges".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). - file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertEquals(record.getMimeType(), record2.getMimeType());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - // Version shouldn't be updated - Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); - Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } - Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + DataResource record2 = mapper.readValue(body, DataResource.class); + validateDataResources(record, record2, true); +// Assert.assertEquals(record.getFormats().iterator().next(), record2.getFormats().iterator().next());//mime type was changed by update +// Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); +// // Version shouldn't be updated +// Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); +// Assert.assertEquals(record.getId(), record2.getId()); +// Assert.assertEquals((long) Long.parseLong(record.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update +// if (record.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } +// Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); } @Test public void testUpdateRecordAndDocument() throws Exception { - String schemaId = "updateRecordAndDocument".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + String schemaId = "updateRecordAndDocument"; + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - String mimeTypeBefore = record.getMimeType(); - record.setMimeType(MediaType.APPLICATION_JSON.toString()); + DataResource record = mapper.readValue(body, DataResource.class); + String mimeTypeBefore = record.getFormats().iterator().next(); + record.getFormats().clear(); + record.getFormats().add(MediaType.APPLICATION_JSON.toString()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). - file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNull(record2.getLicenseUri()); - Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNull(record2.getLicenseUri()); +// Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); + validateRights(record.getRights(), record2.getRights()); + + Assert.assertNotEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type was changed by update +// Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + validateCreateDates(record.getDates(), record2.getDates()); + + testForNextVersion(record.getVersion(), record2.getVersion()); // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); - Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } - Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); +// Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getId(), record2.getId()); + validateSets(record.getAcls(), record2.getAcls()); + validateUpdateDates(record.getDates(), record2.getDates()); +// Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Test also document for update result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); @@ -1008,16 +1087,17 @@ public void testUpdateRecordAndDocument() throws Exception { @Test public void testUpdateRecordAndDocumentWithLicense() throws Exception { String schemaId = "updateRecordAndDocumentWithLicense".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - String mimeTypeBefore = record.getMimeType(); - record.setMimeType(MediaType.APPLICATION_JSON.toString()); - record.setLicenseUri(APACHE_2_LICENSE); + DataResource record = mapper.readValue(body, DataResource.class); + String mimeTypeBefore = record.getFormats().iterator().next(); + record.getFormats().clear(); + record.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); + record.getRights().add(Scheme.factoryScheme("URL", APACHE_2_LICENSE)); System.out.println("****************************************************************************************"); System.out.println("****************************************************************************************"); System.out.println(mapper.writeValueAsString(record)); @@ -1031,24 +1111,27 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { with(putMultipart())). andDo(print()). andExpect(status().isOk()). - andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")). andReturn(); body = result.getResponse().getContentAsString(); etag = result.getResponse().getHeader("ETag"); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotNull(record2.getLicenseUri()); - Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); + DataResource record2 = mapper.readValue(body, DataResource.class); + validateRights(record.getRights(), record2.getRights()); +// Assert.assertNotNull(record2.getLicenseUri()); +// Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); + validateRights(record.getRights(), record2.getRights()); + Assert.assertNotEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type was changed by update + // Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); validateCreateDates(record.getDates(), record2.getDates()); + testForNextVersion(record.getVersion(), record2.getVersion()); // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); - Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } +// Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getId(), record2.getId()); + Assert.assertEquals((long) Long.parseLong(record.getVersion()) + 1l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + validateSets(record.getAcls(), record2.getAcls()); +// if (record.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Test also document for update result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -1056,7 +1139,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { Assert.assertEquals(KIT_SCHEMA_V2, content); // Remove license - record2.setLicenseUri(null); + record2.getRights().clear(); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -1065,57 +1148,54 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { with(putMultipart())). andDo(print()). andExpect(status().isOk()). - andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")). andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record3 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNull(record3.getLicenseUri()); - Assert.assertEquals(record2.getMimeType(), record3.getMimeType());//mime type was changed by update - Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); - Assert.assertEquals(record2.getSchemaDocumentUri(), record3.getSchemaDocumentUri()); -// Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); - Assert.assertEquals(record2.getSchemaHash(), record3.getSchemaHash()); - Assert.assertEquals(record2.getSchemaId(), record3.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); - } - Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + DataResource record3 = mapper.readValue(body, DataResource.class); + validateDataResources(record2, record3, true); +// Assert.assertNull(record3.getLicenseUri()); +// Assert.assertEquals(record2.getFormats().iterator().next(), record3.getFormats().iterator().next());//mime type was changed by update +// Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); +// Assert.assertEquals(record2.getSchemaDocumentUri(), record3.getSchemaDocumentUri()); +//// Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); +// Assert.assertEquals(record2.getSchemaHash(), record3.getSchemaHash()); +// Assert.assertEquals(record2.getId(), record3.getId()); +// Assert.assertEquals((long) Long.parseLong(record.getVersion()) + 1l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update +// if (record.getAcl() != null) { +// Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); +// } +// Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); } @Test public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { String schemaId = "updateRecordAndDocumentWithWrongVersion".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - record.setSchemaVersion(0l); - String mimeTypeBefore = record.getMimeType(); - record.setMimeType(MediaType.APPLICATION_JSON.toString()); + DataResource record = mapper.readValue(body, DataResource.class); + record.setVersion(Long.toString(0l)); + String mimeTypeBefore = record.getFormats().iterator().next(); + record.getFormats().clear(); + record.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). - file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + file(recordFile).file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); -// Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); - Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals(2l, (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } - Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertNotEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type was changed by update + validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(record.getId(), record2.getId()); + Assert.assertEquals(2l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + validateSets(record.getAcls(), record2.getAcls()); + validateUpdateDates(record.getDates(), record2.getDates()); // Test also document for update result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); @@ -1130,13 +1210,13 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { @Test public void testUpdateOnlyDocument() throws Exception { String schemaId = "updateRecordDocumentOnly".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -1145,20 +1225,21 @@ public void testUpdateOnlyDocument() throws Exception { with(putMultipart())). andDo(print()). andExpect(status().isOk()). - andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")). + andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")). andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertEquals(record.getMimeType(), record2.getMimeType());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); - Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertEquals(record.getFormats().iterator().next(), record2.getFormats().iterator().next());//mime type was changed by update + // Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); validateCreateDates(record.getDates(), record2.getDates()); + testForNextVersion(record.getVersion(), record2.getVersion()); +// Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); + Assert.assertEquals(record.getId(), record2.getId()); + Assert.assertEquals((long) Long.parseLong(record.getVersion()) + 1l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + validateSets(record.getAcls(), record2.getAcls()); +// if (record.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Test also document for update result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -1169,11 +1250,8 @@ public void testUpdateOnlyDocument() throws Exception { @Test public void testUpdateRecordWithSmallChangesInDocument() throws Exception { - String schemaId = "updateRecordWithSmallChanges".toLowerCase(Locale.getDefault()); - SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(schemaId); - schemaRecord.setVersion(1l); - schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + String schemaId = "updateRecordWithSmallChanges"; + DataResource schemaRecord = createDataResource(schemaId); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); @@ -1185,27 +1263,20 @@ public void testUpdateRecordWithSmallChangesInDocument() throws Exception { // MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.XML_SCHEMA_V1_TYPO.getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(schemaFile). header("If-Match", etag). with(putMultipart())). andDo(print()). andExpect(status().isOk()). - andExpect(redirectedUrlPattern("http://*:*/**/" + schemaRecord.getSchemaId() + "?version=*")). + andExpect(redirectedUrlPattern("http://*:*/**/" + schemaRecord.getId().toLowerCase() + "?version=*")). andReturn(); } @Test public void testUpdateRecordWithoutExplizitGet() throws Exception { - String schemaId = "updateWithoutGet".toLowerCase(Locale.getDefault()); - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId(schemaId); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + String schemaId = "updateWithoutGet"; + DataResource record = createDataResource(schemaId); ObjectMapper mapper = new ObjectMapper(); @@ -1219,26 +1290,27 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String body = result.getResponse().getContentAsString(); mapper = new ObjectMapper(); - MetadataSchemaRecord record1 = mapper.readValue(body, MetadataSchemaRecord.class); - String mimeTypeBefore = record1.getMimeType(); - record1.setMimeType(MediaType.APPLICATION_JSON.toString()); + DataResource record1 = mapper.readValue(body, DataResource.class); + String mimeTypeBefore = record1.getFormats().iterator().next(); + record1.getFormats().clear(); + record1.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record1).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update - Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertNotEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type was changed by update + validateDates(record1.getDates(), record2.getDates()); // Version shouldn't be updated - Assert.assertEquals(record1.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); - Assert.assertEquals(record1.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record1.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record1.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record1.getAcl() != null) { - Assert.assertTrue(record1.getAcl().containsAll(record2.getAcl())); - } - Assert.assertTrue(record1.getLastUpdate().isBefore(record2.getLastUpdate())); + Assert.assertEquals(record1.getId(), record2.getId()); + Assert.assertEquals((long) Long.parseLong(record1.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + validateSets(record.getAcls(), record2.getAcls()); +// if (record.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } + validateUpdateDates(record1.getDates(), record2.getDates()); +// Assert.assertTrue(record1.getLastUpdate().isBefore(record2.getLastUpdate())); } @Test @@ -1247,8 +1319,9 @@ public void testUpdateRecordWithoutETag() throws Exception { MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - record.setMimeType(MediaType.APPLICATION_JSON.toString()); + DataResource record = mapper.readValue(body, DataResource.class); + record.getFormats().clear(); + record.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). @@ -1262,7 +1335,7 @@ public void testUpdateRecordWithWrongETag() throws Exception { String etag = result.getResponse().getHeader("ETag") + "unknown"; String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionFailed()).andReturn(); @@ -1280,14 +1353,8 @@ public void testUpdateRecordWithoutBody() throws Exception { @Test public void testCreateSchemaRecordWithUpdateWithoutChanges() throws Exception { // Test with a schema missing schema property. - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setSchemaId("updateWithoutChanges_xsd".toLowerCase(Locale.getDefault())); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + String schemaId = "updateWithoutChanges_xsd".toLowerCase(Locale.getDefault()); + DataResource record = createDataResource(schemaId); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1299,30 +1366,31 @@ public void testCreateSchemaRecordWithUpdateWithoutChanges() throws Exception { String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record1 = mapper.readValue(body, MetadataSchemaRecord.class); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + record.getSchemaId()). - file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=*")).andReturn(); + DataResource record1 = mapper.readValue(body, DataResource.class); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + record.getId()). + file(schemaFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")).andReturn(); body = result.getResponse().getContentAsString(); - MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertEquals(record1.getMimeType(), record2.getMimeType());//mime type was changed by update - Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); + DataResource record2 = mapper.readValue(body, DataResource.class); + validateStrings(record1.getFormats(), record2.getFormats()); + validateDates(record1.getDates(), record2.getDates()); +// Assert.assertEquals(record1.getFormats().iterator().next(), record2.getFormats().iterator().next());//mime type was changed by update +// Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); // Version shouldn't be updated - Assert.assertEquals(record1.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); - Assert.assertEquals(record1.getSchemaHash(), record2.getSchemaHash()); - Assert.assertEquals(record1.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record1.getSchemaVersion(), (long) record2.getSchemaVersion());//version is not changing for metadata update - if (record1.getAcl() != null) { - Assert.assertTrue(record1.getAcl().containsAll(record2.getAcl())); - } - Assert.assertTrue(record1.getLastUpdate().isBefore(record2.getLastUpdate())); + Assert.assertEquals(record1.getId(), record2.getId()); + Assert.assertEquals((long) Long.parseLong(record1.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + validateSets(record.getAcls(), record2.getAcls()); +// if (record.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } + validateUpdateDates(record1.getDates(), record2.getDates()); } @Test public void testDeleteSchemaRecord() throws Exception { ObjectMapper mapper = new ObjectMapper(); String schemaId = "testDelete".toLowerCase(Locale.getDefault()); - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); // Get a list of all records MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH). @@ -1330,7 +1398,7 @@ public void testDeleteSchemaRecord() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord[].class).length; + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). @@ -1363,7 +1431,7 @@ public void testDeleteSchemaRecord() throws Exception { // List of records should be smaller afterwards result = this.mockMvc.perform(get(API_SCHEMA_PATH).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); - int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataSchemaRecord[].class).length; + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); } @@ -1378,11 +1446,11 @@ public void testGetAllVersionsOfRecord() throws Exception { MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); ObjectMapper mapper = new ObjectMapper(); CollectionType mapCollectionType = mapper.getTypeFactory() - .constructCollectionType(List.class, MetadataSchemaRecord.class); - List<MetadataSchemaRecord> resultList = mapper.readValue(result.getResponse().getContentAsString(), mapCollectionType); + .constructCollectionType(List.class, DataResource.class); + List<DataResource> resultList = mapper.readValue(result.getResponse().getContentAsString(), mapCollectionType); HashSet<Long> versions = new HashSet<>(); - for (MetadataSchemaRecord item : resultList) { - versions.add(item.getSchemaVersion()); + for (DataResource item : resultList) { + versions.add(Long.parseLong(item.getVersion())); } Assert.assertEquals(version, versions.size()); for (long index = 1; index <= version; index++) { @@ -1553,7 +1621,7 @@ public void testLandingPage4SchemaUnknownID() throws Exception { @Test public void testLandingPage4SchemaWithMetadataDocumentId() throws Exception { String schemaId = "metadata_document_id"; - ingestSchemaRecord(schemaId); + ingestXmlDataResource(schemaId); String documentId = createKitMetadataRecord(schemaId); MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + documentId) @@ -1640,17 +1708,11 @@ public void testLandingPage4Schema() throws Exception { .andExpect(status().isOk()); } - private void ingestSchemaRecord(String schemaId) throws Exception { - MetadataSchemaRecord schemaRecord = new MetadataSchemaRecord(); - schemaRecord.setSchemaId(schemaId); - schemaRecord.setLabel(LABEL); - schemaRecord.setDefinition(DEFINITION); - schemaRecord.setComment(COMMENT); - schemaRecord.setSchemaVersion(1l); - schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + private void ingestXmlDataResource(String schemaId) throws Exception { + DataResource record = createDataResource(schemaId); ObjectMapper mapper = new ObjectMapper(); - MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). @@ -1658,16 +1720,31 @@ private void ingestSchemaRecord(String schemaId) throws Exception { file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } + private void ingestJsonDataResource(String schemaId) throws Exception { + DataResource record = createDataResource(schemaId); + record.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); + record.getFormats().clear(); + record.getFormats().add(MediaType.APPLICATION_JSON_VALUE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", JSON_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + private void ingestSchemaRecord() throws Exception { DataResource dataResource = DataResource.factoryNewDataResource(SCHEMA_ID); dataResource.getCreators().add(Agent.factoryAgent(null, "SELF")); - dataResource.getTitles().add(Title.factoryTitle(MediaType.APPLICATION_XML.toString(), Title.TYPE.OTHER)); + dataResource.getTitles().add(Title.factoryTitle(LABEL, null)); dataResource.setPublisher("SELF"); Instant now = Instant.now(); dataResource.setPublicationYear(Integer.toString(Calendar.getInstance().get(Calendar.YEAR))); - dataResource.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.XML + DataResourceRecordUtil.SCHEMA_SUFFIX)); + dataResource.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); dataResource.getDates().add(Date.factoryDate(now, Date.DATE_TYPE.CREATED)); - dataResource.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.XML.name()); + dataResource.getFormats().add(MediaType.APPLICATION_XML_VALUE); dataResource.setLastUpdate(now); dataResource.setState(DataResource.State.VOLATILE); dataResource.setVersion("1"); @@ -1675,9 +1752,9 @@ private void ingestSchemaRecord() throws Exception { aclEntries.add(new AclEntry("test", PERMISSION.READ)); aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); Set<Description> descriptions = dataResource.getDescriptions(); - descriptions.add(Description.factoryDescription("other", Description.TYPE.OTHER)); - descriptions.add(Description.factoryDescription("abstract", Description.TYPE.ABSTRACT)); - descriptions.add(Description.factoryDescription("technical info", Description.TYPE.TECHNICAL_INFO)); + descriptions.add(Description.factoryDescription(LABEL, Description.TYPE.OTHER)); + descriptions.add(Description.factoryDescription(COMMENT, Description.TYPE.ABSTRACT)); + descriptions.add(Description.factoryDescription(DEFINITION, Description.TYPE.TECHNICAL_INFO)); descriptions.add(Description.factoryDescription("not used yet", Description.TYPE.METHODS)); ContentInformation ci = ContentInformation.createContentInformation( SCHEMA_ID, "schema.xsd", (String[]) null); @@ -1698,7 +1775,7 @@ private void ingestSchemaRecord() throws Exception { SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(dataResource.getId()); schemaRecord.setVersion(1l); - schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.valueOf(dataResource.getFormats().iterator().next())); + schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); schemaRecord.setSchemaDocumentUri(ci.getContentUri()); schemaRecord.setDocumentHash(ci.getHash()); schemaRecordDao.save(schemaRecord); @@ -1713,15 +1790,8 @@ private void ingestSchemaRecord() throws Exception { } private void ingestSchemaWithVersion(String schemaId, long version) throws Exception { - MetadataSchemaRecord record = new MetadataSchemaRecord(); - record.setComment(COMMENT + version); - record.setSchemaId(schemaId); - record.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); - record.setMimeType(MediaType.APPLICATION_XML.toString()); - Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("test", PERMISSION.READ)); - aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + DataResource record = createDataResource(schemaId); + setComment(record, COMMENT + version); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1755,8 +1825,8 @@ private void ingestSchemaWithVersion(String schemaId, long version) throws Excep } String body = result.getResponse().getContentAsString(); - record = mapper.readValue(body, MetadataSchemaRecord.class); - Long versionAfter = record.getSchemaVersion(); + record = mapper.readValue(body, DataResource.class); + Long versionAfter = Long.parseLong(record.getVersion()); Assert.assertEquals("Wrong version created!", version, (long) versionAfter); } @@ -1767,8 +1837,8 @@ private void ingestNewSchemaRecord(String schemaId, long version) throws Excepti String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - record.setComment(COMMENT + version); + DataResource record = mapper.readValue(body, DataResource.class); + setComment(record, COMMENT + version); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -1783,8 +1853,10 @@ private void ingestNewSchemaRecord(String schemaId, long version) throws Excepti private DataResource createDataResource(String id) { DataResource record = new DataResource(); record.setId(id); + setTitle(record, id); + setComment(record, COMMENT); + setDefinition(record, DEFINITION); // mandatory element title has to be set - record.getTitles().add(Title.factoryTitle(id)); record.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.XML + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); record.getFormats().add(MediaType.APPLICATION_XML.toString()); Set<AclEntry> aclEntries = new HashSet<>(); @@ -1835,7 +1907,7 @@ private void testForNextVersion(String first, String second) { int firstVersion = Integer.parseInt(first.substring(index + 1)); int secondVersion = Integer.parseInt(second.substring(index + 1)); Assert.assertEquals(firstVersion + 1, secondVersion); - Assert.assertEquals(first.substring(0, index), second.substring(0, index)); +// Assert.assertEquals(first.substring(0, index), second.substring(0, index)); } public static boolean isSameSetOfAclEntries(Set<AclEntry> firstSet, Set<AclEntry> secondSet) { @@ -1863,7 +1935,18 @@ public static boolean isPartOfAclEntries(AclEntry entry, Set<AclEntry> allEntrie return isPart; } + private static void validateDataResources(String first, String second) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + DataResource firstRecord = mapper.readValue(first, DataResource.class); + DataResource secondRecord = mapper.readValue(second, DataResource.class); + validateDataResources(firstRecord, secondRecord); + } + private static void validateDataResources(DataResource first, DataResource second) { + validateDataResources(first, second, false); + } + + private static void validateDataResources(DataResource first, DataResource second, boolean update) { if (first == second) { return; } @@ -1875,7 +1958,11 @@ private static void validateDataResources(DataResource first, DataResource secon Assert.assertEquals(first.getIdentifier().getValue(), second.getIdentifier().getValue()); Assert.assertEquals(first.getIdentifier().getIdentifierType(), second.getIdentifier().getIdentifierType()); Assert.assertEquals(first.getLanguage(), second.getLanguage()); - Assert.assertEquals(first.getLastUpdate(), second.getLastUpdate()); + if (update) { + Assert.assertTrue(first.getLastUpdate().isBefore(second.getLastUpdate())); + }else { + Assert.assertEquals(first.getLastUpdate(), second.getLastUpdate()); + } Assert.assertEquals(first.getPublicationYear(), second.getPublicationYear()); Assert.assertEquals(first.getPublisher(), second.getPublisher()); Assert.assertEquals(first.getResourceType().getValue(), second.getResourceType().getValue()); @@ -1886,7 +1973,12 @@ private static void validateDataResources(DataResource first, DataResource secon validateIdentifierSets(first.getAlternateIdentifiers(), second.getAlternateIdentifiers()); validateContributors(first.getContributors(), second.getContributors()); validateCreators(first.getCreators(), second.getCreators()); - validateDates(first.getDates(), second.getDates()); + if (update) { + validateCreateDates(first.getDates(), second.getDates()); + validateUpdateDates(first.getDates(), second.getDates()); + } else { + validateDates(first.getDates(), second.getDates()); + } validateDescriptions(first.getDescriptions(), second.getDescriptions()); validateStrings( first.getFormats(), second.getFormats()); @@ -1978,8 +2070,8 @@ private static void validateCreators(Set<Agent> first, Set<Agent> second) { for (Agent item : first) { identical = false; for (Agent item2 : copy) { - identical = (item.getFamilyName().equals(item2.getFamilyName())) - && (item.getGivenName().equals(item2.getGivenName())) + identical = ((item.getFamilyName() == item2.getFamilyName()) || item.getFamilyName().equals(item2.getFamilyName())) + && ((item.getGivenName() == item2.getGivenName()) || item.getGivenName().equals(item2.getGivenName())) && validateStrings(item.getAffiliations(), item2.getAffiliations()); if (identical) { copy.remove(item2); @@ -2012,6 +2104,46 @@ private static void validateDates(Set<Date> first, Set<Date> second) { } } + private static void validateCreateDates(Set<Date> first, Set<Date> second) { + if (first == second) { + return; + } + boolean identical; + for (Date item : first) { + if (item.getType() == Date.DATE_TYPE.CREATED) { + identical = false; + for (Date item2 : second) { + identical = (item.getType() == item2.getType()) + && (item.getValue().equals(item2.getValue())); + if (identical) { + break; + } + } + Assert.assertTrue(identical); + } + } + } + + private static void validateUpdateDates(Set<Date> first, Set<Date> second) { + if (first == second) { + return; + } + boolean isBefore; + for (Date item : first) { + if (item.getType() == Date.DATE_TYPE.UPDATED) { + isBefore = false; + for (Date item2 : second) { + isBefore = (item.getType() == item2.getType()) + && (item.getValue().isBefore(item2.getValue())); + if (isBefore) { + break; + } + } + Assert.assertTrue(isBefore); + } + } + } + private static void validateDescriptions(Set<Description> first, Set<Description> second) { if (first == second) { return; @@ -2023,8 +2155,8 @@ private static void validateDescriptions(Set<Description> first, Set<Description for (Description item : first) { identical = false; for (Description item2 : copy) { - identical = (item.getDescription().equals(item2.getDescription())) - && (item.getLang().equals(item2.getLang())) + identical = ((item.getDescription() == item2.getDescription()) || item.getDescription().equals(item2.getDescription())) + && ((item.getLang() == item2.getLang()) || item.getLang().equals(item2.getLang())) && (item.getType() == item2.getType()); if (identical) { copy.remove(item2); @@ -2035,6 +2167,44 @@ private static void validateDescriptions(Set<Description> first, Set<Description } } + private static void validateDescriptions(DataResource record, + String label, + String definition, + String comment) { + if (record == null + && label == null + && definition == null + && comment == null) { + return; + } + boolean titleValidated = false; + for (Title item : record.getTitles()) { + if ((item.getValue() == label) || item.getTitleType() == null) { + if (item.getValue().equals(label)) { + titleValidated = true; + break; + } + } + } + Assert.assertTrue(titleValidated); + boolean definitionValidated = false; + boolean commentValidated = false; + for (Description item : record.getDescriptions()) { + if (item.getType() == Description.TYPE.TECHNICAL_INFO) { + if ((item.getDescription() == definition) || item.getDescription().equals(definition)) { + definitionValidated = true; + } + } + if (item.getType() == Description.TYPE.OTHER) { + if ((item.getDescription() == comment) || item.getDescription().equals(comment)) { + commentValidated = true; + } + } + } + Assert.assertTrue(definitionValidated); + Assert.assertTrue(commentValidated); + } + private static boolean validateStrings(Set<String> first, Set<String> second) { if (first == second) { return true; @@ -2091,7 +2261,7 @@ private static void validateTitles(Set<Title> first, Set<Title> second) { identical = false; for (Title item2 : copy) { identical = (item.getTitleType() == item2.getTitleType()) - && (item.getValue().equals(item2.getValue())); + && ((item.getValue() == item2.getValue()) || item.getValue().equals(item2.getValue())); if (identical) { copy.remove(item2); break; @@ -2101,4 +2271,78 @@ private static void validateTitles(Set<Title> first, Set<Title> second) { } } + private static String getTitle(DataResource record) { + String returnValue = null; + for (Title item : record.getTitles()) { + if (item.getTitleType() == null) { + returnValue = item.getValue(); + break; + } + } + return returnValue; + } + + private static void setTitle(DataResource record, String title) { + boolean addTitle = true; + for (Title item : record.getTitles()) { + if (item.getTitleType() == null) { + item.setValue(title); + addTitle = false; + break; + } + } + if (addTitle) { + record.getTitles().add(Title.factoryTitle(title, null)); + } + } + + private static String getDefinition(DataResource record) { + String returnValue = null; + for (Description item : record.getDescriptions()) { + if (item.getType() == Description.TYPE.TECHNICAL_INFO) { + returnValue = item.getDescription(); + break; + } + } + return returnValue; + } + + private static void setDefinition(DataResource record, String definition) { + boolean addDefinition = true; + for (Description item : record.getDescriptions()) { + if (item.getType() == Description.TYPE.TECHNICAL_INFO) { + item.setDescription(definition); + addDefinition = false; + break; + } + } + if (addDefinition) { + record.getDescriptions().add(Description.factoryDescription(definition, Description.TYPE.TECHNICAL_INFO)); + } + } + + private static String getComment(DataResource record) { + String returnValue = null; + for (Description item : record.getDescriptions()) { + if (item.getType() == Description.TYPE.OTHER) { + returnValue = item.getDescription(); + break; + } + } + return returnValue; + } + + private static void setComment(DataResource record, String comment) { + boolean addComment = true; + for (Description item : record.getDescriptions()) { + if (item.getType() == Description.TYPE.OTHER) { + item.setDescription(comment); + addComment = false; + break; + } + } + if (addComment) { + record.getDescriptions().add(Description.factoryDescription(comment, Description.TYPE.OTHER)); + } + } } From 5501bebdd1eaccd3ad1bb53c45cab596f8ee8021 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 5 Mar 2024 17:08:43 +0100 Subject: [PATCH 010/181] Fix some tests. Still work in progress. --- .../util/DataResourceRecordUtil.java | 212 +++++++++--------- .../test/SchemaRegistryControllerTestV2.java | 104 +++++---- 2 files changed, 168 insertions(+), 148 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 30233281..7b72d901 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -83,6 +83,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.function.UnaryOperator; +import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -96,6 +97,7 @@ import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.web.client.HttpClientErrorException; @@ -143,8 +145,6 @@ public class DataResourceRecordUtil { public static final String METADATA_SUFFIX = "_Metadata"; public static final String XML_METADATA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.XML + METADATA_SUFFIX; public static final String JSON_METADATA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.JSON + METADATA_SUFFIX; - - DataResourceRecordUtil() { //Utility class @@ -371,8 +371,7 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica boolean noChanges = false; if (document != null) { - metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - validateMetadataDocument(applicationProperties, metadataRecord, document); + validateMetadataDocument(applicationProperties, document, dataResource.getId(), Long.valueOf(dataResource.getVersion())); ContentInformation info; String fileName = document.getOriginalFilename(); @@ -876,64 +875,54 @@ private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentif * Validate metadata document with given schema. * * @param metastoreProperties Configuration for accessing services - * @param metadataRecord metadata of the document. + * @param contentInfo metadata of the document. * @param document document */ private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, - DataResource metadataRecord, - MultipartFile document) { - LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, metadataRecord, document); - if (document == null || document.isEmpty()) { - String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - boolean validationSuccess = false; - StringBuilder errorMessage = new StringBuilder(); - if (metastoreProperties.getSchemaRegistries().isEmpty() || metadataRecord.getSchema().getIdentifierType() != IdentifierType.INTERNAL) { - LOG.trace(LOG_SCHEMA_REGISTRY); - if (schemaConfig != null) { - try { - validateMetadataDocument(schemaConfig, document, metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); - validationSuccess = true; - } catch (Exception ex) { - String message = "Error validating document!"; - LOG.error(message, ex); - errorMessage.append(ex.getMessage()).append("\n"); - } - } else { - throw new CustomInternalServerError("No schema registries defined! "); + MultipartFile document, + ContentInformation contentInfo) { + try { + LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, contentInfo, document); + if (document == null || document.isEmpty()) { + String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); } - } else { - for (String schemaRegistry : metastoreProperties.getSchemaRegistries()) { - LOG.trace(LOG_FETCH_SCHEMA, schemaRegistry); - URI schemaRegistryUri = URI.create(schemaRegistry); - UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(schemaRegistryUri.getScheme()).host(schemaRegistryUri.getHost()).port(schemaRegistryUri.getPort()).pathSegment(schemaRegistryUri.getPath(), PATH_SCHEMA, metadataRecord.getSchema().getIdentifier(), "validate").queryParam("version", metadataRecord.getSchemaVersion()); - - URI finalUri = builder.build().toUri(); - - try { - HttpStatus status = SimpleServiceClient.create(finalUri.toString()).withBearerToken(guestToken).accept(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).withFormParam("document", document.getInputStream()).postForm(MediaType.MULTIPART_FORM_DATA); - - if (Objects.equals(HttpStatus.NO_CONTENT, status)) { - LOG.trace("Successfully validated document against schema {} in registry {}.", metadataRecord.getSchema().getIdentifier(), schemaRegistry); - validationSuccess = true; - break; + URI pathToFile = URI.create(contentInfo.getContentUri()); + switch (pathToFile.getScheme()) { + case "file": + // check file + Path schemaDocumentPath = Paths.get(pathToFile); + if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { + LOG.trace("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); + String errorMessage = "Schema document on server either does not exist or is no file or is not readable."; + throw new CustomInternalServerError(errorMessage); } - } catch (HttpClientErrorException ce) { - //not valid - String message = "Failed to validate metadata document against schema " + metadataRecord.getSchema().getIdentifier() + " at '" + schemaRegistry + "' with status " + ce.getStatusCode() + "."; - LOG.error(message, ce); - errorMessage.append(message).append("\n"); - } catch (IOException | RestClientException ex) { - String message = String.format(LOG_ERROR_ACCESS, schemaRegistry); - LOG.error(message, ex); - errorMessage.append(message).append("\n"); - } + byte[] schemaDocument = FileUtils.readFileToByteArray(schemaDocumentPath.toFile()); + IValidator applicableValidator; + applicableValidator = getValidatorForRecord(metastoreProperties, contentInfo.getMediaType(), schemaDocument); + if (applicableValidator == null) { + String message = "No validator found for schema type " + contentInfo.getMediaType(); + LOG.error(message); + throw new UnprocessableEntityException(message); + } else { + LOG.trace("Validator found."); + DataResource parent = contentInfo.getParentResource(); + LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", parent.getId(), parent.getVersion(), applicableValidator); + if (!applicableValidator.validateMetadataDocument(schemaDocumentPath.toFile(), document.getInputStream())) { + LOG.warn("Metadata document validation failed. -> " + applicableValidator.getErrorMessage()); + throw new UnprocessableEntityException(applicableValidator.getErrorMessage()); + } + } + LOG.trace("Metadata document validation succeeded."); + break; + case "http": + case "https": + default: + throw new CustomInternalServerError("Protocol of schema ('" + pathToFile.getScheme() + "') is not supported yet!"); } - } - if (!validationSuccess) { - throw new UnprocessableEntityException(errorMessage.toString()); + } catch (IOException ex) { + java.util.logging.Logger.getLogger(DataResourceRecordUtil.class.getName()).log(Level.SEVERE, null, ex); } } @@ -944,6 +933,7 @@ public static DataResource getRecordById(MetastoreConfiguration metastorePropert public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metastoreProperties, String recordId, Long version) throws ResourceNotFoundException { + LOG.trace("Obtaining schema record with id {} and version {}.", recordId, version); //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN long nano = System.nanoTime() / 1000000; long nano2; @@ -951,7 +941,7 @@ public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metast try { dataResource = metastoreProperties.getDataResourceService().findAllVersions(recordId, null); } catch (ResourceNotFoundException ex) { - ex.setDetail("Metadata document with ID '" + recordId + "' doesn't exist!"); + ex.setDetail("Document with ID '" + recordId + "' doesn't exist!"); throw ex; } nano2 = System.nanoTime() / 1000000; @@ -977,7 +967,7 @@ public static ContentInformation getContentInformationByIdAndVersion(MetastoreCo public static ContentInformation getContentInformationByIdAndVersion(MetastoreConfiguration metastoreProperties, String recordId, Long version) throws ResourceNotFoundException { - LOG.trace("Obtaining metadata record with id {} and version {}.", recordId, version); + LOG.trace("Obtaining content information record with id {} and version {}.", recordId, version); return metastoreProperties.getContentInformationService().getContentInformation(recordId, null, version); } @@ -1412,6 +1402,37 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast LOG.trace("Schema document is valid!"); } + private static IValidator getValidatorForRecord(MetastoreConfiguration metastoreProperties, String mimeType, byte[] schemaDocument) { + IValidator applicableValidator = null; + + //obtain/guess record type + if (mimeType == null) { + String formatDetected = SchemaUtils.guessMimetype(schemaDocument); + if (formatDetected == null) { + String message = "Unable to detect schema type automatically. Please provide a valid type"; + LOG.error(message); + throw new UnprocessableEntityException(message); + } else { + String type; + if (formatDetected.contains("json")) { + type = JSON + SCHEMA_SUFFIX; + } else { + type = XML + SCHEMA_SUFFIX; + } + mimeType = formatDetected; + LOG.debug("Automatically detected mimetype of schema: '{}' -> '{}'.", formatDetected, type); + } + } + for (IValidator validator : metastoreProperties.getValidators()) { + if (validator.supportsMimetype(mimeType)) { + applicableValidator = validator.getInstance(); + LOG.trace("Found validator for mime type: '{}'", mimeType); + return applicableValidator; + } + } + return applicableValidator; + } + private static IValidator getValidatorForRecord(MetastoreConfiguration metastoreProperties, DataResource schemaRecord, byte[] schemaDocument) { IValidator applicableValidator = null; //obtain/guess record type @@ -1479,36 +1500,26 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu return metadataRecord; } -// /** -// * Validate metadata document with given schema. In case of an error a runtime -// * exception is thrown. -// * -// * @param metastoreProperties Configuration properties. -// * @param document Document to validate. -// * @param schemaId SchemaId of schema. -// * @param version Version of the document. -// */ -// public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, -// MultipartFile document, -// String schemaId, -// Long version) { -// LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); -// if (schemaId == null) { -// String message = "Missing schemaID. Returning HTTP BAD_REQUEST."; -// LOG.error(message); -// throw new BadArgumentException(message); -// } -// -// long nano1 = System.nanoTime() / 1000000; -// ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(schemaId); -// SchemaRecord schemaRecord = getSchemaRecord(resourceIdentifier, version); -// long nano2 = System.nanoTime() / 1000000; -// validateMetadataDocument(metastoreProperties, document, schemaRecord); -// long nano3 = System.nanoTime() / 1000000; -// LOG.info("Validate document(schemaId,version), {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1); -// -// cleanUp(schemaRecord); -// } + /** + * Validate metadata document with given schema. In case of an error a runtime + * exception is thrown. + * + * @param metastoreProperties Configuration properties. + * @param document Document to validate. + * @param schemaId SchemaId of schema. + * @param version Version of the document. + */ + public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, + MultipartFile document, + String schemaId, + Long version) { + LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); + DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); + ContentInformation contentInfo = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaRecord.getId(), Long.valueOf(schemaRecord.getVersion())); + validateMetadataDocument(metastoreProperties, document, contentInfo); + + cleanUp(contentInfo); + } // // /** // * Validate metadata document with given schema. In case of an error a runtime @@ -1594,6 +1605,7 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu // } // LOG.trace("Metadata document validation succeeded."); // } + /** * Gets SchemaRecord from identifier. Afterwards there should be a clean up. * @@ -1672,27 +1684,17 @@ private static SchemaRecord prepareResourceFromUrl(ResourceIdentifier identifier * * @param schemaRecord Schema record. */ - public static void cleanUp(SchemaRecord schemaRecord) { + public static void cleanUp(ContentInformation schemaRecord) { LOG.trace("Clean up {}", schemaRecord); - if (schemaRecord == null || schemaRecord.getSchemaDocumentUri() == null) { + if (schemaRecord == null || schemaRecord.getContentUri() == null) { String message = "Missing resource locator for schema."; LOG.error(message); } else { - String pathToSchemaDocument = fixRelativeURI(schemaRecord.getSchemaDocumentUri()); - List<Url2Path> findByUrl = url2PathDao.findByPath(pathToSchemaDocument); - if (findByUrl.isEmpty()) { - if (LOG.isTraceEnabled()) { - LOG.trace(LOG_SEPARATOR); - Page<Url2Path> page = url2PathDao.findAll(PageRequest.of(0, 100)); - LOG.trace("List '{}' of '{}'", page.getSize(), page.getTotalElements()); - LOG.trace(LOG_SEPARATOR); - page.getContent().forEach(item -> LOG.trace("- {}", item)); - LOG.trace(LOG_SEPARATOR); - } - // Remove downloaded file - String uri = schemaRecord.getSchemaDocumentUri(); - Path pathToFile = Paths.get(URI.create(uri)); - DownloadUtil.removeFile(pathToFile); + URI uri = URI.create(schemaRecord.getContentUri()); + if (!uri.getScheme().equals("file")) { + // remove downloaded file + } else { + // nothing to do } } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 47353bd8..c78f2a7f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -584,44 +584,47 @@ public void testCreateTwoVersionsOfSchemaRecord() throws Exception { @Test public void testGetSchemaRecordByIdWithoutVersion() throws Exception { - ingestSchemaRecord(); + String schemaId = "testGetSchemaRecordByIdWithoutVersion".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); - Assert.assertEquals(SCHEMA_ID, result.getId()); + Assert.assertEquals(schemaId, result.getId()); //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema // Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); } @Test public void testGetSchemaRecordByIdWithVersion() throws Exception { - ingestSchemaRecord(); + String schemaId = "testGetSchemaRecordByIdWithVersion".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").param("version", "1").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "1").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); - Assert.assertEquals(SCHEMA_ID, result.getId()); + Assert.assertEquals(schemaId, result.getId()); // Assert.assertNotEquals("file:///tmp/dc.xsd", result.getSchemaDocumentUri()); } @Test public void testGetSchemaRecordByIdWithInvalidId() throws Exception { - ingestSchemaRecord(); + String schemaId = "testGetSchemaRecordByIdWithInvalidId".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "cd"). header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); - Assert.assertTrue("Try to access invalid schema ID!", result.getResponse().getContentAsString().contains("Schema document with ID 'cd' doesn't exist!")); + Assert.assertTrue("Try to access invalid schema ID!", result.getResponse().getContentAsString().contains("Document with ID 'cd' doesn't exist!")); } @Test public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { - String schemaId = SCHEMA_ID; - ingestSchemaRecord(); + String schemaId = "testGetSchemaRecordByIdWithInvalidVersion".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). param("version", "13"). header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). @@ -633,8 +636,9 @@ public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { @Test public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { - ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String schemaId = "testFindRecordsBySchemaIdWithAlternateEndpoint".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); @@ -643,8 +647,9 @@ public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { @Test public void testFindRecordsBySchemaId() throws Exception { - ingestSchemaRecord(); - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", SCHEMA_ID).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String schemaId = "testFindRecordsBySchemaId".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); @@ -706,7 +711,8 @@ public void testFindRecordsByMimeType() throws Exception { @Test public void testFindRecordsByInvalidMimeType() throws Exception { - ingestSchemaRecord(); + String schemaId = "testFindRecordsByInvalidMimeType".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("mimeType", "invalid")).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); @@ -716,7 +722,8 @@ public void testFindRecordsByInvalidMimeType() throws Exception { @Test public void testFindRecordsByUnknownSchemaId() throws Exception { - ingestSchemaRecord(); + String schemaId = "testFindRecordsByUnknownSchemaId".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH). param("schemaId", "schema_id_which_is_not_known")). andDo(print()). @@ -730,8 +737,9 @@ public void testFindRecordsByUnknownSchemaId() throws Exception { @Test public void testGetSchemaDocument() throws Exception { - ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc")).andDo(print()).andExpect(status().isOk()).andReturn(); + String schemaId = "testGetSchemaDocument".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA, content); @@ -739,50 +747,56 @@ public void testGetSchemaDocument() throws Exception { @Test public void testGetSchemaDocumentWithMissingSchemaFile() throws Exception { - ingestXmlDataResource("dc"); + String schemaId = "testGetSchemaDocumentWithMissingSchemaFile".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); String contentUri = contentInformationDao.findAll(PageRequest.of(0, 2)).getContent().get(0).getContentUri(); //delete schema file URI uri = new URI(contentUri); Files.delete(Paths.get(uri)); - this.mockMvc.perform(get(API_SCHEMA_PATH + "dc")).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); + this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId)).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); } @Test public void testValidate() throws Exception { - - ingestXmlDataResource("testValidate"); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + String schemaId = "testValidate".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test public void testValidateUnknownVersion() throws Exception { - ingestXmlDataResource("validateUnknownVersion"); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=666").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + String schemaId = "testValidateUnknownVersion".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate?version=666").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test public void testValidateKnownVersion() throws Exception { - ingestXmlDataResource("validateKnownVersion"); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + String schemaId = "testValidateKnownVersion".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test public void testValidateUnknownSchemaId() throws Exception { - ingestXmlDataResource("testValidateUnknownSchemaId"); + String schemaId = "testValidateUnknownSchemaId".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + INVALID_SCHEMA_ID + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); } @Test public void testValidateWithInvalidDocument() throws Exception { - ingestXmlDataResource("testValidateWithInvalidDocument"); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + String schemaId = "testValidateWithInvalidDocument".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test public void testValidateWithEmptyDocument() throws Exception { - ingestXmlDataResource("testValidateWithEmptyDocument"); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + String schemaId = "testValidateWithEmptyDocument".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test @@ -792,9 +806,10 @@ public void testValidateWithoutDocument() throws Exception { @Test public void testValidateWithoutValidator() throws Exception { - ingestXmlDataResource("testValidateWithoutValidator"); + String schemaId = "testValidateWithoutValidator".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test @@ -1315,8 +1330,9 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { @Test public void testUpdateRecordWithoutETag() throws Exception { - ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String schemaId = "testUpdateRecordWithoutETag".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); @@ -1324,30 +1340,32 @@ public void testUpdateRecordWithoutETag() throws Exception { record.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionRequired()).andReturn(); } @Test public void testUpdateRecordWithWrongETag() throws Exception { - ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String schemaId = "testUpdateRecordWithWrongETag".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag") + "unknown"; String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + "dc"). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionFailed()).andReturn(); } @Test public void testUpdateRecordWithoutBody() throws Exception { - ingestSchemaRecord(); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "dc").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String schemaId = "testUpdateRecordWithoutBody".toLowerCase(Locale.getDefault()); + ingestXmlDataResource(schemaId); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); - this.mockMvc.perform(put(API_SCHEMA_PATH + "dc").header("If-Match", etag).contentType(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).content("{}")).andDo(print()).andExpect(status().isUnsupportedMediaType()).andReturn(); + this.mockMvc.perform(put(API_SCHEMA_PATH + schemaId).header("If-Match", etag).contentType(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).content("{}")).andDo(print()).andExpect(status().isUnsupportedMediaType()).andReturn(); } @Test From 028b89bdf1111fd73c68a2b02fc92c01ddad02ff Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 6 Mar 2024 09:12:16 +0100 Subject: [PATCH 011/181] Most tests for schemas successful. --- ...TestAccessWithAuthenticationEnabledV2.java | 437 ++++++++++++++++++ .../test/SchemaRegistryControllerTestV2.java | 129 ++++-- 2 files changed, 515 insertions(+), 51 deletions(-) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java new file mode 100644 index 00000000..4f0dfdde --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java @@ -0,0 +1,437 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.IDataRecordDao; +import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import edu.kit.datamanager.util.AuthenticationHelper; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import org.javers.core.Javers; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41423"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_schema_v2_accesswithaai;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/schema/aai/access/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/schema/aai/access/metadata"}) +@TestPropertySource(properties = {"repo.auth.enabled=true"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2 { + + private static final String API_BASE_PATH = "/api/v2"; + private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; + private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; + private static final String API_METADATA_PATH = API_BASE_PATH + "/metadata/"; + + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/schema/aai/access/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private static final String SCHEMA_ID = "my_dc_access_aai"; + private static final String INVALID_SCHEMA = "invalid_dc"; + + private String adminToken; + private String userToken; + private String otherUserToken; + private String guestToken; + + private final String adminPrincipal = "admin"; + private final String userPrincipal = "user1"; + private final String otherUserPrincipal = "test_user"; + private final String guestPrincipal = "guest"; + + private static Boolean alreadyInitialized = Boolean.FALSE; + + private MockMvc mockMvc; + @Autowired + private ApplicationProperties applicationProperties; + @Autowired + private WebApplicationContext context; + @Autowired + Javers javers = null; + @Autowired + private ILinkedMetadataRecordDao metadataRecordDao; + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private IDataRecordDao dataRecordDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + @Autowired + private IUrl2PathDao url2PathDao; + @Autowired + private MetastoreConfiguration metadataConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + @Before + public void setUp() throws Exception { + // setup mockMvc + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation).uris() + .withPort(41413)) + .build(); + adminToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(adminPrincipal, RepoUserRole.ADMINISTRATOR). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("groupid", "USERS"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(userPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + otherUserToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(otherUserPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + + guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(guestPrincipal, RepoUserRole.GUEST). + addSimpleClaim("email", "guest@kit.edu"). + addSimpleClaim("orcid", "0123-4567-89AB-CDEF"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + if (!isInitialized()) { + System.out.println("------MetadataControllerAccessTestWithAAI-------------"); + System.out.println("------" + this.metadataConfig); + System.out.println("------------------------------------------------------"); + + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + metadataRecordDao.deleteAll(); + schemaRecordDao.deleteAll(); + dataRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + url2PathDao.deleteAll(); + + try { + // Create schema only once. + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + Paths.get(TEMP_DIR_4_SCHEMAS + INVALID_SCHEMA).toFile().createNewFile(); + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + + int schemaNo = 1; + for (PERMISSION user1 : PERMISSION.values()) { + for (PERMISSION guest : PERMISSION.values()) { + ingestSchemaRecord(SCHEMA_ID + "_" + schemaNo, user1, guest); + schemaNo++; + } + } + ingestSchemaRecord4UnregisteredUsers(SCHEMA_ID + "_" + schemaNo); + } + } + + @Test + public void testCreateRecordWithoutAuthentication() throws Exception { + String schemaId = "no_authentication"; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)). + // Test with no authentication +// header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isUnauthorized()); + } + + @Test + @Ignore + public void testCreateRecordAsGuestOnly() throws Exception { + String schemaId = "guest_authentication"; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile). + // Test with guest rights only + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()). + andExpect(status().isUnauthorized()); + } + + @Test + public void testAccessRecordListAdmin() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(17))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get(API_SCHEMA_PATH + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListUser() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(13))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get(API_SCHEMA_PATH + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListGuest() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(13))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get(API_SCHEMA_PATH + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListOtherUser() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(17))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get(API_SCHEMA_PATH + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListWithoutAuthentication() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get(API_SCHEMA_PATH). + param("size", Integer.toString(200))). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get(API_SCHEMA_PATH + item.getId())). + andDo(print()). + andExpect(status().isOk()); + } + } + + /** + * Ingest metadata with 'otheruser' set permissions for admin, user and guest. + * + * @param schemaId + * @param user + * @param guest + * @throws Exception + */ + private void ingestSchemaRecord(String schemaId, PERMISSION user, PERMISSION guest) throws Exception { + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + Set<AclEntry> aclEntries = new HashSet<>(); + if (user != PERMISSION.NONE) { + aclEntries.add(new AclEntry(userPrincipal, user)); + } + if (guest != PERMISSION.NONE) { + aclEntries.add(new AclEntry(guestPrincipal, guest)); + } + if (!aclEntries.isEmpty()) { + record.setAcls(aclEntries); + } + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + } + + /** + * Ingest metadata with 'otheruser' set permissions for admin, user and guest. + * + * @param schemaId + * @throws Exception + */ + private void ingestSchemaRecord4UnregisteredUsers(String schemaId) throws Exception { + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + } + + public static synchronized boolean isInitialized() { + boolean returnValue = alreadyInitialized; + alreadyInitialized = Boolean.TRUE; + + return returnValue; + } +} diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index c78f2a7f..ea048b3c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -29,6 +29,7 @@ import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; import edu.kit.datamanager.repo.domain.Description; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.repo.domain.Scheme; import edu.kit.datamanager.repo.domain.Title; @@ -48,6 +49,7 @@ import java.util.Locale; import java.util.Set; import java.util.stream.Stream; +import net.bytebuddy.agent.VirtualMachine; import org.hamcrest.Matchers; import org.junit.Assert; import static org.junit.Assert.assertEquals; @@ -224,7 +226,7 @@ public void setUp() throws Exception { @Test public void testCreateSchemaRecord() throws Exception { - DataResource record = createDataResource("my_dc"); + DataResource record = createDataResource4Schema("my_dc"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -237,7 +239,7 @@ public void testCreateSchemaRecord() throws Exception { @Test public void testCreateSchemaRecordWithAlternateEndpoint() throws Exception { - DataResource record = createDataResource("my_dc_alternate"); + DataResource record = createDataResource4Schema("my_dc_alternate"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -251,7 +253,7 @@ public void testCreateSchemaRecordWithAlternateEndpoint() throws Exception { @Test public void testCreateSchemaRecordWithCapitalLetter() throws Exception { String schemaIDWithCapitalLetters = "myFirstTest"; - DataResource record = createDataResource(schemaIDWithCapitalLetters); + DataResource record = createDataResource4Schema(schemaIDWithCapitalLetters); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -271,7 +273,7 @@ public void testCreateSchemaRecordWithCapitalLetter() throws Exception { @Test public void testCreateRegisterSchemaRecordWithSameIdButCapitalLetter() throws Exception { String schemaIDWithCapitalLetters = "mySecondTest"; - DataResource record = createDataResource(schemaIDWithCapitalLetters); + DataResource record = createDataResource4Schema(schemaIDWithCapitalLetters); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -298,7 +300,7 @@ public void testCreateRegisterSchemaRecordWithSameIdButCapitalLetter() throws Ex //@Test public void testCreateSchemaRecordWithIdentifierWithoutType() throws Exception { - DataResource record = createDataResource("my_dc_without_type"); + DataResource record = createDataResource4Schema("my_dc_without_type"); Identifier ri = Identifier.factoryIdentifier("any", null); record.getAlternateIdentifiers().add(ri); ObjectMapper mapper = new ObjectMapper(); @@ -313,7 +315,7 @@ public void testCreateSchemaRecordWithIdentifierWithoutType() throws Exception { @Test public void testCreateSchemaRecordWithoutMimeType() throws Exception { - DataResource record = createDataResource("my_dc_2"); + DataResource record = createDataResource4Schema("my_dc_2"); record.getFormats().clear(); ObjectMapper mapper = new ObjectMapper(); @@ -327,7 +329,7 @@ public void testCreateSchemaRecordWithoutMimeType() throws Exception { @Test public void testCreateSchemaRecordWithoutContentType() throws Exception { - DataResource record = createDataResource("my_dc_3"); + DataResource record = createDataResource4Schema("my_dc_3"); record.getFormats().clear(); record.setResourceType(null); ObjectMapper mapper = new ObjectMapper(); @@ -342,7 +344,7 @@ public void testCreateSchemaRecordWithoutContentType() throws Exception { @Test public void testCreateSchemaRecordWithLocationUri() throws Exception { - DataResource record = createDataResource("my_dc_new"); + DataResource record = createDataResource4Schema("my_dc_new"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -363,7 +365,7 @@ public void testCreateSchemaRecordWithLocationUri() throws Exception { @Test public void testCreateInvalidSchemaRecord() throws Exception { - DataResource record = createDataResource(INVALID_SCHEMA_ID); + DataResource record = createDataResource4Schema(INVALID_SCHEMA_ID); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -376,7 +378,7 @@ public void testCreateInvalidSchemaRecord() throws Exception { @Test public void testCreateSchemaRecordWithEmptyAclSid() throws Exception { - DataResource record = createDataResource("my_dc_empty_sid"); + DataResource record = createDataResource4Schema("my_dc_empty_sid"); Set<AclEntry> aclEntries = new HashSet<>(); aclEntries.add(new AclEntry(null, PERMISSION.ADMINISTRATE)); record.setAcls(aclEntries); @@ -428,7 +430,7 @@ public void testCreateEmptyMetadataSchemaRecord() throws Exception { // @Test public void testCreateSchemaRecordFromExternal() throws Exception { - DataResource record = createDataResource("my_dc_from_extern"); + DataResource record = createDataResource4Schema("my_dc_from_extern"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -447,7 +449,7 @@ public MockHttpServletRequest postProcessRequest(MockHttpServletRequest mhsr) { //@Test @ToDo Set external remote address. public void testCreateSchemaRecordUpdateFromExternal() throws Exception { - DataResource record = createDataResource("my_dcExt"); + DataResource record = createDataResource4Schema("my_dcExt"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -462,7 +464,7 @@ public void testCreateSchemaRecordUpdateFromExternal() throws Exception { @Test public void testCreateSchemaRecordWrongType() throws Exception { - DataResource record = createDataResource("my_dc"); + DataResource record = createDataResource4Schema("my_dc"); record.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.JSON + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); ObjectMapper mapper = new ObjectMapper(); @@ -476,7 +478,7 @@ public void testCreateSchemaRecordWrongType() throws Exception { @Test public void testCreateSchemaRecordGuessingType() throws Exception { - DataResource record = createDataResource("my_dc"); + DataResource record = createDataResource4Schema("my_dc"); record.setResourceType(null); ObjectMapper mapper = new ObjectMapper(); @@ -492,7 +494,7 @@ record = mapper.readValue(res.getResponse().getContentAsString(), DataResource.c @Test public void testCreateSchemaRecordGuessingTypeFails() throws Exception { - DataResource record = createDataResource("my_dc"); + DataResource record = createDataResource4Schema("my_dc"); record.setResourceType(null); ObjectMapper mapper = new ObjectMapper(); @@ -507,7 +509,7 @@ public void testCreateSchemaRecordGuessingTypeFails() throws Exception { @Test public void testCreateSchemaRecordWithBadSchema() throws Exception { - DataResource record = createDataResource("bad_schema"); + DataResource record = createDataResource4Schema("bad_schema"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -520,7 +522,7 @@ public void testCreateSchemaRecordWithBadSchema() throws Exception { @Test public void testCreateSchemaRecordWithEmptySchema() throws Exception { - DataResource record = createDataResource("empty_schema"); + DataResource record = createDataResource4Schema("empty_schema"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -540,7 +542,7 @@ public void testCreateSchemaRecordWithoutRecord() throws Exception { @Test public void testCreateSchemaRecordWithoutSchema() throws Exception { - DataResource record = createDataResource("without_schema"); + DataResource record = createDataResource4Schema("without_schema"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -552,7 +554,7 @@ public void testCreateSchemaRecordWithoutSchema() throws Exception { public void testCreateSchemaRecordWithBadRecord() throws Exception { ObjectMapper mapper = new ObjectMapper(); - DataResource record = createDataResource(null); + DataResource record = createDataResource4Schema(null); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); @@ -564,7 +566,7 @@ public void testCreateSchemaRecordWithBadRecord() throws Exception { @Test public void testCreateTwoVersionsOfSchemaRecord() throws Exception { - DataResource record = createDataResource("my_dc_with_version"); + DataResource record = createDataResource4Schema("my_dc_with_version"); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -761,21 +763,24 @@ public void testGetSchemaDocumentWithMissingSchemaFile() throws Exception { public void testValidate() throws Exception { String schemaId = "testValidate".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test public void testValidateUnknownVersion() throws Exception { String schemaId = "testValidateUnknownVersion".toLowerCase(Locale.getDefault()); + String version = "666"; ingestXmlDataResource(schemaId); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate?version=666").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate?version=" + version).file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); + Assert.assertTrue(andReturn.getResponse().getContentAsString().contains(version)); + Assert.assertTrue(andReturn.getResponse().getContentAsString().contains(schemaId)); } @Test public void testValidateKnownVersion() throws Exception { String schemaId = "testValidateKnownVersion".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate?version=1").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNoContent()).andReturn(); } @Test @@ -789,14 +794,14 @@ public void testValidateUnknownSchemaId() throws Exception { public void testValidateWithInvalidDocument() throws Exception { String schemaId = "testValidateWithInvalidDocument".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", INVALID_KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test public void testValidateWithEmptyDocument() throws Exception { String schemaId = "testValidateWithEmptyDocument".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", "".getBytes())).andDo(print()).andExpect(status().isBadRequest()).andReturn(); } @Test @@ -809,7 +814,7 @@ public void testValidateWithoutValidator() throws Exception { String schemaId = "testValidateWithoutValidator".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId +"/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", JSON_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test @@ -819,7 +824,7 @@ public void testValidateWithMissingSchemaFile() throws Exception { // Get location of schema file. DataResource dataRes = DataResource.factoryNewDataResource(); dataRes.setId(schemaId.toLowerCase()); - String contentUri = contentInformationDao.findByParentResource(dataRes,PageRequest.of(0, 2)).getContent().get(0).getContentUri(); + String contentUri = contentInformationDao.findByParentResource(dataRes, PageRequest.of(0, 2)).getContent().get(0).getContentUri(); //delete schema file URI uri = new URI(contentUri); Files.delete(Paths.get(uri)); @@ -1207,7 +1212,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { DataResource record2 = mapper.readValue(body, DataResource.class); Assert.assertNotEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type was changed by update validateCreateDates(record.getDates(), record2.getDates()); - Assert.assertEquals(record.getId(), record2.getId()); + Assert.assertEquals(record.getId(), record2.getId()); Assert.assertEquals(2l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); validateUpdateDates(record.getDates(), record2.getDates()); @@ -1266,7 +1271,7 @@ public void testUpdateOnlyDocument() throws Exception { @Test public void testUpdateRecordWithSmallChangesInDocument() throws Exception { String schemaId = "updateRecordWithSmallChanges"; - DataResource schemaRecord = createDataResource(schemaId); + DataResource schemaRecord = createDataResource4Schema(schemaId); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); @@ -1291,7 +1296,7 @@ public void testUpdateRecordWithSmallChangesInDocument() throws Exception { @Test public void testUpdateRecordWithoutExplizitGet() throws Exception { String schemaId = "updateWithoutGet"; - DataResource record = createDataResource(schemaId); + DataResource record = createDataResource4Schema(schemaId); ObjectMapper mapper = new ObjectMapper(); @@ -1372,7 +1377,7 @@ public void testUpdateRecordWithoutBody() throws Exception { public void testCreateSchemaRecordWithUpdateWithoutChanges() throws Exception { // Test with a schema missing schema property. String schemaId = "updateWithoutChanges_xsd".toLowerCase(Locale.getDefault()); - DataResource record = createDataResource(schemaId); + DataResource record = createDataResource4Schema(schemaId); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1427,10 +1432,7 @@ public void testDeleteSchemaRecord() throws Exception { this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); // create should return conflict - SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(schemaId); - schemaRecord.setVersion(1l); - schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + DataResource schemaRecord = createDataResource4Schema(schemaId); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); @@ -1727,7 +1729,7 @@ public void testLandingPage4Schema() throws Exception { } private void ingestXmlDataResource(String schemaId) throws Exception { - DataResource record = createDataResource(schemaId); + DataResource record = createDataResource4Schema(schemaId); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1739,7 +1741,7 @@ private void ingestXmlDataResource(String schemaId) throws Exception { } private void ingestJsonDataResource(String schemaId) throws Exception { - DataResource record = createDataResource(schemaId); + DataResource record = createDataResource4Schema(schemaId); record.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); record.getFormats().clear(); record.getFormats().add(MediaType.APPLICATION_JSON_VALUE); @@ -1808,7 +1810,7 @@ private void ingestSchemaRecord() throws Exception { } private void ingestSchemaWithVersion(String schemaId, long version) throws Exception { - DataResource record = createDataResource(schemaId); + DataResource record = createDataResource4Schema(schemaId); setComment(record, COMMENT + version); ObjectMapper mapper = new ObjectMapper(); @@ -1832,6 +1834,11 @@ private void ingestSchemaWithVersion(String schemaId, long version) throws Excep if (version > 1) { // Read ETag result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String body = result.getResponse().getContentAsString(); + + DataResource oldRecord = mapper.readValue(body, DataResource.class); + setComment(oldRecord, COMMENT + version); + recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(oldRecord).getBytes()); String etag = result.getResponse().getHeader("ETag"); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). file(recordFile). @@ -1868,7 +1875,7 @@ private void ingestNewSchemaRecord(String schemaId, long version) throws Excepti andReturn(); } - private DataResource createDataResource(String id) { + public static DataResource createDataResource4Schema(String id) { DataResource record = new DataResource(); record.setId(id); setTitle(record, id); @@ -1884,24 +1891,44 @@ private DataResource createDataResource(String id) { return record; } - private String createKitMetadataRecord(String schemaId) throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(schemaId)); - record.setRelatedResource(RELATED_RESOURCE); + private DataResource createDataResource4Document(String id, String schemaId) { + DataResource record = new DataResource(); + record.setId(id); + // mandatory element title has to be set + setTitle(record, id); + record.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.XML_METADATA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); + + RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE_STRING, null, null); + relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + record.getRelatedIdentifiers().add(relatedResource); + relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaId, null, null); + relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + record.getRelatedIdentifiers().add(relatedResource); + + record.getFormats().add(MediaType.APPLICATION_XML.toString()); Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); - aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); - ObjectMapper mapper = new ObjectMapper(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); + return record; + } + private String createKitMetadataRecord(String schemaId) throws Exception { + String documentId = "kit"; + DataResource record = createDataResource4Document(documentId, schemaId); + + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). - file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); - MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + file(metadataFile)). + andDo(print()). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn(); + DataResource result = mapper.readValue(andReturn.getResponse().getContentAsString(), DataResource.class); return result.getId(); } @@ -1978,7 +2005,7 @@ private static void validateDataResources(DataResource first, DataResource secon Assert.assertEquals(first.getLanguage(), second.getLanguage()); if (update) { Assert.assertTrue(first.getLastUpdate().isBefore(second.getLastUpdate())); - }else { + } else { Assert.assertEquals(first.getLastUpdate(), second.getLastUpdate()); } Assert.assertEquals(first.getPublicationYear(), second.getPublicationYear()); From f7eb802cc86672b75e8df747bc8fdeabc94a9a06 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 6 Mar 2024 16:07:39 +0100 Subject: [PATCH 012/181] Add tests for schemas with AAI enabled. --- ...TestAccessWithAuthenticationEnabledV2.java | 1 - .../test/SchemaRegistryControllerTestV2.java | 55 ++++++++++++++----- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java index 4f0dfdde..55c215d5 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestAccessWithAuthenticationEnabledV2.java @@ -15,7 +15,6 @@ import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index ea048b3c..0b2ba0f7 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1891,7 +1891,7 @@ public static DataResource createDataResource4Schema(String id) { return record; } - private DataResource createDataResource4Document(String id, String schemaId) { + public static DataResource createDataResource4Document(String id, String schemaId) { DataResource record = new DataResource(); record.setId(id); // mandatory element title has to be set @@ -1980,18 +1980,18 @@ public static boolean isPartOfAclEntries(AclEntry entry, Set<AclEntry> allEntrie return isPart; } - private static void validateDataResources(String first, String second) throws JsonProcessingException { + public static void validateDataResources(String first, String second) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); DataResource firstRecord = mapper.readValue(first, DataResource.class); DataResource secondRecord = mapper.readValue(second, DataResource.class); validateDataResources(firstRecord, secondRecord); } - private static void validateDataResources(DataResource first, DataResource second) { + public static void validateDataResources(DataResource first, DataResource second) { validateDataResources(first, second, false); } - private static void validateDataResources(DataResource first, DataResource second, boolean update) { + public static void validateDataResources(DataResource first, DataResource second, boolean update) { if (first == second) { return; } @@ -2037,7 +2037,7 @@ private static void validateDataResources(DataResource first, DataResource secon first.getSubjects(); } - private static void validateSets(Set<AclEntry> first, Set<AclEntry> second) { + public static void validateSets(Set<AclEntry> first, Set<AclEntry> second) { if (first == second) { return; } @@ -2059,7 +2059,7 @@ private static void validateSets(Set<AclEntry> first, Set<AclEntry> second) { } } - private static void validateIdentifierSets(Set<Identifier> first, Set<Identifier> second) { + public static void validateIdentifierSets(Set<Identifier> first, Set<Identifier> second) { if (first == second) { return; } @@ -2081,7 +2081,32 @@ private static void validateIdentifierSets(Set<Identifier> first, Set<Identifier } } - private static void validateContributors(Set<Contributor> first, Set<Contributor> second) { + public static void validateRelatedIdentifierSets(Set<RelatedIdentifier> first, Set<RelatedIdentifier> second) { + if (first == second) { + return; + } + Assert.assertEquals(first.size(), second.size()); + Set<RelatedIdentifier> copy = new HashSet<>(); + copy.addAll(second); + boolean identical; + for (RelatedIdentifier item : first) { + identical = false; + for (RelatedIdentifier item2 : copy) { + identical = (item.getIdentifierType() == item2.getIdentifierType()) + && ((item.getValue() == item2.getValue()) || item.getValue().equals(item2.getValue())) + && (item.getRelationType() == item2.getRelationType()) + && ((item.getScheme() == item2.getScheme()) || (item.getScheme().getSchemeId().equals(item2.getScheme().getSchemeId()) && + item.getScheme().getSchemeUri().equals(item2.getScheme().getSchemeUri()))); + if (identical) { + copy.remove(item2); + break; + } + } + Assert.assertTrue(identical); + } + } + + public static void validateContributors(Set<Contributor> first, Set<Contributor> second) { if (first == second) { return; } @@ -2104,7 +2129,7 @@ private static void validateContributors(Set<Contributor> first, Set<Contributor } } - private static void validateCreators(Set<Agent> first, Set<Agent> second) { + public static void validateCreators(Set<Agent> first, Set<Agent> second) { if (first == second) { return; } @@ -2127,7 +2152,7 @@ private static void validateCreators(Set<Agent> first, Set<Agent> second) { } } - private static void validateDates(Set<Date> first, Set<Date> second) { + public static void validateDates(Set<Date> first, Set<Date> second) { if (first == second) { return; } @@ -2149,7 +2174,7 @@ private static void validateDates(Set<Date> first, Set<Date> second) { } } - private static void validateCreateDates(Set<Date> first, Set<Date> second) { + public static void validateCreateDates(Set<Date> first, Set<Date> second) { if (first == second) { return; } @@ -2169,7 +2194,7 @@ private static void validateCreateDates(Set<Date> first, Set<Date> second) { } } - private static void validateUpdateDates(Set<Date> first, Set<Date> second) { + public static void validateUpdateDates(Set<Date> first, Set<Date> second) { if (first == second) { return; } @@ -2189,7 +2214,7 @@ private static void validateUpdateDates(Set<Date> first, Set<Date> second) { } } - private static void validateDescriptions(Set<Description> first, Set<Description> second) { + public static void validateDescriptions(Set<Description> first, Set<Description> second) { if (first == second) { return; } @@ -2212,7 +2237,7 @@ private static void validateDescriptions(Set<Description> first, Set<Description } } - private static void validateDescriptions(DataResource record, + public static void validateDescriptions(DataResource record, String label, String definition, String comment) { @@ -2272,7 +2297,7 @@ private static boolean validateStrings(Set<String> first, Set<String> second) { return true; } - private static void validateRights(Set<Scheme> first, Set<Scheme> second) { + public static void validateRights(Set<Scheme> first, Set<Scheme> second) { if (first == second) { return; } @@ -2294,7 +2319,7 @@ private static void validateRights(Set<Scheme> first, Set<Scheme> second) { } } - private static void validateTitles(Set<Title> first, Set<Title> second) { + public static void validateTitles(Set<Title> first, Set<Title> second) { if (first == second) { return; } From a10d05fa0a044006da9d0d503969bd186c8f3c1e Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 6 Mar 2024 16:08:04 +0100 Subject: [PATCH 013/181] Start tests for MetadataController. --- .../test/MetadataControllerTestV2.java | 2379 +++++++++++++++++ 1 file changed, 2379 insertions(+) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java new file mode 100644 index 00000000..43789252 --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -0,0 +1,2379 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import edu.kit.datamanager.entities.Identifier; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.IDataRecordDao; +import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import org.javers.core.Javers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.*; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import java.util.Locale; +import org.springframework.http.MediaType; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; + +/** + * + * @author Torridity + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41421"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_md_v2;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"spring.jpa.database-platform=org.hibernate.dialect.H2Dialect"}) +@TestPropertySource(properties = {"spring.jpa.defer-datasource-initialization=true"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/md/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/md/metadata"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@TestPropertySource(properties = {"repo.search.url=http://localhost:41421"}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class MetadataControllerTestV2 { + + private static final String API_BASE_PATH = "/api/v2"; + private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; + private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; + private static final String API_METADATA_PATH = API_BASE_PATH + "/metadata/"; + + + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/md/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private static final String METADATA_RECORD_ID = "test_id"; + private static final String SCHEMA_ID = "my_dc"; + private static final String JSON_SCHEMA_ID = "my_json"; + private static final String JSON_HTTP_SCHEMA_ID = "my_json_with_http"; + private static final String JSON_HTTP_SCHEMA_ID_WITH_HASH = "my_json_with_hash"; + private static final String INVALID_SCHEMA = "invalid_dc"; + private static final String UNKNOWN_RELATED_RESOURCE = "unknownHResourceId"; + private static final String RELATED_RESOURCE_STRING = "anyResourceId"; + private static final String APACHE_2_LICENSE = "https://spdx.org/licenses/Apache-2.0"; + private static final ResourceIdentifier RELATED_RESOURCE = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING); + private static final ResourceIdentifier RELATED_RESOURCE_URL = ResourceIdentifier.factoryUrlResourceIdentifier(RELATED_RESOURCE_STRING); + private static final ResourceIdentifier RELATED_RESOURCE_2 = ResourceIdentifier.factoryInternalResourceIdentifier("anyOtherResourceId"); + private final static String DC_SCHEMA = CreateSchemaUtil.KIT_SCHEMA; + + private final static String DC_DOCUMENT = CreateSchemaUtil.KIT_DOCUMENT; + private final static String DC_DOCUMENT_SMALL_CHANGE = CreateSchemaUtil.KIT_DOCUMENT_SMALL_CHANGE; + private final static String DC_DOCUMENT_VERSION_2 = CreateSchemaUtil.KIT_DOCUMENT_VERSION_2; + private final static String DC_DOCUMENT_WRONG_NAMESPACE = CreateSchemaUtil.KIT_DOCUMENT_WRONG_NAMESPACE; + private final static String DC_DOCUMENT_INVALID = CreateSchemaUtil.KIT_DOCUMENT_INVALID_1; + + private static final String JSON_SCHEMA = "{\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}\n"; + + private final static String JSON_HTTP_SCHEMA = "{\n" + + " \"$schema\": \"http://json-schema.org/draft/2019-09/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + + private final static String JSON_HTTP_SCHEMA_WITH_HASH = "{\n" + + " \"$schema\": \"http://json-schema.org/draft/2019-09/schema#\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + private static final String JSON_DOCUMENT_VERSION_1 = "{\n" + + " \"title\": \"My first JSON document\" \n" + + "}\n"; + + private static final String JSON_DOCUMENT_VERSION_2 = "{\n" + + " \"title\": \"My updated JSON document\" \n" + + "}\n"; + + private static Boolean alreadyInitialized = Boolean.FALSE; + + private MockMvc mockMvc; + @Autowired + private WebApplicationContext context; + @Autowired + Javers javers = null; + @Autowired + private ILinkedMetadataRecordDao metadataRecordDao; + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private IDataRecordDao dataRecordDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + @Autowired + private MetastoreConfiguration metadataConfig; + @Autowired + private MetastoreConfiguration schemaConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + @Before + public void setUp() throws Exception { + System.out.println("------MetadataControllerTest--------------------------"); + System.out.println("------" + this.metadataConfig); + System.out.println("------------------------------------------------------"); + + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + metadataRecordDao.deleteAll(); + schemaRecordDao.deleteAll(); + dataRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + + try { + // setup mockMvc + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation).uris() + .withPort(41401)) + .build(); + // Create schema only once. + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + Paths.get(TEMP_DIR_4_SCHEMAS + INVALID_SCHEMA).toFile().createNewFile(); + CreateSchemaUtil.ingestKitSchemaRecord(mockMvc, SCHEMA_ID, schemaConfig.getJwtSecret()); + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Test + public void testCreateRecord() throws Exception { + String id = "testCreateRecord"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + } + + @Test + public void testCreateRecordWithHttpSchema() throws Exception { + ingestHttpJsonSchemaRecord(); + String id = "testCreateRecordWithHttpSchema"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.json", "application/json", JSON_DOCUMENT_VERSION_1.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + } + + @Test + public void testCreateRecordWithHttpSchemaAndHash() throws Exception { + ingestHttpJsonSchemaRecordWithHash(); + String id = "testCreateRecordWithHttpSchemaAndHash"; + String schemaId = JSON_HTTP_SCHEMA_ID_WITH_HASH; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.json", "application/json", JSON_DOCUMENT_VERSION_1.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + } + + @Test + public void testCreateRecordAlternateEndpoint() throws Exception { + String id = "testCreateRecordAlternateEndpoint"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + } + + @Test + public void testCreateRecordWithRelatedResourceOfTypeUrl() throws Exception { + String id = "testCreateRecordWithRelatedResourceOfTypeUrl"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE_URL); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)). + andDo(print()). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn(); + + DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record.getRelatedIdentifiers(), result.getRelatedIdentifiers()); +// Assert.assertEquals("Type of related resource should be unchanged!", record.getRelatedIdentifiers().getIdentifierType(), result.getRelatedResource().getIdentifierType()); + } + + @Test + public void testCreateRecordWithValidUrlSchema() throws Exception { + String id = "testCreateRecordWithValidUrlSchema"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// // Get URL of schema +// String schemaUrl = getSchemaUrl(SCHEMA_ID); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(schemaUrl)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + } + + @Test + public void testCreateRecordWithUrlSchemaNull() throws Exception { + String id = "testCreateRecordWithUrlSchemaNull"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setValue(null); + } + } +// MetadataRecord record = new MetadataRecord(); +// +// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(null)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + } + + @Test + public void testCreateRecordWithInvalidUrl() throws Exception { + String id = "testCreateRecordWithInvalidUrl"; + String invalidSchemaUrl = getSchemaUrl(SCHEMA_ID).substring(1); + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setValue(invalidSchemaUrl); + item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + } + } +// MetadataRecord record = new MetadataRecord(); +// // Get URL of schema and remove first character +// String invalidSchemaUrl = getSchemaUrl(SCHEMA_ID).substring(1); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(invalidSchemaUrl)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateRecordWithInvalidUrlSchema() throws Exception { + String id = "testCreateRecordWithInvalidUrlSchema"; + String schemaId = INVALID_SCHEMA; + String urlWithInvalidSchema = getSchemaUrl(SCHEMA_ID).replace(SCHEMA_ID, INVALID_SCHEMA); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setValue(urlWithInvalidSchema); + item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + } + } +// MetadataRecord record = new MetadataRecord(); +// // Get URL of schema +// String urlWithInvalidSchema = getSchemaUrl(SCHEMA_ID).replace(SCHEMA_ID, INVALID_SCHEMA); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(urlWithInvalidSchema)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateRecordWithAnyValidUrl() throws Exception { + String id = "testCreateRecordWithAnyValidUrl"; + String schemaId = INVALID_SCHEMA; + String schemaUrl = "http://anyurl.example.org/shouldNotExist"; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setValue(schemaUrl); + item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + } + } +// MetadataRecord record = new MetadataRecord(); +// // Get URL of schema +// String schemaUrl = "http://anyurl.example.org/shouldNotExist"; +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(schemaUrl)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateRecordWithId() throws Exception { + String id = "SomeValidId"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// record.setId("SomeValidId"); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateRecordWithInvalidId() throws Exception { + String id = "http://localhost:8080/d1"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// record.setId("http://localhost:8080/d1"); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateRecordWithIdTwice() throws Exception { + String id = "AnyValidId"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// record.setId("AnyValidId"); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR)) { + item.setValue(RELATED_RESOURCE_2.getIdentifier()); + } + } +// record.setRelatedResource(RELATED_RESOURCE_2CE_2); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); + } + + @Test + public void testCreateRecordWithLocationUri() throws Exception { + String id = "testCreateRecordWithLocationUri"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); +// aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); +// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + String locationUri = result.getResponse().getHeader("Location"); + String content = result.getResponse().getContentAsString(); + + ObjectMapper map = new ObjectMapper(); + MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content2 = result2.getResponse().getContentAsString(); + + Assert.assertEquals(content, content2); + } + + @Test + public void testCreateInvalidRecord() throws Exception { + String id = "testCreateInvalidRecord"; + String schemaId = INVALID_SCHEMA; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(INVALID_SCHEMA)); +// record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testCreateInvalidMetadataRecord() throws Exception { + String wrongTypeJson = "{\"id\":\"dc\",\"relatedResource\":\"anyResource\",\"createdAt\":\"right now!\"}"; + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", wrongTypeJson.getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + String wrongFormatJson = "<metadata><schemaId>dc</schemaId><type>XML</type></metadata>"; + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", wrongFormatJson.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + } + + @Test + public void testCreateEmptyMetadataSchemaRecord() throws Exception { + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", (byte[]) null); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", " ".getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + // @Test + // Test is not active as remote address seems not to work + public void testCreateRecordFromExternal() throws Exception { + String id = "testCreateRecordFromExternal"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + RequestPostProcessor rpp = new RequestPostProcessor() { + @Override + public MockHttpServletRequest postProcessRequest(MockHttpServletRequest mhsr) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + }; + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile).with(remoteAddr("any.external.domain"))).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + //@Test @ToDo Set external remote address. + public void testCreateRecordUpdateFromExternal() throws Exception { + String id = "testCreateRecordUpdateFromExternal"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier("my_dcExt")); +// record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile).with(remoteAddr("any.domain.com"))).andDo(print()).andExpect(status().isCreated()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile).with(remoteAddr("www.google.com"))).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateMetadataUnknownSchemaId() throws Exception { + String id = "testCreateMetadataUnknownSchemaId"; + String schemaId = "unknown_dc"; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier("unknown_dc")); +// record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testCreateRecordWithBadMetadata() throws Exception { + String id = "testCreateRecordWithBadMetadata"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", "<>".getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testCreateRecordWithEmptyAclSid() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry(null, PERMISSION.READ)); + record.setAcl(aclEntries); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + Assert.assertTrue(res.getResponse().getContentAsString().contains("Subject ID of ACL entry must not be null.")); + } + + @Test + public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_WRONG_NAMESPACE.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testCreateRecordWithInvalidMetadata() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_INVALID.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); + } + + @Test + public void testCreateRecordWithoutRecord() throws Exception { + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(metadataFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateRecordWithoutSchema() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateRecordWithBadRecord() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + MetadataRecord record = new MetadataRecord(); + //schemaId is missing + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(null)); + record.setRelatedResource(RELATED_RESOURCE); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateRecordWithBadRecord2() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + //related resource is missing + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateRecordWithoutDocument() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + } + + @Test + public void testCreateTwoVersionsOfSameRecord() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + + MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + + res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); + + Assert.assertTrue(res.getResponse().getContentAsString().contains("Conflict")); + Assert.assertTrue(res.getResponse().getContentAsString().contains(SCHEMA_ID)); + Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); + } + + @Test + public void testCreateTwoVersions() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + + MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + + record.setRelatedResource(RELATED_RESOURCE_2); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + + result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + } + + @Test + public void testGetRecordById() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertNotNull(result); + Assert.assertEquals(IdentifierType.URL, result.getSchema().getIdentifierType()); + String schemaUrl = result.getSchema().getIdentifier(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); + //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema + Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); + } + + @Test + public void testGetRecordByIdWithVersion() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1").header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertNotNull(result); + Assert.assertEquals(IdentifierType.URL, result.getSchema().getIdentifierType()); + String schemaUrl = result.getSchema().getIdentifier(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); + Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); + } + + @Test + public void testGetRecordByIdWithInvalidId() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + "cd"). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + Assert.assertTrue("Try to access invalid id!", result.getResponse().getContentAsString().contains("Metadata document with ID 'cd' doesn't exist!")); + } + + @Test + public void testGetRecordByIdWithInvalidVersion() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + param("version", "13"). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + Assert.assertTrue("Try to access invalid version!", result.getResponse().getContentAsString().contains("Version '13' of ID '" + metadataRecordId + "' doesn't exist!")); + } + + @Test + public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", SCHEMA_ID)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(1, result.length); + } + + @Test + public void testFindRecordsBySchemaId() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", SCHEMA_ID)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(1, result.length); + } + + @Test + public void testFindRecordsByInvalidSchemaId() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", "anyinvalidschemaid")). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindRecordsOfMultipleVersionsBySchemaId() throws Exception { + String schemaId = "multipleSchemas".toLowerCase(Locale.getDefault()); + CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V1, metadataConfig.getJwtSecret(), false, status().isCreated()); + CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V2, metadataConfig.getJwtSecret(), true, status().isOk()); + CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V3, metadataConfig.getJwtSecret(), true, status().isOk()); + ObjectMapper map = new ObjectMapper(); + String[] multipleVersions = {"1", "2", "3"}; + // Ingest 1st version of document. + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 1l, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(1, result.length); + // Ingest 2nd version of document + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 2l, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); + res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(2, result.length); + // Ingest 3rd version of document + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 3l, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); + res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(3, result.length); + } + + @Test + public void testFindRecordsByResourceId() throws Exception { + Instant oneHourBefore = Instant.now().minusSeconds(3600); + Instant twoHoursBefore = Instant.now().minusSeconds(7200); + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("resoureId", RELATED_RESOURCE.getIdentifier())).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(1, result.length); + res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", RELATED_RESOURCE.getIdentifier()).param("from", twoHoursBefore.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); + map = new ObjectMapper(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(1, result.length); + } + + @Test + public void testFindRecordsBySchemaIdANDResourceId() throws Exception { + String relatedResource = "testFindRecordsBySchemaIdANDResourceId"; + String relatedResource2 = "anotherTestFindRecordsBySchemaIdANDResourceId"; + String secondSchemaId = "schema_for_find_records_by_schema_and_resource_id"; + CreateSchemaUtil.ingestKitSchemaRecord(mockMvc, secondSchemaId, schemaConfig.getJwtSecret()); + String metadataRecordId = createDCMetadataRecordWithRelatedResource(relatedResource, SCHEMA_ID); + String metadataRecordId2 = createDCMetadataRecordWithRelatedResource(relatedResource2, SCHEMA_ID); + String metadataRecordIdv2 = createDCMetadataRecordWithRelatedResource(relatedResource, secondSchemaId); + String metadataRecordId2v2 = createDCMetadataRecordWithRelatedResource(relatedResource2, secondSchemaId); + // Looking for first schema + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", SCHEMA_ID)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + // Looking for second schema + Assert.assertEquals(2, result.length); + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", secondSchemaId)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(2, result.length); + // Looking for first AND second schema + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", SCHEMA_ID). + param("schemaId", secondSchemaId)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(4, result.length); + // Looking for first, second AND invalid schema + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", SCHEMA_ID). + param("schemaId", secondSchemaId). + param("schemaId", "invalidschemaid")). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(4, result.length); + // Looking for first, second AND invalid schema AND resource1 and resource2 + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", SCHEMA_ID). + param("schemaId", secondSchemaId). + param("schemaId", "invalidschemaid"). + param("resourceId", relatedResource). + param("resourceId", relatedResource2)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(4, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", SCHEMA_ID). + param("resourceId", relatedResource). + param("resourceId", relatedResource2)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(2, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", SCHEMA_ID). + param("resourceId", relatedResource)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(1, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", SCHEMA_ID). + param("resourceId", relatedResource2)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(1, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", secondSchemaId). + param("resourceId", relatedResource). + param("resourceId", relatedResource2)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(2, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", secondSchemaId). + param("resourceId", relatedResource)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(1, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", secondSchemaId). + param("resourceId", relatedResource2)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + Assert.assertEquals(1, result.length); + } + + @Test + public void testFindRecordsByInvalidResourceId() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", UNKNOWN_RELATED_RESOURCE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindRecordsByInvalidUploadDate() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + Instant oneHourBefore = Instant.now().minusSeconds(3600); + Instant twoHoursBefore = Instant.now().minusSeconds(7200); + + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", RELATED_RESOURCE.getIdentifier()).param("until", oneHourBefore.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", RELATED_RESOURCE.getIdentifier()).param("from", twoHoursBefore.toString()).param("until", oneHourBefore.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); + map = new ObjectMapper(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindRecordsByUnknownParameter() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", "cd")). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testGetSchemaDocument() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT; + + Assert.assertEquals(dcMetadata, content); + } + + @Test + public void testGetMetadataDocumentWithUnknownSchema() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + this.mockMvc.perform(get(API_METADATA_PATH + "unknown_dc")).andDo(print()).andExpect(status().isNotFound()).andReturn(); + } + + @Test + public void testUpdateRecord() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + } + + @Test + public void testUpdateRecordWithWrongVersion() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + record.setRecordVersion(0l); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals(Long.valueOf(2l), record2.getRecordVersion());// version should be 2 + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + // Check for old metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); + content = result.getResponse().getContentAsString(); + + dcMetadata = DC_DOCUMENT; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + } + + @Test + public void testUpdateRecordIgnoreACL() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + // Set all ACL to WRITE + for (AclEntry entry : record.getAcl()) { + entry.setPermission(PERMISSION.WRITE); + } + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(SchemaRegistryControllerTest.isSameSetOfAclEntries(record.getAcl(), record2.getAcl())); + Assert.assertFalse(SchemaRegistryControllerTest.isSameSetOfAclEntries(oldRecord.getAcl(), record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + } + + @Test + public void testUpdateRecordWithoutExplizitGet() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> acl = new HashSet<>(); + acl.add(new AclEntry("test", PERMISSION.READ)); + acl.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(acl); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + String locationUri = result.getResponse().getHeader("Location"); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); + MockMultipartFile metadataFile2 = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(locationUri). + file(recordFile2). + file(metadataFile2).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record2.getDocumentHash(), record3.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); + Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); + Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); + Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1l);// version should be 1 higher + if (record2.getAcl() != null) { + Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); + } + Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + } + + @Test + public void testUpdateRecordWithSameDocument() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); + String body = result.getResponse().getContentAsString(); + + MetadataRecord record1 = mapper.readValue(body, MetadataRecord.class); + // Update without any changes. + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals("Version shouldn't change!", record1.getRecordVersion(), record2.getRecordVersion()); + } + + @Test + public void testUpdateRecordWithSmallChangesInDocument() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); + String body = result.getResponse().getContentAsString(); + + MetadataRecord record1 = mapper.readValue(body, MetadataRecord.class); + // Update without any changes. + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals("Version should change!", record1.getRecordVersion(), record2.getRecordVersion()); + Assert.assertEquals("Version should incremented!", (long) record1.getRecordVersion(), (long) (record2.getRecordVersion() - 1l)); + } + + @Test + public void testUpdateRecordWithoutETag() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile). + file(metadataFile).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionRequired()).andReturn(); + } + + @Test + public void testUpdateRecordWithWrongETag() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag") + "unknown"; + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionFailed()).andReturn(); + } + + @Test + public void testUpdateRecordWithoutRecord() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testUpdateRecordWithoutRecord4Json() throws Exception { + // Update only Json document + ingestJsonSchemaRecord(); + String metadataRecordId = createJsonMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", JSON_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String jsonMetadata = JSON_DOCUMENT_VERSION_1; + + Assert.assertEquals(jsonMetadata, content); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isOk()).andReturn(); + content = result.getResponse().getContentAsString(); + + jsonMetadata = JSON_DOCUMENT_VERSION_2; + + Assert.assertEquals(jsonMetadata, content); + } + + @Test + public void testUpdateRecordWithoutDocument() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getMetadataDocumentUri(), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion());// version should be the same + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + String expectedRelatedResource; + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + expectedRelatedResource = record2.getRelatedResource().getIdentifier() + "_NEW"; + record2.getRelatedResource().setIdentifier(expectedRelatedResource); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + body = result.getResponse().getContentAsString(); + etag = result.getResponse().getHeader("ETag"); + + record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getMetadataDocumentUri(), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion());// version should be the same + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + Assert.assertNotEquals("Related resource should be updated!", record.getRelatedResource().getIdentifier(), record2.getRelatedResource().getIdentifier()); + Assert.assertEquals("Related resource should be updated!", expectedRelatedResource, record2.getRelatedResource().getIdentifier()); + + // Test also updating related type only... + IdentifierType expectedIdentifierType = IdentifierType.ISBN; + record2.getRelatedResource().setIdentifierType(expectedIdentifierType); + + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals(record.getDocumentHash(), record3.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record3.getCreatedAt()); + Assert.assertEquals(record.getMetadataDocumentUri(), record3.getMetadataDocumentUri()); + Assert.assertEquals(record.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); + Assert.assertEquals(record.getRecordVersion(), record3.getRecordVersion());// version should be the same + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record3.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record3.getLastUpdate())); + Assert.assertEquals("Related resource should be the same!", record2.getRelatedResource().getIdentifier(), record3.getRelatedResource().getIdentifier()); + Assert.assertEquals("Related resource type should be changed!", expectedIdentifierType, record3.getRelatedResource().getIdentifierType()); + + } + + @Test + public void testUpdateRecordWithInvalidSetting4Json() throws Exception { + String alternativeSchemaId = "testupdate"; + CreateSchemaUtil.ingestXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V1, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V2, schemaConfig.getJwtSecret(), true, status().isOk()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, alternativeSchemaId, 2l, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); + // Change only version of schema to a version which is not valid. + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, alternativeSchemaId, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + // Change to a nonexistent version of schema. + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isNotFound()); + // Change to another schema + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + } + + @Test + public void testUpdateRecordWithLicense() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + record.setLicenseUri(APACHE_2_LICENSE); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + Assert.assertNotNull(record2.getLicenseUri()); + Assert.assertTrue(record2.getLicenseUri().equals(APACHE_2_LICENSE)); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + body = result.getResponse().getContentAsString(); + + mapper = new ObjectMapper(); + record = mapper.readValue(body, MetadataRecord.class); + record.setLicenseUri(null); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals(record2.getDocumentHash(), record3.getDocumentHash()); + Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); + Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); + Assert.assertEquals((long) record2.getRecordVersion(), (long)record3.getRecordVersion());// version should be the same + if (record.getAcl() != null) { + Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); + } + Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + Assert.assertNull(record3.getLicenseUri()); + + } + + @Test + public void testDeleteRecordWithoutAuthentication() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); + //delete second time + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isPreconditionRequired()).andReturn(); + } + + @Test + public void testDeleteRecord() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String metadataRecordId = createDCMetadataRecord(); + + // Get a list of all records + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + + // Get Etag + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + // Delete record + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); + + // Delete second time + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isPreconditionRequired()).andReturn(); +// Recreation should be no problem. +// //try to create after deletion (Should return HTTP GONE) +// MetadataRecord record = new MetadataRecord(); +// record.setSchemaId("dc"); +// record.setRelatedResource(RELATED_RESOURCE); +// ObjectMapper mapper = new ObjectMapper(); +// +// MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); +// MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); +// +// this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + METADATA_RECORD_ID). +// file(recordFile). +// file(metadataFile)).andDo(print()).andExpect(status().isGone()).andReturn(); + + // List of records should be smaller afterwards + result = this.mockMvc.perform(get(API_METADATA_PATH). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); + } + + @Test + public void testGetAllVersionsOfRecord() throws Exception { + String id = null; + for (long version = 1; version <= 3; version++) { + id = ingestMetadataRecordWithVersion(id, version); + // Get version of record as array + // Read all versions + this.mockMvc.perform(get(API_METADATA_PATH).param("id", id).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))); + + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals("Expect current version '" + version + "'", (Long) version, record.getRecordVersion());// version should be 1 higher + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + id)).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT; + + if (version == 1) { + + Assert.assertTrue(content.startsWith(dcMetadata)); + +// Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + // Get version of record as array + // Read all versions (2 version2 available) + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", id).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); + mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, MetadataRecord.class); + List<MetadataRecord> resultList = mapper.readValue(result.getResponse().getContentAsString(), mapCollectionType); + HashSet<Long> versions = new HashSet<>(); + for (MetadataRecord item : resultList) { + versions.add(item.getRecordVersion()); + } + Assert.assertEquals(version, versions.size()); + for (long index = 1; index <= version; index++) { + Assert.assertTrue("Test for version: " + index, versions.contains(index)); + } + } + } + } + + @Test + public void testIssue52() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + int version = 1; + + // Test get record with one version + // Read all versions + MvcResult result = this.mockMvc + .perform(get(API_METADATA_PATH) + .param("id", metadataRecordId) + .header(HttpHeaders.ACCEPT, "application/json")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))) + .andReturn(); + Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + "\"")); + // check for higher versions which should be not available + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + version++; + metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); + // check for higher versions which should be not available + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + version++; + metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); + // check for higher versions which should be not available + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + metadataRecordId = ingestMetadataRecordWithVersion(metadataRecordId, version); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); + // check for higher versions which should be not available (if version > 2) + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isOk()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + + version++; + metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); + // Read all versions (should be still one version) + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT; +// Assert.assertEquals(dcMetadata, content); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isOk()).andReturn(); + content = result.getResponse().getContentAsString(); + + Assert.assertNotEquals(dcMetadata, content); + Assert.assertEquals("Length must differ!", dcMetadata.length() + 3, content.length()); + // check for higher versions which should be not available (if version > 2) + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "3")).andDo(print()).andExpect(status().isNotFound()); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "4")).andDo(print()).andExpect(status().isNotFound()); + } + + @Test + public void testSearchProxy() throws Exception { + + // Skip test due to Spring Security 6 + this.mockMvc.perform(MockMvcRequestBuilders.post(API_METADATA_PATH + "search?page=0&size=20") + .contentType(MediaType.APPLICATION_JSON) + .content("{}")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testSearchWithSchemaProxy() throws Exception { + + // Test for swagger definition + this.mockMvc.perform(MockMvcRequestBuilders.post(API_METADATA_PATH + "index/search?page=0&size=20") + .contentType(MediaType.APPLICATION_JSON) + .content("{}")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testSwaggerUI() throws Exception { + + // Test for swagger definition + this.mockMvc.perform(get("/v3/api-docs")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.info.title", Matchers.startsWith("MetaStore"))); + } + + @Test + public void testLandingPage4MetadataUnknownID() throws Exception { + String documentId = createDCMetadataRecord(); + + MvcResult andReturn = this.mockMvc.perform(get(API_METADATA_PATH + "anything") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/metadata-landing-page?id=anything&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testLandingPage4MetadataWithSchemaId() throws Exception { + MvcResult andReturn = this.mockMvc.perform(get(API_METADATA_PATH + SCHEMA_ID) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/metadata-landing-page?id=" + SCHEMA_ID + "&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @Test + public void testLandingPage4MetadataWrongVersion() throws Exception { + String documentId = createDCMetadataRecord(); + + MvcResult andReturn = this.mockMvc.perform(get(API_METADATA_PATH + documentId) + .queryParam("version", "2") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=2")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testLandingPage4Metadata() throws Exception { + String documentId = createDCMetadataRecord(); + + MvcResult andReturn = this.mockMvc.perform(get(API_METADATA_PATH + documentId) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get(API_METADATA_PATH + documentId) + .queryParam("version", "1") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=1")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + // Ingest a second version... + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + documentId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + andReturn = this.mockMvc.perform(get(API_METADATA_PATH + documentId) + .queryParam("version", "2") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=2")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get(API_METADATA_PATH + documentId) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + } + + @Test + public void testDeleteSchemaWithLinkedDocument() throws Exception { + String schemaId = "deleteschema"; + String metadataRecordId = "deletedocument"; + String jwtSecret = schemaConfig.getJwtSecret(); + ingestKitSchemaRecord(this.mockMvc, schemaId, jwtSecret); + ingestXmlMetadataDocument(this.mockMvc, schemaId, null, metadataRecordId, DC_DOCUMENT, jwtSecret); + // Deletion of schema shouldn't work + // Get ETag. + MvcResult result = mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etagSchema = result.getResponse().getHeader("ETag"); + this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId). + header("If-Match", etagSchema)). + andDo(print()). + andExpect(status().isConflict()). + andReturn(); + // Get Etag + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + // Delete record + result = this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag)). + andDo(print()). + andExpect(status().isNoContent()). + andReturn(); + + // Try deleting schema once more should also fail + this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId). + header("If-Match", etagSchema)). + andDo(print()). + andExpect(status().isConflict()). + andReturn(); + + // Delete second time + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + result = this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag)). + andDo(print()). + andExpect(status().isNoContent()). + andReturn(); + + // Now it should be possible to delete schema + result = this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId). + header("If-Match", etagSchema)). + andDo(print()). + andExpect(status().isNoContent()). + andReturn(); + // But it's still available + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etagSchema = result.getResponse().getHeader("ETag"); + // Remove it ones more + this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId). + header("If-Match", etagSchema)). + andDo(print()). + andExpect(status().isNoContent()). + andReturn(); + // Now it' gone + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + } + + private String createJsonMetadataRecord() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(JSON_SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); + aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", JSON_DOCUMENT_VERSION_1.getBytes()); + + MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + + return result.getId(); + } + + private String createDCMetadataRecord() throws Exception { + return createDCMetadataRecordWithRelatedResource(RELATED_RESOURCE_STRING, SCHEMA_ID); + } + + private String createDCMetadataRecordWithRelatedResource(String myRelatedResource, String schemaId) throws Exception { + ResourceIdentifier relatedResource = ResourceIdentifier.factoryInternalResourceIdentifier(myRelatedResource); + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(schemaId)); + record.setRelatedResource(relatedResource); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); + aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + + return result.getId(); + } + + /** + * Ingest new metadata document and create new version (if not already exists) + * + * @param id id of the DO to update + * @param version version of the new document. + * @return id of the DO + * @throws Exception + */ + private String ingestMetadataRecordWithVersion(String id, long version) throws Exception { + if (id == null) { + id = createDCMetadataRecord(); + } else { + // add new version + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + String newDocument = DC_DOCUMENT; + for (int i = 0; i < version; i++) { + newDocument = newDocument.concat(" "); + } + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", newDocument.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + } + return id; + } + + /** + * Ingest new metadata document (if no id is given) and/or create new version + * of record + * + * @param id id of the DO to update + * @param version version of the new document. + * @return id of the DO + * @throws Exception + */ + private String ingestNewMetadataRecord(String id, long version) throws Exception { + if (id == null) { + id = createDCMetadataRecord(); + } else { + // add new version + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + record.setRelatedResource(ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING + version)); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + } + return id; + } + + private static RequestPostProcessor remoteAddr(final String remoteAddr) { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setRemoteAddr(remoteAddr); + return request; + }; + } + + private static RequestPostProcessor putMultipart() { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setMethod("PUT"); + return request; + }; + } + + private void ingestJsonSchemaRecord() throws Exception { + MetadataSchemaRecord record = new MetadataSchemaRecord(); + record.setSchemaId(JSON_SCHEMA_ID); + record.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", JSON_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + private void ingestHttpJsonSchemaRecord() throws Exception { + String schemaId = JSON_HTTP_SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); +// MetadataSchemaRecord record = new MetadataSchemaRecord(); +// record.setSchemaId(JSON_HTTP_SCHEMA_ID); +// record.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); +// Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("test", PERMISSION.READ)); +// aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); +// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", JSON_HTTP_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + private void ingestHttpJsonSchemaRecordWithHash() throws Exception { + String schemaId = JSON_HTTP_SCHEMA_ID_WITH_HASH; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", JSON_HTTP_SCHEMA_WITH_HASH.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + private String getSchemaUrl(String schemaId) throws Exception { + String schemaUrl = null; + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); + for (RelatedIdentifier item: result.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + schemaUrl = item.getValue(); + } + } + if (schemaUrl != null) + schemaUrl = schemaUrl.replaceFirst("8080", "41421"); + return schemaUrl; + } + + public static synchronized boolean isInitialized() { + boolean returnValue = alreadyInitialized; + alreadyInitialized = Boolean.TRUE; + + return returnValue; + } +} From c4b6587bf141a10fcd82ff18b3fc42ca132a1272 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 8 Mar 2024 15:27:14 +0100 Subject: [PATCH 014/181] First compilable version of MetadataControllerV2. --- .../messaging/MetadataResourceMessage.java | 70 ++- .../metastore2/dao/ISchemaRecordDao.java | 2 + .../metastore2/domain/SchemaRecord.java | 7 +- .../util/DataResourceRecordUtil.java | 155 +++-- .../util/MetadataSchemaRecordUtil.java | 6 +- .../web/impl/MetadataControllerImplV2.java | 541 ++++++++++++++++++ .../web/impl/MetadataControllerImpl_v2.java | 506 ---------------- .../impl/SchemaRegistryControllerImplV2.java | 2 +- .../test/MetadataControllerTestV2.java | 2 +- 9 files changed, 730 insertions(+), 561 deletions(-) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java delete mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java diff --git a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java index 6de50b4f..aded6563 100644 --- a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java +++ b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java @@ -16,6 +16,8 @@ package edu.kit.datamanager.entities.messaging; import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.repo.domain.DataResource; import java.util.HashMap; import java.util.Map; import lombok.Data; @@ -69,7 +71,18 @@ public static MetadataResourceMessage factoryUpdateMetadataMessage(MetadataRecor public static MetadataResourceMessage factoryDeleteMetadataMessage(MetadataRecord metadataRecord, String caller, String sender) { return createMessage(metadataRecord, ACTION.DELETE, SUB_CATEGORY.DATA, caller, sender); } - + + /** + * Create Message for create event. + * + * @param metadataRecord record holding all properties of document + * @param caller caller of the event + * @param sender sender of the event. + * @return Message for create event. + */ + public static MetadataResourceMessage factoryCreateMetadataMessage(DataResource metadataRecord, String caller, String sender) { + return createMessage(metadataRecord, ACTION.CREATE, SUB_CATEGORY.DATA, caller, sender); + } /** * Create Message for create event. * @@ -101,6 +114,61 @@ public static MetadataResourceMessage createMessage(MetadataRecord metadataRecor return msg; } + /** + * Create Message for update event. + * + * @param metadataRecord record holding all properties of document + * @param caller caller of the event + * @param sender sender of the event. + * @return Message for update event. + */ + public static MetadataResourceMessage factoryUpdateMetadataMessage(DataResource metadataRecord, String caller, String sender) { + return createMessage(metadataRecord, ACTION.UPDATE, SUB_CATEGORY.DATA, caller, sender); + } + + /** + * Create Message for delete event. + * + * @param metadataRecord record holding all properties of document + * @param caller caller of the event + * @param sender sender of the event. + * @return Message for delete event. + */ + public static MetadataResourceMessage factoryDeleteMetadataMessage(DataResource metadataRecord, String caller, String sender) { + return createMessage(metadataRecord, ACTION.DELETE, SUB_CATEGORY.DATA, caller, sender); + } + + /** + * Create Message for create event. + * + * @param metadataRecord record holding all properties of document + * @param action message was triggered by this action + * @param subCategory the sub category of the message + * @param principal who triggered this message + * @param sender sender of the event. + * @return Message for create event. + */ + public static MetadataResourceMessage createMessage(DataResource metadataRecord, ACTION action, SUB_CATEGORY subCategory, String principal, String sender) { + MetadataResourceMessage msg = new MetadataResourceMessage(); + Map<String, String> properties = new HashMap<>(); + if (metadataRecord != null) { + properties.put(RESOLVING_URL_PROPERTY, "To do"); + properties.put(DOCUMENT_TYPE_PROPERTY, "To do"); + msg.setEntityId(metadataRecord.getId()); + } + if (action != null) { + msg.setAction(action.getValue()); + } + if (subCategory != null) { + msg.setSubCategory(subCategory.getValue()); + } + msg.setPrincipal(principal); + msg.setSender(sender); + msg.setMetadata(properties); + msg.setCurrentTimestamp(); + return msg; + } + @Override public String getEntityName() { return "metadata"; diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java index c847cd74..ee07a2d6 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java @@ -31,6 +31,8 @@ public interface ISchemaRecordDao extends JpaRepository<SchemaRecord, String>, J SchemaRecord findBySchemaIdAndVersion(String schemaId, Long version); + SchemaRecord findByAlternateId(String alternateId); + List<SchemaRecord> findBySchemaIdOrderByVersionDesc(String schemaId); SchemaRecord findFirstBySchemaIdOrderByVersionDesc(String schemaId); diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index bc04b610..732d19e7 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -15,6 +15,7 @@ */ package edu.kit.datamanager.metastore2.domain; +import edu.kit.datamanager.entities.Identifier; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -42,7 +43,7 @@ public class SchemaRecord implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; - @NotBlank(message = "The unqiue identifier of the schema used in the metadata repository for identifying the schema.") + @NotBlank(message = "The unqiue (internal) identifier of the schema used in the metadata repository for identifying the schema.") private String schemaId; @NotNull(message = "The schema version. The version is set by the schema registry and cannot be provided manually. Typically, a new schema version is only for metadata changes via PUT. In a few cases, \"\n" + " + \"e.g. schema synchronization, a new version can be also created by overwriting an existing schema received from a remote, authoritative source.") @@ -50,10 +51,10 @@ public class SchemaRecord implements Serializable { @Enumerated(EnumType.STRING) @NotNull(message = "The schema type used for quick decision making, e.g. to select a proper validator.") private MetadataSchemaRecord.SCHEMA_TYPE type; - @NotBlank(message = "The schema document uri, e.g. pointing to a local file.") private String schemaDocumentUri; @NotBlank(message = "The SHA-1 hash of the associated metadata file. The hash is used for comparison while updating.") private String documentHash; - + @NotBlank(message = "Alternate id of schema document.") + private String alternateId; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 7b72d901..7412e241 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -42,9 +42,9 @@ import edu.kit.datamanager.metastore2.domain.SchemaRecord; import edu.kit.datamanager.metastore2.domain.Url2Path; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; -import static edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil.fixRelativeURI; import edu.kit.datamanager.metastore2.validation.IValidator; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; +import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; import edu.kit.datamanager.repo.dao.IDataResourceDao; @@ -81,7 +81,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.BiFunction; +import java.util.StringTokenizer; import java.util.function.UnaryOperator; import java.util.logging.Level; import java.util.regex.Matcher; @@ -95,9 +95,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.web.client.HttpClientErrorException; @@ -161,8 +158,7 @@ public class DataResourceRecordUtil { */ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguration applicationProperties, MultipartFile recordDocument, - MultipartFile document, - BiFunction<String, Long, String> getSchemaDocumentById) { + MultipartFile document) { DataResource metadataRecord; // Do some checks first. @@ -209,10 +205,12 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); // store document ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); - + // Get URL for schema + String schemaUrl = getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion()); schemaRecord.setVersion(applicationProperties.getAuditService().getCurrentVersion(dataResource.getId())); schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); schemaRecord.setDocumentHash(contentInformation.getHash()); + schemaRecord.setAlternateId(schemaUrl); MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); // Settings for OAI PMH @@ -220,7 +218,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio try { MetadataFormat metadataFormat = new MetadataFormat(); metadataFormat.setMetadataPrefix(schemaRecord.getSchemaId()); - metadataFormat.setSchema(getSchemaDocumentById.apply(schemaRecord.getSchemaId(), schemaRecord.getVersion())); + metadataFormat.setSchema(schemaUrl); String metadataNamespace = SchemaUtils.getTargetNamespaceFromSchema(document.getBytes()); metadataFormat.setMetadataNamespace(metadataNamespace); metadataFormatDao.save(metadataFormat); @@ -329,14 +327,14 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio * @param supplier Function for updating record. * @return Enriched metadata record. */ - public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applicationProperties, + public static DataResource updateDataResource4MetadataDocument(MetastoreConfiguration applicationProperties, String resourceId, String eTag, MultipartFile recordDocument, MultipartFile document, UnaryOperator<String> supplier) { - MetadataRecord metadataRecord = null; - MetadataRecord existingRecord; + DataResource metadataRecord = null; + DataResource existingRecord; // Do some checks first. if ((recordDocument == null || recordDocument.isEmpty()) && (document == null || document.isEmpty())) { @@ -346,7 +344,7 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica } if (!(recordDocument == null || recordDocument.isEmpty())) { try { - metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), MetadataRecord.class); + metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); } catch (IOException ex) { String message = "Can't map record document to MetadataRecord"; if (ex instanceof JsonParseException) { @@ -362,9 +360,8 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, dataResource); if (metadataRecord != null) { - existingRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - existingRecord = mergeRecords(existingRecord, metadataRecord); - dataResource = migrateToDataResource(applicationProperties, existingRecord); + existingRecord = metadataRecord; + // existingRecord = mergeDataResourceRecords(existingRecord, metadataRecord); } else { dataResource = DataResourceUtils.copyDataResource(dataResource); } @@ -411,8 +408,8 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica } else { // validate if document is still valid due to changed record settings. - metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); + // metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); + URI metadataDocumentUri = getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()); Path metadataDocumentPath = Paths.get(metadataDocumentUri); if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { @@ -422,7 +419,8 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica try { InputStream inputStream = Files.newInputStream(metadataDocumentPath); - SchemaRecord schemaRecord = MetadataSchemaRecordUtil.getSchemaRecord(metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); + ResourceIdentifier schema = ResourceIdentifier.factoryInternalResourceIdentifier(DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue()); + SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecord(schema, Long.parseLong(metadataRecord.getVersion())); MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); } catch (IOException ex) { LOG.error("Error validating file!", ex); @@ -437,7 +435,7 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica } dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); - return migrateToMetadataRecord(applicationProperties, dataResource, true); + return dataResource; } /** @@ -494,7 +492,7 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application * @param eTag ETag of the old digital object. * @param supplier Function for updating record. */ - public static void deleteMetadataRecord(MetastoreConfiguration applicationProperties, + public static void deleteDataResourceRecord(MetastoreConfiguration applicationProperties, String id, String eTag, UnaryOperator<String> supplier) { @@ -978,26 +976,25 @@ public static ContentInformation getContentInformationByIdAndVersion(MetastoreCo * @param provided New metadata record. * @return Merged record */ - public static MetadataRecord mergeRecords(MetadataRecord managed, MetadataRecord provided) { - if (provided != null && managed != null) { - //update pid - managed.setPid(mergeEntry("Update record->pid", managed.getPid(), provided.getPid())); - - //update acl - managed.setAcl(mergeAcl(managed.getAcl(), provided.getAcl())); - //update getRelatedResource - managed.setRelatedResource(mergeEntry("Updating record->relatedResource", managed.getRelatedResource(), provided.getRelatedResource())); - //update schemaId - managed.setSchema(mergeEntry("Updating record->schema", managed.getSchema(), provided.getSchema())); - //update schemaVersion - managed.setSchemaVersion(mergeEntry("Updating record->schemaVersion", managed.getSchemaVersion(), provided.getSchemaVersion())); - // update licenseUri - managed.setLicenseUri(mergeEntry("Updating record->licenseUri", managed.getLicenseUri(), provided.getLicenseUri(), true)); - } else { - managed = (managed != null) ? managed : provided; - } - return managed; - } +// public static MetadataRecord mergeDataResources(DataResource managed, DataResource provided) { +// if (provided != null && managed != null) { +// //update pid +// managed.setAlternateIdentifiers(mergeEntry("Update record->pid", managed.getAlternateIdentifiers(), provided.getAlternateIdentifiers())); +// +// //update acl +// managed.setAcls(mergeAcl(managed.getAcls(), provided.getAcls())); +// //update getRelatedResource +// managed.setRelatedIdentifiers(mergeEntry("Updating record->relatedResource", managed.getRelatedIdentifiers(), provided.getRelatedIdentifiers())); +// //update schemaVersion +// managed.setVersion(mergeEntry("Updating record->schemaVersion", managed.getVersion(), provided.getVersion())); +// managed.setRights(mergeEntry("halo", managed.getRights(), provided.getRights(), true)); +// // update licenseUri +// managed.setRights(mergeEntry("Updating record->licenseUri", managed.getRights(), provided.getRights(), true)); +//1 } else { +// managed = (managed != null) ? managed : provided; +// } +// return managed; +// } /** * Check validity of acl list and then merge new acl list in the existing one. @@ -1215,6 +1212,34 @@ public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { LOG.trace("Fix metadata document Uri '{}' -> '{}'", metadataDocumentUri, metadataRecord.getMetadataDocumentUri()); } + public static final void fixSchemaUrl(DataResource dataresource) { + RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataresource); + if ((schemaIdentifier != null) && (schemaIdentifier.getIdentifierType().equals(Identifier.IDENTIFIER_TYPE.INTERNAL))) { + String value = schemaIdentifier.getValue(); + StringTokenizer tokenizer = new StringTokenizer(schemaIdentifier.getValue()); + Long version = null; + String schemaId = null; + SchemaRecord schemaRecord = null; + switch (tokenizer.countTokens()) { + case 2: + schemaId = tokenizer.nextToken(); + version = Long.parseLong(tokenizer.nextToken()); + schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + break; + case 1: + schemaId = tokenizer.nextToken(); + schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(schemaId); + break; + default: + throw new CustomInternalServerError("Invalid schemaId!"); + } + + schemaIdentifier.setValue(schemaRecord.getAlternateId()); + schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + LOG.trace("Fix scheme Url '{}' -> '{}'", value, schemaIdentifier.getValue()); + } + } + public static void checkLicense(DataResource dataResource, String licenseUri) { if (licenseUri != null) { Set<Scheme> rights = dataResource.getRights(); @@ -1293,24 +1318,36 @@ public static void validateRelatedResources4MetadataDocuments(DataResource dataR } /** - * Transform schema identifier to global available identifier (if neccessary). + * Get schema identifier of data resource. * * @param dataResourceRecord Metadata record hold schema identifier. - * @return ResourceIdentifier with a global accessible identifier. + * @return RelatedIdentifier with a global accessible identifier. */ public static RelatedIdentifier getSchemaIdentifier(DataResource dataResourceRecord) { LOG.trace("Get schema identifier for '{}'.", dataResourceRecord.getId()); - RelatedIdentifier schemaIdentifier = null; + RelatedIdentifier relatedIdentifier = getRelatedIdentifier(dataResourceRecord, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + return relatedIdentifier; + } + + /** + * Transform schema identifier to global available identifier (if neccessary). + * + * @param dataResourceRecord Metadata record hold schema identifier. + * @return ResourceIdentifier with a global accessible identifier. + */ + public static RelatedIdentifier getRelatedIdentifier(DataResource dataResourceRecord, RelatedIdentifier.RELATION_TYPES relationType) { + LOG.trace("Get related identifier for '{}' of type '{}'.", dataResourceRecord.getId(), relationType); + RelatedIdentifier relatedIdentifier = null; Set<RelatedIdentifier> relatedResources = dataResourceRecord.getRelatedIdentifiers(); // Check if related resource already exists (only one related resource of type isMetadataFor allowed) for (RelatedIdentifier item : relatedResources) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { - schemaIdentifier = item; + if (item.getRelationType().equals(relationType)) { + relatedIdentifier = item; } } - return schemaIdentifier; + return relatedIdentifier; } /** @@ -1517,7 +1554,7 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); ContentInformation contentInfo = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaRecord.getId(), Long.valueOf(schemaRecord.getVersion())); validateMetadataDocument(metastoreProperties, document, contentInfo); - + cleanUp(contentInfo); } // @@ -1806,4 +1843,26 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app return dataResource; } + + /** + * Get String (URL) for accessing schema document via schemaId and version. + * + * @param schemaId schemaId. + * @param version version. + * @return String for accessing schema document. + */ + public static final String getSchemaDocumentUri(String schemaId, Long version) { + return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(schemaId, version, null, null)).toUri().toString(); + } + + /** + * Get String (URL) for accessing metadata document via id and version. + * + * @param id id. + * @param version version. + * @return URI for accessing schema document. + */ + public static final URI getMetadataDocumentUri(String id, String version) { + return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, Long.parseLong(version), null, null)).toUri(); + } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 347d0abb..0ab22f69 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -45,7 +45,6 @@ import edu.kit.datamanager.repo.domain.Date; import edu.kit.datamanager.repo.domain.Description; import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Scheme; import edu.kit.datamanager.repo.domain.Title; import edu.kit.datamanager.repo.service.IContentInformationService; import edu.kit.datamanager.repo.util.ContentDataUtils; @@ -70,6 +69,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.StringTokenizer; import java.util.function.BiFunction; import java.util.function.UnaryOperator; import java.util.stream.Stream; @@ -1127,6 +1127,10 @@ private static SchemaRecord transformToSchemaRecord(MetadataSchemaRecord result) public static void saveNewSchemaRecord(SchemaRecord schemaRecord) { if (schemaRecordDao != null) { try { + schemaRecord.setAlternateId(DataResourceRecordUtil.getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion())); + if (new StringTokenizer(schemaRecord.getSchemaId()).countTokens() < 2) { + schemaRecord.setSchemaId(schemaRecord.getSchemaId() + " " + schemaRecord.getVersion()); + } schemaRecordDao.save(schemaRecord); } catch (Exception npe) { LOG.error("Can't save schema record: " + schemaRecord, npe); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java new file mode 100644 index 00000000..f4470c94 --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -0,0 +1,541 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web.impl; + +import com.fasterxml.jackson.core.JsonParseException; +import static edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE.INTERNAL; +import static edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE.URL; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; +import edu.kit.datamanager.exceptions.AccessForbiddenException; +import edu.kit.datamanager.exceptions.BadArgumentException; +import edu.kit.datamanager.exceptions.ResourceNotFoundException; +import edu.kit.datamanager.exceptions.UnprocessableEntityException; +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.domain.AclRecord; +import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.metastore2.util.ActuatorUtil; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; +import edu.kit.datamanager.metastore2.web.IMetadataControllerV2; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.service.IMessagingService; +import edu.kit.datamanager.service.impl.LogfileMessagingService; +import edu.kit.datamanager.util.AuthenticationHelper; +import edu.kit.datamanager.util.ControllerUtils; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.UnaryOperator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.FileSystemResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Controller for metadata documents. + */ +@Controller +@RequestMapping(value = "/api/v2/metadata") +@Tag(name = "Metadata Repository") +@Schema(description = "Metadata Resource Management") +public class MetadataControllerImplV2 implements IMetadataControllerV2 { + + public static final String POST_FILTER = "post_filter"; + /** + * Placeholder string for id of resource. (landingpage) + */ + public static final String PLACEHOLDER_ID = "$(id)"; + /** + * Placeholder string for version of resource. (landingpage) + */ + public static final String PLACEHOLDER_VERSION = "$(version)"; + + private static final Logger LOG = LoggerFactory.getLogger(MetadataControllerImplV2.class); + + private final ApplicationProperties applicationProperties; + + private final ILinkedMetadataRecordDao metadataRecordDao; + + private final MetastoreConfiguration metadataConfig; + + private final IDataResourceDao dataResourceDao; + + private final ISchemaRecordDao schemaRecordDao; + + /** + * Optional messagingService bean may or may not be available, depending on a + * service's configuration. If messaging capabilities are disabled, this bean + * should be not available. In that case, messages are only logged. + */ + @Autowired + private Optional<IMessagingService> messagingService; + + private final String guestToken; + + /** + * Constructor for metadata documents controller. + * + * @param applicationProperties Configuration for controller. + * @param metadataConfig Configuration for metadata documents repository. + * @param metadataRecordDao DAO for metadata records. + * @param dataResourceDao DAO for data resources. + */ + public MetadataControllerImplV2(ApplicationProperties applicationProperties, + MetastoreConfiguration metadataConfig, + ILinkedMetadataRecordDao metadataRecordDao, + IDataResourceDao dataResourceDao, + ISchemaRecordDao schemaRecordDao) { + this.applicationProperties = applicationProperties; + this.metadataConfig = metadataConfig; + this.metadataRecordDao = metadataRecordDao; + this.dataResourceDao = dataResourceDao; + this.schemaRecordDao = schemaRecordDao; + LOG.info("------------------------------------------------------"); + LOG.info("------{}", this.metadataConfig); + LOG.info("------------------------------------------------------"); + LOG.trace("Create guest token"); + guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken("guest", RepoUserRole.GUEST). + addSimpleClaim("email", "metastore@localhost"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + MetadataRecordUtil.setToken(guestToken); + } + + @Override + public ResponseEntity createRecord( + @RequestPart(name = "record") final MultipartFile recordDocument, + @RequestPart(name = "document") final MultipartFile document, + HttpServletRequest request, + HttpServletResponse response, + UriComponentsBuilder uriBuilder) throws URISyntaxException { + +// long nano1 = System.nanoTime() / 1000000; + LOG.trace("Performing createRecord({},...).", recordDocument); + DataResource metadataRecord; + if (recordDocument == null || recordDocument.isEmpty()) { + String message = "No data resource record provided. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + try { + metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); + } catch (IOException ex) { + String message = "No valid data resource record provided. Returning HTTP BAD_REQUEST."; + if (ex instanceof JsonParseException) { + message = message + " Reason: " + ex.getMessage(); + } + LOG.error("Error parsing json: ", ex); + throw new BadArgumentException(message); + } +// long nano2 = System.nanoTime() / 1000000; + + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(metadataRecord); + + LOG.debug("Test for existing metadata record for given schema and resource"); + RelatedIdentifier schemaIdentifier; + try { + schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); + switch (schemaIdentifier.getIdentifierType()) { + case INTERNAL: + // nothing to do + break; + case URL: + SchemaRecord schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); + schemaIdentifier.setValue(schemaRecord.getSchemaId()); + schemaIdentifier.setIdentifierType(INTERNAL); + break; + default: + throw new UnprocessableEntityException("Schema referenced by '" + schemaIdentifier.getIdentifierType().toString() + "' is not supported yet!"); + } + } catch (ResourceNotFoundException rnfe) { + LOG.debug("Error checking for existing relations.", rnfe); + throw new UnprocessableEntityException("Schema ID seems to be invalid"); + } + //Can't test this +// boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId( +// getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue(), +// getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM).getValue()); +// long nano3 = System.nanoTime() / 1000000; +// +// if (recordAlreadyExists) { +// String message = String.format("Conflict! There is already a metadata document with " +// + "the same schema ('%s') and the same related resource ('%s')", +// metadataRecord.getSchemaId(), +// metadataRecord.getRelatedResource().getIdentifier()); +// LOG.error(message); +// return ResponseEntity.status(HttpStatus.CONFLICT).body(message); +// } + DataResource result = DataResourceRecordUtil.createDataResourceRecord4Schema(metadataConfig, recordDocument, document); + // Successfully created metadata record. +// long nano4 = System.nanoTime() / 1000000; + LOG.trace("Metadata record successfully persisted. Returning result."); + DataResourceRecordUtil.fixSchemaUrl(result); +// long nano5 = System.nanoTime() / 1000000; +// metadataRecordDao.save(new LinkedMetadataRecord(result)); +// long nano6 = System.nanoTime() / 1000000; + + URI locationUri; + locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), Long.parseLong(result.getVersion()), null, null)).toUri(); + long nano7 = System.nanoTime() / 1000000; +// LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); + + LOG.trace("Sending CREATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryCreateMetadataMessage(result, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); + + return ResponseEntity.created(locationUri).eTag("\"" + result.getEtag() + "\"").body(result); + } + + @Override + public ResponseEntity<DataResource> getRecordById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing getRecordById({}, {}).", id, version); + + LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); + DataResource metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + LOG.trace("Metadata record found. Prepare response."); + //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN + LOG.trace("Get ETag of DataResource."); + String etag = metadataRecord.getEtag(); + DataResourceRecordUtil.fixSchemaUrl(metadataRecord); + + return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); + } + + @Override + public ResponseEntity<AclRecord> getAclById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing getAclById({}, {}).", id, version); + if (!AuthenticationHelper.isAuthenticatedAsService()) { + throw new AccessForbiddenException("Only for services!"); + } + + DataResource metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + DataResourceRecordUtil.fixSchemaUrl(metadataRecord); + AclRecord aclRecord = new AclRecord(); + aclRecord.setAcl(metadataRecord.getAcls()); +// aclRecord.setDataResource(metadataRecord); + + return ResponseEntity.ok().body(aclRecord); + } + + @Override + public ResponseEntity getMetadataDocumentById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); + + Path metadataDocumentPath = MetadataRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); + + return ResponseEntity. + ok(). + header(HttpHeaders.CONTENT_LENGTH, String.valueOf(metadataDocumentPath.toFile().length())). + body(new FileSystemResource(metadataDocumentPath.toFile())); + } + + @Override + public ModelAndView getLandingpageById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr) { + LOG.trace("Performing Landing page for metadata document with ({}, {}).", id, version); + String redirectUrl = applicationProperties.getMetadataLandingPage(); + redirectUrl = redirectUrl.replace(PLACEHOLDER_ID, id); + String versionString = ""; + if (version != null) { + versionString = version.toString(); + } + redirectUrl = "redirect:" + redirectUrl.replace(PLACEHOLDER_VERSION, versionString); + + LOG.trace("Redirect to '{}'", redirectUrl); + + return new ModelAndView(redirectUrl); + } + + public ResponseEntity<List<DataResource>> getAllVersions( + @PathVariable(value = "id") String id, + Pageable pgbl + ) { + LOG.trace("Performing getAllVersions({}).", id); + // Search for resource type of MetadataSchemaRecord + + //if security is enabled, include principal in query + LOG.debug("Performing query for records."); + DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordById(metadataConfig, id); + List<DataResource> recordList = new ArrayList<>(); + long totalNoOfElements = Long.parseLong(recordByIdAndVersion.getVersion()); + for (long version = totalNoOfElements - pgbl.getOffset(), size = 0; version > 0 && size < pgbl.getPageSize(); version--, size++) { + recordList.add(DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version)); + } + + LOG.trace("Transforming Dataresource to DataResource"); + List<DataResource> metadataList = new ArrayList<>(); + + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); + + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); + } + + @Override + public ResponseEntity<List<DataResource>> getRecords( + @RequestParam(value = "id", required = false) String id, + @RequestParam(value = "resourceId", required = false) List<String> relatedIds, + @RequestParam(value = "schemaId", required = false) List<String> schemaIds, + @RequestParam(name = "from", required = false) Instant updateFrom, + @RequestParam(name = "until", required = false) Instant updateUntil, + Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb + ) { + LOG.trace("Performing getRecords({}, {}, {}, {}).", relatedIds, schemaIds, updateFrom, updateUntil); + if (id != null) { + return getAllVersions(id, pgbl); + } + // Search for resource type of MetadataSchemaRecord + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResourceRecordUtil.METADATA_SUFFIX)); + // Add authentication if enabled + if (metadataConfig.isAuthEnabled()) { + boolean isAdmin; + isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); + // Add authorization for non administrators + if (!isAdmin) { + List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); + if (authorizationIdentities != null) { + LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); + Specification<DataResource> permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); + spec = spec.and(permissionSpec); + } else { + LOG.trace("No permission information provided. Skip creating permission specification."); + } + } + } + List<String> allRelatedIdentifiersSchema = new ArrayList<>(); + List<String> allRelatedIdentifiersResource = new ArrayList<>(); + +// File file = new File(new URIoa) + if (schemaIds != null) { + for (String schemaId : schemaIds) { + MetadataSchemaRecord currentSchemaRecord; + try { + currentSchemaRecord = MetadataRecordUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); + // Test for internal URI -> Transform to global URI. + if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { + ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); + currentSchemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); + } + allRelatedIdentifiersSchema.add(currentSchemaRecord.getSchemaDocumentUri()); + } catch (Exception rnfe) { + // schemaID not found set version to 1 + currentSchemaRecord = new MetadataSchemaRecord(); + currentSchemaRecord.setSchemaVersion(1l); + allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); + } + for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { + MetadataSchemaRecord schemaRecord = MetadataRecordUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); + // Test for internal URI -> Transform to global URI. + if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { + ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); + schemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); + } + allRelatedIdentifiersSchema.add(schemaRecord.getSchemaDocumentUri()); + } + } + Specification<DataResource> schemaSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersSchema.toArray(new String[allRelatedIdentifiersSchema.size()])); + spec = spec.and(schemaSpecification); + } + if (relatedIds != null) { + allRelatedIdentifiersResource.addAll(relatedIds); + Specification<DataResource> relResourceSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersResource.toArray(new String[allRelatedIdentifiersResource.size()])); + spec = spec.and(relResourceSpecification); + } + if ((updateFrom != null) || (updateUntil != null)) { + spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); + } + + // Hide revoked and gone data resources. + DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; + List<DataResource.State> stateList = Arrays.asList(states); + spec = spec.and(StateSpecification.toSpecification(stateList)); + + if (LOG.isTraceEnabled()) { + Page<DataResource> records = dataResourceDao.findAll(pgbl); + LOG.trace("List all data resources..."); + LOG.trace("-----------------------------------------------"); + for (DataResource item : records.getContent()) { + LOG.trace("- '{}'", item); + } + LOG.trace("-----------------------------------------------"); + LOG.trace("Specification: '{}'", spec); + } + LOG.debug("Performing query for records."); + Page<DataResource> records = dataResourceDao.findAll(spec, pgbl); + + LOG.trace("Transforming Dataresource to DataResource"); + List<DataResource> recordList = records.getContent(); +// List<DataResource> metadataList = new ArrayList<>(); +// recordList.forEach(metadataRecord -> { +//// DataResource item = MetadataRecordUtil.migrateToDataResource(metadataConfig, metadataRecord, false); +//// MetadataRecordUtil.fixMetadataDocumentUri(item); +// metadataList.add(metadataRecord); +// }); + + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); + + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); + } + + @Override + public ResponseEntity updateRecord( + @PathVariable("id") String id, + @RequestPart(name = "record", required = false) MultipartFile metadataRecord, + @RequestPart(name = "document", required = false) final MultipartFile document, + WebRequest request, + HttpServletResponse response, + UriComponentsBuilder uriBuilder + ) { + LOG.trace("Performing updateRecord({}, {}, {}).", id, metadataRecord, "#document"); + UnaryOperator<String> getById; + getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); + String eTag = ControllerUtils.getEtagFromHeader(request); + DataResource updateDataResource = DataResourceRecordUtil.updateDataResource4MetadataDocument(metadataConfig, id, eTag, metadataRecord, document, getById); + + LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); + String etag = updateDataResource.getEtag(); + + URI locationUri; + locationUri = DataResourceRecordUtil.getMetadataDocumentUri(updateDataResource.getId(), updateDataResource.getVersion()); + + LOG.trace("Sending UPDATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryUpdateMetadataMessage(updateDataResource, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); + + return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updateDataResource); + } + + @Override + public ResponseEntity deleteRecord( + @PathVariable(value = "id") String id, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing deleteRecord({}).", id); + UnaryOperator<String> getById; + getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, wr, hsr)).toString(); + + String eTag = ControllerUtils.getEtagFromHeader(wr); + DataResourceRecordUtil.deleteDataResourceRecord(metadataConfig, id, eTag, getById); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public void contribute(Info.Builder builder) { + LOG.trace("Check for MetadataRepo actuator information..."); + + URL basePath = metadataConfig.getBasepath(); + Map<String, String> details = ActuatorUtil.testDirectory(basePath); + + if (!details.isEmpty()) { + details.put("No of metadata documents", Long.toString(MetadataRecordUtil.getNoOfDocuments())); + builder.withDetail("metadataRepo", details); + } + } + /** + * Get value of relatedIdentitier with given relation type. + * + * @param result data resource. + * @param relationType type of related identifier. + * @return related identifier. + */ + private RelatedIdentifier getRelatedIdentifier(DataResource result, RelatedIdentifier.RELATION_TYPES relationType) { + RelatedIdentifier relatedIdentifier = null; + for (RelatedIdentifier item: result.getRelatedIdentifiers()) { + if (item.getRelationType().equals(relationType)) { + relatedIdentifier = item; + break; + } + } + return relatedIdentifier; + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java deleted file mode 100644 index b3093a1b..00000000 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl_v2.java +++ /dev/null @@ -1,506 +0,0 @@ -///* -// * Copyright 2019 Karlsruhe Institute of Technology. -// * -// * 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 -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * 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 edu.kit.datamanager.metastore2.web.impl; -// -//import com.fasterxml.jackson.core.JsonParseException; -//import edu.kit.datamanager.entities.PERMISSION; -//import edu.kit.datamanager.entities.RepoUserRole; -//import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; -//import edu.kit.datamanager.exceptions.AccessForbiddenException; -//import edu.kit.datamanager.exceptions.BadArgumentException; -//import edu.kit.datamanager.exceptions.ResourceNotFoundException; -//import edu.kit.datamanager.exceptions.UnprocessableEntityException; -//import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; -//import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; -//import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; -//import edu.kit.datamanager.metastore2.domain.AclRecord; -//import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; -//import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -//import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; -//import edu.kit.datamanager.metastore2.util.ActuatorUtil; -//import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; -//import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; -//import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; -//import edu.kit.datamanager.metastore2.web.IMetadataController_v2; -//import edu.kit.datamanager.repo.dao.IDataResourceDao; -//import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; -//import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; -//import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; -//import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; -//import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; -//import edu.kit.datamanager.repo.domain.DataResource; -//import edu.kit.datamanager.repo.domain.RelatedIdentifier; -//import edu.kit.datamanager.repo.domain.ResourceType; -//import edu.kit.datamanager.service.IMessagingService; -//import edu.kit.datamanager.service.impl.LogfileMessagingService; -//import edu.kit.datamanager.util.AuthenticationHelper; -//import edu.kit.datamanager.util.ControllerUtils; -//import io.swagger.v3.core.util.Json; -//import io.swagger.v3.oas.annotations.media.Schema; -//import io.swagger.v3.oas.annotations.tags.Tag; -//import jakarta.servlet.http.HttpServletRequest; -//import jakarta.servlet.http.HttpServletResponse; -//import java.io.IOException; -//import java.net.URI; -//import java.net.URISyntaxException; -//import java.net.URL; -//import java.nio.file.Path; -//import java.time.Instant; -//import java.util.ArrayList; -//import java.util.Arrays; -//import java.util.List; -//import java.util.Map; -//import java.util.Optional; -//import java.util.function.UnaryOperator; -//import org.slf4j.Logger; -//import org.slf4j.LoggerFactory; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.boot.actuate.info.Info; -//import org.springframework.context.annotation.Bean; -//import org.springframework.core.io.FileSystemResource; -//import org.springframework.data.domain.Page; -//import org.springframework.data.domain.Pageable; -//import org.springframework.data.jpa.domain.Specification; -//import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; -//import org.springframework.http.HttpHeaders; -//import org.springframework.http.HttpStatus; -//import org.springframework.http.ResponseEntity; -//import org.springframework.stereotype.Controller; -//import org.springframework.web.bind.annotation.PathVariable; -//import org.springframework.web.bind.annotation.RequestMapping; -//import org.springframework.web.bind.annotation.RequestParam; -//import org.springframework.web.bind.annotation.RequestPart; -//import org.springframework.web.client.RestTemplate; -//import org.springframework.web.context.request.WebRequest; -//import org.springframework.web.multipart.MultipartFile; -//import org.springframework.web.servlet.ModelAndView; -//import org.springframework.web.util.UriComponentsBuilder; -// -///** -// * Controller for metadata documents. -// */ -//@Controller -//@RequestMapping(value = "/api/v2/metadata") -//@Tag(name = "Metadata Repository") -//@Schema(description = "Metadata Resource Management") -//public class MetadataControllerImpl_v2 implements IMetadataController_v2 { -// -// public static final String POST_FILTER = "post_filter"; -// /** -// * Placeholder string for id of resource. (landingpage) -// */ -// public static final String PLACEHOLDER_ID = "$(id)"; -// /** -// * Placeholder string for version of resource. (landingpage) -// */ -// public static final String PLACEHOLDER_VERSION = "$(version)"; -// -// private static final Logger LOG = LoggerFactory.getLogger(MetadataControllerImpl_v2.class); -// -// private final ApplicationProperties applicationProperties; -// -// private final ILinkedMetadataRecordDao metadataRecordDao; -// -// private final MetastoreConfiguration metadataConfig; -// -// private final IDataResourceDao dataResourceDao; -// -// /** -// * Optional messagingService bean may or may not be available, depending on a -// * service's configuration. If messaging capabilities are disabled, this bean -// * should be not available. In that case, messages are only logged. -// */ -// @Autowired -// private Optional<IMessagingService> messagingService; -// -// private final String guestToken; -// -// /** -// * Constructor for metadata documents controller. -// * -// * @param applicationProperties Configuration for controller. -// * @param metadataConfig Configuration for metadata documents repository. -// * @param metadataRecordDao DAO for metadata records. -// * @param dataResourceDao DAO for data resources. -// */ -// public MetadataControllerImpl_v2(ApplicationProperties applicationProperties, -// MetastoreConfiguration metadataConfig, -// ILinkedMetadataRecordDao metadataRecordDao, -// IDataResourceDao dataResourceDao) { -// this.applicationProperties = applicationProperties; -// this.metadataConfig = metadataConfig; -// this.metadataRecordDao = metadataRecordDao; -// this.dataResourceDao = dataResourceDao; -// LOG.info("------------------------------------------------------"); -// LOG.info("------{}", this.metadataConfig); -// LOG.info("------------------------------------------------------"); -// LOG.trace("Create guest token"); -// guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken("guest", RepoUserRole.GUEST). -// addSimpleClaim("email", "metastore@localhost"). -// addSimpleClaim("loginFailures", 0). -// addSimpleClaim("active", true). -// addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); -// MetadataRecordUtil.setToken(guestToken); -// } -// -// @Override -// public ResponseEntity createRecord( -// @RequestPart(name = "record") final MultipartFile recordDocument, -// @RequestPart(name = "document") final MultipartFile document, -// HttpServletRequest request, -// HttpServletResponse response, -// UriComponentsBuilder uriBuilder) throws URISyntaxException { -// -// long nano1 = System.nanoTime() / 1000000; -// LOG.trace("Performing createRecord({},...).", recordDocument); -// DataResource metadataRecord; -// if (recordDocument == null || recordDocument.isEmpty()) { -// String message = "No data resource record provided. Returning HTTP BAD_REQUEST."; -// LOG.error(message); -// throw new BadArgumentException(message); -// } -// try { -// metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); -// } catch (IOException ex) { -// String message = "No valid data resource record provided. Returning HTTP BAD_REQUEST."; -// if (ex instanceof JsonParseException) { -// message = message + " Reason: " + ex.getMessage(); -// } -// LOG.error("Error parsing json: ", ex); -// throw new BadArgumentException(message); -// } -// long nano2 = System.nanoTime() / 1000000; -// -// DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(metadataRecord); -// -// LOG.debug("Test for existing metadata record for given schema and resource"); -// RelatedIdentifier schemaIdentifier; -// try { -// schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); -// } catch (ResourceNotFoundException rnfe) { -// LOG.debug("Error checking for existing relations.", rnfe); -// throw new UnprocessableEntityException("Schema ID seems to be invalid"); -// } -// boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId(metadataRecord.getRelatedResource().getIdentifier(), schemaIdentifier.getIdentifier()); -// long nano3 = System.nanoTime() / 1000000; -// -// if (recordAlreadyExists) { -// String message = String.format("Conflict! There is already a metadata document with " -// + "the same schema ('%s') and the same related resource ('%s')", -// metadataRecord.getSchemaId(), -// metadataRecord.getRelatedResource().getIdentifier()); -// LOG.error(message); -// return ResponseEntity.status(HttpStatus.CONFLICT).body(message); -// } -// DataResource result = MetadataRecordUtil.createDataResource(metadataConfig, recordDocument, document); -// // Successfully created metadata record. -// long nano4 = System.nanoTime() / 1000000; -// LOG.trace("Metadata record successfully persisted. Returning result."); -// MetadataRecordUtil.fixMetadataDocumentUri(result); -// long nano5 = System.nanoTime() / 1000000; -// metadataRecordDao.save(new LinkedMetadataRecord(result)); -// long nano6 = System.nanoTime() / 1000000; -// -// URI locationUri; -// locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), result.getRecordVersion(), null, null)).toUri(); -// long nano7 = System.nanoTime() / 1000000; -// LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); -// -// LOG.trace("Sending CREATE event."); -// messagingService.orElse(new LogfileMessagingService()). -// send(MetadataResourceMessage.factoryCreateMetadataMessage(result, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); -// -// return ResponseEntity.created(locationUri).eTag("\"" + result.getEtag() + "\"").body(result); -// } -// -// @Override -// public ResponseEntity<DataResource> getRecordById( -// @PathVariable(value = "id") String id, -// @RequestParam(value = "version", required = false) Long version, -// WebRequest wr, -// HttpServletResponse hsr -// ) { -// LOG.trace("Performing getRecordById({}, {}).", id, version); -// -// LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); -// DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); -// LOG.trace("Metadata record found. Prepare response."); -// //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN -// LOG.trace("Get ETag of DataResource."); -// String etag = metadataRecord.getEtag(); -// MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); -// -// return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); -// } -// -// @Override -// public ResponseEntity<AclRecord> getAclById( -// @PathVariable(value = "id") String id, -// @RequestParam(value = "version", required = false) Long version, -// WebRequest wr, -// HttpServletResponse hsr -// ) { -// LOG.trace("Performing getAclById({}, {}).", id, version); -// if (!AuthenticationHelper.isAuthenticatedAsService()) { -// throw new AccessForbiddenException("Only for services!"); -// } -// -// DataResource metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); -// MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); -// AclRecord aclRecord = new AclRecord(); -// aclRecord.setAcl(metadataRecord.getAcl()); -// aclRecord.setDataResource(metadataRecord); -// -// return ResponseEntity.ok().body(aclRecord); -// } -// -// @Override -// public ResponseEntity getMetadataDocumentById( -// @PathVariable(value = "id") String id, -// @RequestParam(value = "version", required = false) Long version, -// WebRequest wr, -// HttpServletResponse hsr -// ) { -// LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); -// -// Path metadataDocumentPath = MetadataRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); -// -// return ResponseEntity. -// ok(). -// header(HttpHeaders.CONTENT_LENGTH, String.valueOf(metadataDocumentPath.toFile().length())). -// body(new FileSystemResource(metadataDocumentPath.toFile())); -// } -// -// @Override -// public ModelAndView getLandingpageById( -// @PathVariable(value = "id") String id, -// @RequestParam(value = "version", required = false) Long version, -// WebRequest wr, -// HttpServletResponse hsr) { -// LOG.trace("Performing Landing page for metadata document with ({}, {}).", id, version); -// String redirectUrl = applicationProperties.getMetadataLandingPage(); -// redirectUrl = redirectUrl.replace(PLACEHOLDER_ID, id); -// String versionString = ""; -// if (version != null) { -// versionString = version.toString(); -// } -// redirectUrl = "redirect:" + redirectUrl.replace(PLACEHOLDER_VERSION, versionString); -// -// LOG.trace("Redirect to '{}'", redirectUrl); -// -// return new ModelAndView(redirectUrl); -// } -// -// public ResponseEntity<List<DataResource>> getAllVersions( -// @PathVariable(value = "id") String id, -// Pageable pgbl -// ) { -// LOG.trace("Performing getAllVersions({}).", id); -// // Search for resource type of MetadataSchemaRecord -// -// //if security is enabled, include principal in query -// LOG.debug("Performing query for records."); -// DataResource recordByIdAndVersion = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id); -// List<DataResource> recordList = new ArrayList<>(); -// long totalNoOfElements = recordByIdAndVersion.getRecordVersion(); -// for (long version = totalNoOfElements - pgbl.getOffset(), size = 0; version > 0 && size < pgbl.getPageSize(); version--, size++) { -// recordList.add(MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version)); -// } -// -// LOG.trace("Transforming Dataresource to DataResource"); -// List<DataResource> metadataList = new ArrayList<>(); -// recordList.forEach(metadataRecord -> { -// MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); -// metadataList.add(metadataRecord); -// }); -// -// String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); -// -// return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); -// } -// -// @Override -// public ResponseEntity<List<DataResource>> getRecords( -// @RequestParam(value = "id", required = false) String id, -// @RequestParam(value = "resourceId", required = false) List<String> relatedIds, -// @RequestParam(value = "schemaId", required = false) List<String> schemaIds, -// @RequestParam(name = "from", required = false) Instant updateFrom, -// @RequestParam(name = "until", required = false) Instant updateUntil, -// Pageable pgbl, -// WebRequest wr, -// HttpServletResponse hsr, -// UriComponentsBuilder ucb -// ) { -// LOG.trace("Performing getRecords({}, {}, {}, {}).", relatedIds, schemaIds, updateFrom, updateUntil); -// if (id != null) { -// return getAllVersions(id, pgbl); -// } -// // Search for resource type of MetadataSchemaRecord -// Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResource.RESOURCE_TYPE)); -// // Add authentication if enabled -// if (metadataConfig.isAuthEnabled()) { -// boolean isAdmin; -// isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); -// // Add authorization for non administrators -// if (!isAdmin) { -// List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); -// if (authorizationIdentities != null) { -// LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); -// Specification<DataResource> permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); -// spec = spec.and(permissionSpec); -// } else { -// LOG.trace("No permission information provided. Skip creating permission specification."); -// } -// } -// } -// List<String> allRelatedIdentifiersSchema = new ArrayList<>(); -// List<String> allRelatedIdentifiersResource = new ArrayList<>(); -// -//// File file = new File(new URIoa) -// if (schemaIds != null) { -// for (String schemaId : schemaIds) { -// MetadataSchemaRecord currentSchemaRecord; -// try { -// currentSchemaRecord = MetadataRecordUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); -// // Test for internal URI -> Transform to global URI. -// if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { -// ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); -// currentSchemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); -// } -// allRelatedIdentifiersSchema.add(currentSchemaRecord.getSchemaDocumentUri()); -// } catch (Exception rnfe) { -// // schemaID not found set version to 1 -// currentSchemaRecord = new MetadataSchemaRecord(); -// currentSchemaRecord.setSchemaVersion(1l); -// allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); -// } -// for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { -// MetadataSchemaRecord schemaRecord = MetadataRecordUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); -// // Test for internal URI -> Transform to global URI. -// if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { -// ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); -// schemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); -// } -// allRelatedIdentifiersSchema.add(schemaRecord.getSchemaDocumentUri()); -// } -// } -// Specification<DataResource> schemaSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersSchema.toArray(new String[allRelatedIdentifiersSchema.size()])); -// spec = spec.and(schemaSpecification); -// } -// if (relatedIds != null) { -// allRelatedIdentifiersResource.addAll(relatedIds); -// Specification<DataResource> relResourceSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersResource.toArray(new String[allRelatedIdentifiersResource.size()])); -// spec = spec.and(relResourceSpecification); -// } -// if ((updateFrom != null) || (updateUntil != null)) { -// spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); -// } -// -// // Hide revoked and gone data resources. -// DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; -// List<DataResource.State> stateList = Arrays.asList(states); -// spec = spec.and(StateSpecification.toSpecification(stateList)); -// -// if (LOG.isTraceEnabled()) { -// Page<DataResource> records = dataResourceDao.findAll(pgbl); -// LOG.trace("List all data resources..."); -// LOG.trace("-----------------------------------------------"); -// for (DataResource item : records.getContent()) { -// LOG.trace("- '{}'", item); -// } -// LOG.trace("-----------------------------------------------"); -// LOG.trace("Specification: '{}'", spec); -// } -// LOG.debug("Performing query for records."); -// Page<DataResource> records = dataResourceDao.findAll(spec, pgbl); -// -// LOG.trace("Transforming Dataresource to DataResource"); -// List<DataResource> recordList = records.getContent(); -// List<DataResource> metadataList = new ArrayList<>(); -// recordList.forEach(metadataRecord -> { -// DataResource item = MetadataRecordUtil.migrateToDataResource(metadataConfig, metadataRecord, false); -// MetadataRecordUtil.fixMetadataDocumentUri(item); -// metadataList.add(item); -// }); -// -// String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); -// -// return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); -// } -// -// @Override -// public ResponseEntity updateRecord( -// @PathVariable("id") String id, -// @RequestPart(name = "record", required = false) MultipartFile metadataRecord, -// @RequestPart(name = "document", required = false) final MultipartFile document, -// WebRequest request, -// HttpServletResponse response, -// UriComponentsBuilder uriBuilder -// ) { -// LOG.trace("Performing updateRecord({}, {}, {}).", id, metadataRecord, "#document"); -// UnaryOperator<String> getById; -// getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); -// String eTag = ControllerUtils.getEtagFromHeader(request); -// DataResource updateDataResource = MetadataRecordUtil.updateDataResource(metadataConfig, id, eTag, metadataRecord, document, getById); -// -// LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); -// String etag = updateDataResource.getEtag(); -// MetadataRecordUtil.fixMetadataDocumentUri(updateDataResource); -// -// URI locationUri; -// locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(updateDataResource.getId(), updateDataResource.getRecordVersion(), null, null)).toUri(); -// -// LOG.trace("Sending UPDATE event."); -// messagingService.orElse(new LogfileMessagingService()). -// send(MetadataResourceMessage.factoryUpdateMetadataMessage(updateDataResource, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); -// -// return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updateDataResource); -// } -// -// @Override -// public ResponseEntity deleteRecord( -// @PathVariable(value = "id") String id, -// WebRequest wr, -// HttpServletResponse hsr -// ) { -// LOG.trace("Performing deleteRecord({}).", id); -// UnaryOperator<String> getById; -// getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, wr, hsr)).toString(); -// -// String eTag = ControllerUtils.getEtagFromHeader(wr); -// MetadataRecordUtil.deleteDataResource(metadataConfig, id, eTag, getById); -// -// return new ResponseEntity<>(HttpStatus.NO_CONTENT); -// } -// -// @Override -// public void contribute(Info.Builder builder) { -// LOG.trace("Check for MetadataRepo actuator information..."); -// -// URL basePath = metadataConfig.getBasepath(); -// Map<String, String> details = ActuatorUtil.testDirectory(basePath); -// -// if (!details.isEmpty()) { -// details.put("No of metadata documents", Long.toString(MetadataRecordUtil.getNoOfDocuments())); -// builder.withDetail("metadataRepo", details); -// } -// } -// -// @Bean -// public RestTemplate restTemplate() { -// return new RestTemplate(); -// } -//} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index f9408524..17b41ab9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -121,7 +121,7 @@ public ResponseEntity<DataResource> createRecord( BiFunction<String, Long, String> getSchemaDocumentById; getSchemaDocumentById = (schema, version) -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getSchemaDocumentById(schema, version, null, null)).toString(); - DataResource dataResourceRecord = DataResourceRecordUtil.createDataResourceRecord4Schema(schemaConfig, recordDocument, document, getSchemaDocumentById); + DataResource dataResourceRecord = DataResourceRecordUtil.createDataResourceRecord4Schema(schemaConfig, recordDocument, document); LOG.trace("Schema record successfully persisted. Returning result."); String etag = dataResourceRecord.getEtag(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 43789252..1e362764 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -243,7 +243,7 @@ public void setUp() throws Exception { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(springSecurity()) .apply(documentationConfiguration(this.restDocumentation).uris() - .withPort(41401)) + .withPort(41421)) .build(); // Create schema only once. try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { From c15702d6813da85201520047974df57dab6b50a5 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 11 Mar 2024 15:43:06 +0100 Subject: [PATCH 015/181] Fix first tests for MetadataController. --- .../metastore2/domain/SchemaRecord.java | 2 +- .../util/DataResourceRecordUtil.java | 171 +++++++++++++++--- .../util/MetadataSchemaRecordUtil.java | 6 +- .../web/impl/MetadataControllerImplV2.java | 2 +- .../test/MetadataControllerTestV2.java | 110 +++++++---- .../test-config/application-test.properties | 4 +- 6 files changed, 222 insertions(+), 73 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index 732d19e7..ffcb9de7 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -55,6 +55,6 @@ public class SchemaRecord implements Serializable { private String schemaDocumentUri; @NotBlank(message = "The SHA-1 hash of the associated metadata file. The hash is used for comparison while updating.") private String documentHash; - @NotBlank(message = "Alternate id of schema document.") +// @NotBlank(message = "Alternate id of schema document.") private String alternateId; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 7412e241..b0fb9575 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -95,6 +95,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.web.client.HttpClientErrorException; @@ -219,6 +221,8 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio MetadataFormat metadataFormat = new MetadataFormat(); metadataFormat.setMetadataPrefix(schemaRecord.getSchemaId()); metadataFormat.setSchema(schemaUrl); + String documentString = new String(document.getBytes()); + LOG.trace(documentString); String metadataNamespace = SchemaUtils.getTargetNamespaceFromSchema(document.getBytes()); metadataFormat.setMetadataNamespace(metadataNamespace); metadataFormatDao.save(metadataFormat); @@ -232,6 +236,70 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio return metadataRecord; } + /** + * Create/Ingest an instance of MetadataRecord. + * + * @param applicationProperties Settings of repository. + * @param recordDocument Record of the metadata. + * @param document Schema document. + * @param getSchemaDocumentById Method for creating access URL. + * @return Record of registered schema document. + */ + public static DataResource createDataResourceRecord4Metadata(MetastoreConfiguration applicationProperties, + MultipartFile recordDocument, + MultipartFile document) { + DataResource metadataRecord; + + // Do some checks first. + metadataRecord = checkParameters(recordDocument, document, true); + Objects.requireNonNull(metadataRecord); + if (metadataRecord.getId() == null) { + String message = "Mandatory attribute 'id' not found in record. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + // End of parameter checks + // validate schema document / determine type if not given + validateMetadataDocument(applicationProperties, metadataRecord, document); + // set internal parameters + if (metadataRecord.getResourceType() == null) { + LOG.trace("No mimetype set! Try to determine..."); + if (document.getContentType() != null) { + LOG.trace("Set mimetype determined from document: '{}'", document.getContentType()); + metadataRecord.getFormats().add(document.getContentType()); + } + } + // Create schema record + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(metadataRecord.getId()); + String type = metadataRecord.getResourceType().getValue(); + if (type.equals(JSON + SCHEMA_SUFFIX) || type.equals(JSON + METADATA_SUFFIX)) { + schemaRecord.setType(JSON); + } else { + if (type.equals(XML + SCHEMA_SUFFIX) || type.equals(XML + METADATA_SUFFIX)) { + schemaRecord.setType(XML); + + } else { + throw new BadArgumentException("Please provide resource type for data resource '" + schemaRecord.getSchemaId() + "'"); + } + } + metadataRecord.setVersion(Long.toString(1)); + // create record. + DataResource dataResource = metadataRecord; + DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); + // store document + ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); + // Get URL for schema + String schemaUrl = getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion()); + schemaRecord.setVersion(applicationProperties.getAuditService().getCurrentVersion(dataResource.getId())); + schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); + schemaRecord.setDocumentHash(contentInformation.getHash()); + schemaRecord.setAlternateId(schemaUrl); + MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); + + return metadataRecord; + } + // /** // * Create a digital object from metadata record and metadata document. // * @@ -361,7 +429,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur ControllerUtils.checkEtag(eTag, dataResource); if (metadataRecord != null) { existingRecord = metadataRecord; - // existingRecord = mergeDataResourceRecords(existingRecord, metadataRecord); + // existingRecord = mergeDataResourceRecords(existingRecord, metadataRecord); } else { dataResource = DataResourceUtils.copyDataResource(dataResource); } @@ -408,7 +476,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur } else { // validate if document is still valid due to changed record settings. - // metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); + // metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); URI metadataDocumentUri = getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()); Path metadataDocumentPath = Paths.get(metadataDocumentUri); @@ -419,7 +487,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur try { InputStream inputStream = Files.newInputStream(metadataDocumentPath); - ResourceIdentifier schema = ResourceIdentifier.factoryInternalResourceIdentifier(DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue()); + ResourceIdentifier schema = ResourceIdentifier.factoryInternalResourceIdentifier(DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue()); SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecord(schema, Long.parseLong(metadataRecord.getVersion())); MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); } catch (IOException ex) { @@ -873,24 +941,24 @@ private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentif * Validate metadata document with given schema. * * @param metastoreProperties Configuration for accessing services - * @param contentInfo metadata of the document. + * @param schemaRecord metadata of the schema document. * @param document document */ private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, MultipartFile document, - ContentInformation contentInfo) { + SchemaRecord schemaRecord) { + LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaRecord, document); + if (document == null || document.isEmpty()) { + String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + URI pathToSchemaFile = URI.create(schemaRecord.getSchemaDocumentUri()); try { - LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, contentInfo, document); - if (document == null || document.isEmpty()) { - String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - URI pathToFile = URI.create(contentInfo.getContentUri()); - switch (pathToFile.getScheme()) { + switch (pathToSchemaFile.getScheme()) { case "file": // check file - Path schemaDocumentPath = Paths.get(pathToFile); + Path schemaDocumentPath = Paths.get(pathToSchemaFile); if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { LOG.trace("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); String errorMessage = "Schema document on server either does not exist or is no file or is not readable."; @@ -898,15 +966,25 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro } byte[] schemaDocument = FileUtils.readFileToByteArray(schemaDocumentPath.toFile()); IValidator applicableValidator; - applicableValidator = getValidatorForRecord(metastoreProperties, contentInfo.getMediaType(), schemaDocument); + String mediaType = null; + switch (schemaRecord.getType()) { + case JSON: + mediaType = MediaType.APPLICATION_JSON_VALUE; + break; + case XML: + mediaType = MediaType.APPLICATION_XML_VALUE; + break; + default: + LOG.error("Unkown schema type: '" + schemaRecord.getType() + "'"); + } + applicableValidator = getValidatorForRecord(metastoreProperties, mediaType, schemaDocument); if (applicableValidator == null) { - String message = "No validator found for schema type " + contentInfo.getMediaType(); + String message = "No validator found for schema type " + mediaType; LOG.error(message); throw new UnprocessableEntityException(message); } else { LOG.trace("Validator found."); - DataResource parent = contentInfo.getParentResource(); - LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", parent.getId(), parent.getVersion(), applicableValidator); + LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); if (!applicableValidator.validateMetadataDocument(schemaDocumentPath.toFile(), document.getInputStream())) { LOG.warn("Metadata document validation failed. -> " + applicableValidator.getErrorMessage()); throw new UnprocessableEntityException(applicableValidator.getErrorMessage()); @@ -917,10 +995,11 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro case "http": case "https": default: - throw new CustomInternalServerError("Protocol of schema ('" + pathToFile.getScheme() + "') is not supported yet!"); + throw new CustomInternalServerError("Protocol of schema ('" + pathToSchemaFile.getScheme() + "') is not supported yet!"); } } catch (IOException ex) { java.util.logging.Logger.getLogger(DataResourceRecordUtil.class.getName()).log(Level.SEVERE, null, ex); + throw new CustomInternalServerError("Schema '" + pathToSchemaFile + "' is not accessible!"); } } @@ -995,7 +1074,6 @@ public static ContentInformation getContentInformationByIdAndVersion(MetastoreCo // } // return managed; // } - /** * Check validity of acl list and then merge new acl list in the existing one. * @@ -1491,7 +1569,7 @@ private static IValidator getValidatorForRecord(MetastoreConfiguration metastore LOG.debug("Automatically detected mimetype of schema: '{}' -> '{}'.", formatDetected, type); } } - String schemaType = schemaRecord.getResourceType().getValue().replace(SCHEMA_SUFFIX, ""); + String schemaType = schemaRecord.getResourceType().getValue().replace(SCHEMA_SUFFIX, "").replace(METADATA_SUFFIX, ""); for (IValidator validator : metastoreProperties.getValidators()) { if (validator.supportsSchemaType(MetadataSchemaRecord.SCHEMA_TYPE.valueOf(schemaType))) { applicableValidator = validator.getInstance(); @@ -1551,11 +1629,8 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp String schemaId, Long version) { LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); - DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); - ContentInformation contentInfo = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaRecord.getId(), Long.valueOf(schemaRecord.getVersion())); - validateMetadataDocument(metastoreProperties, document, contentInfo); - - cleanUp(contentInfo); + SchemaRecord findBySchemaIdAndVersion = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + validateMetadataDocument(metastoreProperties, document, findBySchemaIdAndVersion); } // // /** @@ -1844,6 +1919,50 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app return dataResource; } + /** + * Validate metadata document with given schema. + * + * @param metastoreProperties Configuration for accessing services + * @param metadataRecord metadata of the document. + * @param document document + */ + private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, + DataResource metadataRecord, + MultipartFile document) { + LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, metadataRecord, document); + if (document == null || document.isEmpty()) { + String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + boolean validationSuccess = false; + StringBuilder errorMessage = new StringBuilder(); + RelatedIdentifier schemaIdentifier = getSchemaIdentifier(metadataRecord); + SchemaRecord findByAlternateId; + if (schemaIdentifier != null) { + if (schemaIdentifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { + findByAlternateId = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); + } else { + findByAlternateId = schemaRecordDao.findBySchemaIdOrderByVersionDesc(schemaIdentifier.getValue()).get(0); + } + if (findByAlternateId != null) { + try { + validateMetadataDocument(metastoreProperties, document, findByAlternateId); + validationSuccess = true; + } catch (Exception ex) { + String message = "Error validating document!"; + LOG.error(message, ex); + errorMessage.append(ex.getMessage()).append("\n"); + } + } else { + throw new CustomInternalServerError("No schema registries defined! "); + } + } + if (!validationSuccess) { + throw new UnprocessableEntityException(errorMessage.toString()); + } + } + /** * Get String (URL) for accessing schema document via schemaId and version. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 0ab22f69..32f55948 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -1128,9 +1128,9 @@ public static void saveNewSchemaRecord(SchemaRecord schemaRecord) { if (schemaRecordDao != null) { try { schemaRecord.setAlternateId(DataResourceRecordUtil.getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion())); - if (new StringTokenizer(schemaRecord.getSchemaId()).countTokens() < 2) { - schemaRecord.setSchemaId(schemaRecord.getSchemaId() + " " + schemaRecord.getVersion()); - } +// if (new StringTokenizer(schemaRecord.getSchemaId()).countTokens() < 2) { +// schemaRecord.setSchemaId(schemaRecord.getSchemaId() + " " + schemaRecord.getVersion()); +// } schemaRecordDao.save(schemaRecord); } catch (Exception npe) { LOG.error("Can't save schema record: " + schemaRecord, npe); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index f4470c94..4705cd19 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -228,7 +228,7 @@ public ResponseEntity createRecord( // LOG.error(message); // return ResponseEntity.status(HttpStatus.CONFLICT).body(message); // } - DataResource result = DataResourceRecordUtil.createDataResourceRecord4Schema(metadataConfig, recordDocument, document); + DataResource result = DataResourceRecordUtil.createDataResourceRecord4Metadata(metadataConfig, recordDocument, document); // Successfully created metadata record. // long nano4 = System.nanoTime() / 1000000; LOG.trace("Metadata record successfully persisted. Returning result."); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 1e362764..687139e8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -75,6 +75,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.*; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import java.util.Locale; @@ -788,12 +789,15 @@ public void testCreateRecordWithBadMetadata() throws Exception { @Test public void testCreateRecordWithEmptyAclSid() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry(null, PERMISSION.READ)); - record.setAcl(aclEntries); + String id = "testCreateRecordWithEmptyAclSid"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry(null, PERMISSION.READ)); +// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); @@ -809,9 +813,12 @@ public void testCreateRecordWithEmptyAclSid() throws Exception { @Test public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateRecordWithInvalidMetadataNamespace"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -824,9 +831,12 @@ public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { @Test public void testCreateRecordWithInvalidMetadata() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateRecordWithInvalidMetadata"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -846,9 +856,12 @@ public void testCreateRecordWithoutRecord() throws Exception { @Test public void testCreateRecordWithoutSchema() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateRecordWithoutSchema"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -858,12 +871,15 @@ public void testCreateRecordWithoutSchema() throws Exception { @Test public void testCreateRecordWithBadRecord() throws Exception { + String id = "testCreateRecordWithBadRecord"; + String schemaId = null; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = new MetadataRecord(); - //schemaId is missing - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(null)); - record.setRelatedResource(RELATED_RESOURCE); +// MetadataRecord record = new MetadataRecord(); +// //schemaId is missing +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(null)); +// record.setRelatedResource(RELATED_RESOURCE); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); @@ -877,8 +893,13 @@ public void testCreateRecordWithBadRecord() throws Exception { public void testCreateRecordWithBadRecord2() throws Exception { ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + String id = "testCreateRecordWithLocationUri"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + record.getRelatedIdentifiers().remove(relatedIdentifier); +// MetadataRecord record = new MetadataRecord(); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); //related resource is missing MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -891,14 +912,17 @@ public void testCreateRecordWithBadRecord2() throws Exception { @Test public void testCreateRecordWithoutDocument() throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); -// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -// record.setAcl(aclEntries); + String id = "testCreateRecordWithoutDocument"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); +//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +//// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -910,11 +934,14 @@ public void testCreateRecordWithoutDocument() throws Exception { @Test public void testCreateTwoVersionsOfSameRecord() throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); + String id = "testCreateTwoVersionsOfSameRecord"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -938,11 +965,14 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { @Test public void testCreateTwoVersions() throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); + String id = "testCreateTwoVersions"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); +// MetadataRecord record = new MetadataRecord(); +//// record.setId("my_id"); +// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); +// record.setRelatedResource(RELATED_RESOURCE); +// Set<AclEntry> aclEntries = new HashSet<>(); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -955,7 +985,7 @@ public void testCreateTwoVersions() throws Exception { MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); - record.setRelatedResource(RELATED_RESOURCE_2); + DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setValue(RELATED_RESOURCE_2.toString()); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). diff --git a/src/test/resources/test-config/application-test.properties b/src/test/resources/test-config/application-test.properties index e5b114fc..ecd4d988 100644 --- a/src/test/resources/test-config/application-test.properties +++ b/src/test/resources/test-config/application-test.properties @@ -46,8 +46,8 @@ spring.datasource.password: sa ############################################################################### # Logging settings ############################################################################### -#logging.level.root: OFF -#logging.level.edu.kit.datamanager: TRACE +logging.level.root: OFF +logging.level.edu.kit.datamanager: TRACE #logging.level.org.springframework.security: TRACE #logging.level.org.springframework: TRACE From 880edc25eb0908a96d3effdeec761f894b645735 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 12 Mar 2024 15:17:12 +0100 Subject: [PATCH 016/181] Next step towards tests for metadata documents. Add endpoint for ContentInformation. --- .../metastore2/web/IMetadataControllerV2.java | 14 + .../web/ISchemaRegistryControllerV2.java | 19 +- .../web/impl/MetadataControllerImplV2.java | 18 +- .../impl/SchemaRegistryControllerImplV2.java | 15 + .../metastore2/test/CreateSchemaUtil.java | 170 ++++++ .../test/MetadataControllerTestV2.java | 562 +++++++++--------- .../test/SchemaRegistryControllerTestV2.java | 19 +- 7 files changed, 531 insertions(+), 286 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index d3301038..b1b2439d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -16,6 +16,7 @@ package edu.kit.datamanager.metastore2.web; import edu.kit.datamanager.metastore2.domain.AclRecord; +import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -88,6 +89,19 @@ public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The WebRequest wr, HttpServletResponse hsr); + @Operation(summary = "Get a content information record by id.", description = "Obtain a single record by its resource identifier. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " + + "by providing a version number as request parameter.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = DataResource.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) + + @RequestMapping(value = {"/{id}/data"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.metadata-record+json"}) + public ResponseEntity<ContentInformation> getContentInformationById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); + @Operation(summary = "Get a metadata record by id.", description = "Obtain a single record by its resource identifier. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " + "by providing a version number as request parameter.", diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java index 643a76d8..fffd84f0 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java @@ -16,6 +16,7 @@ package edu.kit.datamanager.metastore2.web; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -84,12 +85,26 @@ public ResponseEntity<DataResource> createRecord( responses = { @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) - @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.schema-record+json"}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, WebRequest wr, HttpServletResponse hsr); + @Operation(operationId = "getContentInformationRecordOfSchema", + summary = "Get content information record by schema id (and version).", + description = "Obtain is single schema record by its schema id. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + + "Furthermore, a specific version of the record can be returned by providing a version number as request parameter. If no version is specified, the most recent version is returned.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.content-information+json"}) + @ResponseBody + public ResponseEntity<ContentInformation> getContentInformationById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get landing page of schema by schema id (and version).", description = "Show landing page by its schema id. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " @@ -125,7 +140,7 @@ public ResponseEntity validate(@Parameter(description = "The record identifier o responses = { @ApiResponse(responseCode = "200", description = "OK and the schema document is returned if the record exists and the user has sufficient permission."), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) - @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/json", "application/xml"}) + @RequestMapping(value = {"/{schemaId}/data"}, method = {RequestMethod.GET}, produces = {"application/json", "application/xml"}) @ResponseBody public ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 4705cd19..684ea52d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -30,7 +30,6 @@ import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.AclRecord; -import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.domain.SchemaRecord; @@ -45,6 +44,7 @@ import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; +import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; @@ -269,6 +269,22 @@ public ResponseEntity<DataResource> getRecordById( return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); } + @Override + public ResponseEntity<ContentInformation> getContentInformationById( + @PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr + ) { + LOG.trace("Performing getRecordById({}, {}).", id, version); + + LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); + ContentInformation contentInformation = DataResourceRecordUtil.getContentInformationByIdAndVersion(metadataConfig, id, version); + LOG.trace("Metadata record found. Prepare response."); + + return ResponseEntity.ok().body(contentInformation); + } + @Override public ResponseEntity<AclRecord> getAclById( @PathVariable(value = "id") String id, diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 17b41ab9..6c500ceb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -148,6 +148,21 @@ public ResponseEntity<DataResource> getRecordById( return ResponseEntity.ok().eTag("\"" + etag + "\"").body(schemaRecord); } + @Override + public ResponseEntity<ContentInformation> getContentInformationById( + @PathVariable(value = "schemaId") String schemaId, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr) { + LOG.trace("Performing getContentInformationById({}, {}).", schemaId, version); + + LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); + ContentInformation contentInformation = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaId, version); + + LOG.trace("Returning result."); + return ResponseEntity.ok().body(contentInformation); + } + @Override public ModelAndView getLandingPageById(@PathVariable(value = "schemaId") String id, @RequestParam(value = "version", required = false) Long version, diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java index f721491d..e099fd4b 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java @@ -6,11 +6,15 @@ package edu.kit.datamanager.metastore2.test; import com.fasterxml.jackson.databind.ObjectMapper; +import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.metastore2.domain.MetadataRecord; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.acl.AclEntry; import edu.kit.datamanager.util.AuthenticationHelper; import java.util.HashSet; @@ -371,6 +375,172 @@ record = mapper.readValue(body, MetadataRecord.class); return result; } + public static String ingestKitSchemaRecordV2(MockMvc mockMvc, String schemaId, String jwtSecret) throws Exception { + return ingestXmlSchemaRecordV2(mockMvc, schemaId, KIT_SCHEMA, jwtSecret); + + } + + /** + * Ingest schema in MetaStore as user 'test_user' If schema already exists + * update schema. + * + * @param mockMvc + * @param schemaId + * @param schemaContent + * @param jwtSecret + * @return + * @throws Exception + */ + public static String ingestXmlSchemaRecordV2(MockMvc mockMvc, String schemaId, String schemaContent, String jwtSecret) throws Exception { + return ingestOrUpdateXmlSchemaRecordV2(mockMvc, schemaId, schemaContent, jwtSecret, false, status().isCreated()); + } + + /** + * Update schema in MetaStore as user 'test_user'. If schema already exists + * and noUpdate is false update schema. + * + * @param mockMvc + * @param schemaId + * @param schemaContent + * @param jwtSecret + * @param noUpdate Only ingest or do update also + * @return + * @throws Exception + */ + public static String ingestOrUpdateXmlSchemaRecordV2(MockMvc mockMvc, String schemaId, String schemaContent, String jwtSecret, boolean update, ResultMatcher expectedStatus) throws Exception { + String locationUri = null; + jwtSecret = (jwtSecret == null) ? "jwtSecret" : jwtSecret; + userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(otherUserPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "any@example.org"). + addSimpleClaim("orcid", "0000-0001-2345-6789"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(jwtSecret); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + record.getAcls().add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile; + MockMultipartFile schemaFile; + recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", schemaContent.getBytes()); + // Test if schema is already registered. + MvcResult result = mockMvc.perform(get("/api/v2/schemas/" + schemaId). + header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + andDo(print()). + andReturn(); + if (result.getResponse().getStatus() != HttpStatus.OK.value()) { + + result = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v2/schemas/"). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()).andExpect(expectedStatus).andReturn(); + if (result.getResponse().getStatus() == HttpStatus.CREATED.value()) { + locationUri = result.getResponse().getHeader("Location"); + } + } else { + if (update) { + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + record = mapper.readValue(body, DataResource.class); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + // Update metadata document + MockHttpServletRequestBuilder header = MockMvcRequestBuilders. + multipart("/api/v2/schemas/" + schemaId). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart()); + mockMvc.perform(header). + andDo(print()). + andExpect(expectedStatus). + andReturn(); + } + + } + return locationUri; + } + + public static MvcResult ingestXmlMetadataDocumentV2(MockMvc mockMvc, String schemaId, Long version, String metadataId, String metadataDocument, String jwtSecret) throws Exception { + return ingestOrUpdateXmlMetadataDocumentV2(mockMvc, schemaId, version, metadataId, metadataDocument, jwtSecret, false, status().isCreated()); + } + + public static MvcResult ingestOrUpdateXmlMetadataDocumentV2(MockMvc mockMvc, String schemaId, Long version, String metadataId, String metadataDocument, String jwtSecret, boolean update, ResultMatcher expectedStatus) throws Exception { + jwtSecret = (jwtSecret == null) ? "jwtSecret" : jwtSecret; + userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(otherUserPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "any@example.org"). + addSimpleClaim("orcid", "0000-0001-2345-6789"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(jwtSecret); + // Test if metadataId is already registered. + + MvcResult result = null; + + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(metadataId, schemaId); + if (version != null) { + record.setVersion(version.toString()); + } + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + relatedIdentifier.setValue("any"); + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + record.getAcls().add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile; + MockMultipartFile metadataFile = null; + recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + if (metadataDocument != null) { + metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", metadataDocument.getBytes()); + } + result = mockMvc.perform(get("/api/v2/metadata/" + metadataId). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andReturn(); + if (result.getResponse().getStatus() != HttpStatus.OK.value()) { + // Create metadata document + MockMultipartHttpServletRequestBuilder file = MockMvcRequestBuilders.multipart("/api/v1/metadata/").file(recordFile); + if (metadataFile != null) { + file = file.file(metadataFile); + } + MockHttpServletRequestBuilder header = file.header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken); + result = mockMvc.perform(header). + andDo(print()). + andExpect(expectedStatus). + andReturn(); + } else { + if (update) { + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + record = mapper.readValue(body, DataResource.class); + relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + relatedIdentifier.setValue(schemaId); + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + if (version != null) { + record.setVersion(version.toString()); + } + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + // Update metadata document + MockMultipartHttpServletRequestBuilder file = MockMvcRequestBuilders.multipart("/api/v2/metadata/" + metadataId).file(recordFile); + if (metadataFile != null) { + file = file.file(metadataFile); + } + MockHttpServletRequestBuilder header = file.header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart()); + result = mockMvc.perform(header). + andDo(print()). + andExpect(expectedStatus). + andReturn(); + } + + } + return result; + } + private static RequestPostProcessor putMultipart() { // it's nice to extract into a helper return (MockHttpServletRequest request) -> { request.setMethod("PUT"); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 687139e8..689cf8e6 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -8,15 +8,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.CollectionType; import edu.kit.datamanager.entities.Identifier; +import edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE; import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; @@ -78,7 +77,9 @@ import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.Scheme; import java.util.Locale; +import java.util.UUID; import org.springframework.http.MediaType; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @@ -254,7 +255,7 @@ public void setUp() throws Exception { } Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); Paths.get(TEMP_DIR_4_SCHEMAS + INVALID_SCHEMA).toFile().createNewFile(); - CreateSchemaUtil.ingestKitSchemaRecord(mockMvc, SCHEMA_ID, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestKitSchemaRecordV2(mockMvc, SCHEMA_ID, schemaConfig.getJwtSecret()); try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { walk.sorted(Comparator.reverseOrder()) .map(Path::toFile) @@ -331,7 +332,7 @@ public void testCreateRecordAlternateEndpoint() throws Exception { String id = "testCreateRecordAlternateEndpoint"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -354,7 +355,7 @@ public void testCreateRecordWithRelatedResourceOfTypeUrl() throws Exception { String id = "testCreateRecordWithRelatedResourceOfTypeUrl"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE_URL); @@ -385,7 +386,7 @@ public void testCreateRecordWithValidUrlSchema() throws Exception { String id = "testCreateRecordWithValidUrlSchema"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // // Get URL of schema // String schemaUrl = getSchemaUrl(SCHEMA_ID); //// record.setId("my_id"); @@ -415,7 +416,7 @@ public void testCreateRecordWithUrlSchemaNull() throws Exception { item.setValue(null); } } -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // // record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(null)); // record.setRelatedResource(RELATED_RESOURCE); @@ -448,7 +449,7 @@ public void testCreateRecordWithInvalidUrl() throws Exception { item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } } -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // // Get URL of schema and remove first character // String invalidSchemaUrl = getSchemaUrl(SCHEMA_ID).substring(1); //// record.setId("my_id"); @@ -483,7 +484,7 @@ public void testCreateRecordWithInvalidUrlSchema() throws Exception { item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } } -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // // Get URL of schema // String urlWithInvalidSchema = getSchemaUrl(SCHEMA_ID).replace(SCHEMA_ID, INVALID_SCHEMA); //// record.setId("my_id"); @@ -518,7 +519,7 @@ public void testCreateRecordWithAnyValidUrl() throws Exception { item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } } -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // // Get URL of schema // String schemaUrl = "http://anyurl.example.org/shouldNotExist"; //// record.setId("my_id"); @@ -546,7 +547,7 @@ public void testCreateRecordWithId() throws Exception { String id = "SomeValidId"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -570,7 +571,7 @@ public void testCreateRecordWithInvalidId() throws Exception { String id = "http://localhost:8080/d1"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -594,7 +595,7 @@ public void testCreateRecordWithIdTwice() throws Exception { String id = "AnyValidId"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -629,7 +630,7 @@ public void testCreateRecordWithLocationUri() throws Exception { String id = "testCreateRecordWithLocationUri"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -649,7 +650,7 @@ public void testCreateRecordWithLocationUri() throws Exception { String content = result.getResponse().getContentAsString(); ObjectMapper map = new ObjectMapper(); - MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String content2 = result2.getResponse().getContentAsString(); Assert.assertEquals(content, content2); @@ -713,7 +714,7 @@ public void testCreateRecordFromExternal() throws Exception { String id = "testCreateRecordFromExternal"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); @@ -736,7 +737,7 @@ public void testCreateRecordUpdateFromExternal() throws Exception { String id = "testCreateRecordUpdateFromExternal"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier("my_dcExt")); // record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); @@ -756,7 +757,7 @@ public void testCreateMetadataUnknownSchemaId() throws Exception { String id = "testCreateMetadataUnknownSchemaId"; String schemaId = "unknown_dc"; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier("unknown_dc")); // record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); @@ -774,7 +775,7 @@ public void testCreateRecordWithBadMetadata() throws Exception { String id = "testCreateRecordWithBadMetadata"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); @@ -792,7 +793,7 @@ public void testCreateRecordWithEmptyAclSid() throws Exception { String id = "testCreateRecordWithEmptyAclSid"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); // Set<AclEntry> aclEntries = new HashSet<>(); @@ -816,7 +817,7 @@ public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { String id = "testCreateRecordWithInvalidMetadataNamespace"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); @@ -834,7 +835,7 @@ public void testCreateRecordWithInvalidMetadata() throws Exception { String id = "testCreateRecordWithInvalidMetadata"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); @@ -859,7 +860,7 @@ public void testCreateRecordWithoutSchema() throws Exception { String id = "testCreateRecordWithoutSchema"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); @@ -876,7 +877,7 @@ public void testCreateRecordWithBadRecord() throws Exception { DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); ObjectMapper mapper = new ObjectMapper(); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // //schemaId is missing // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(null)); // record.setRelatedResource(RELATED_RESOURCE); @@ -898,7 +899,7 @@ public void testCreateRecordWithBadRecord2() throws Exception { DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); record.getRelatedIdentifiers().remove(relatedIdentifier); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); //related resource is missing @@ -915,7 +916,7 @@ public void testCreateRecordWithoutDocument() throws Exception { String id = "testCreateRecordWithoutDocument"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -937,7 +938,7 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { String id = "testCreateTwoVersionsOfSameRecord"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -951,16 +952,16 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); - MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(1l, Long.parseLong(result.getVersion())); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); Assert.assertTrue(res.getResponse().getContentAsString().contains("Conflict")); - Assert.assertTrue(res.getResponse().getContentAsString().contains(SCHEMA_ID)); - Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); + Assert.assertTrue(res.getResponse().getContentAsString().contains(id)); + //Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); } @Test @@ -968,7 +969,7 @@ public void testCreateTwoVersions() throws Exception { String id = "testCreateTwoVersions"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); //// record.setId("my_id"); // record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); // record.setRelatedResource(RELATED_RESOURCE); @@ -982,57 +983,58 @@ public void testCreateTwoVersions() throws Exception { file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); - MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(1l, Long.parseLong(result.getVersion())); DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setValue(RELATED_RESOURCE_2.toString()); + record.getAlternateIdentifiers().clear(); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); - result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(1l, Long.parseLong(result.getVersion())); } @Test public void testGetRecordById() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); - Assert.assertEquals(IdentifierType.URL, result.getSchema().getIdentifierType()); - String schemaUrl = result.getSchema().getIdentifier(); + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(result); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); - //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema - Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); } @Test public void testGetRecordByIdWithVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1").header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1").header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); - Assert.assertEquals(IdentifierType.URL, result.getSchema().getIdentifierType()); - String schemaUrl = result.getSchema().getIdentifier(); + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(result); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); - Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); +// Assert.assertNotEquals("file:///tmp/dc.xml", result.getEtag()); } @Test public void testGetRecordByIdWithInvalidId() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + "cd"). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); @@ -1044,7 +1046,7 @@ public void testGetRecordByIdWithInvalidVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). param("version", "13"). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); @@ -1056,7 +1058,7 @@ public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", SCHEMA_ID)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); } @@ -1066,7 +1068,7 @@ public void testFindRecordsBySchemaId() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", SCHEMA_ID)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); } @@ -1080,7 +1082,7 @@ public void testFindRecordsByInvalidSchemaId() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -1096,18 +1098,18 @@ public void testFindRecordsOfMultipleVersionsBySchemaId() throws Exception { // Ingest 1st version of document. CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 1l, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); // Ingest 2nd version of document CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 2l, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); // Ingest 3rd version of document CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 3l, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(3, result.length); } @@ -1119,12 +1121,12 @@ public void testFindRecordsByResourceId() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("resoureId", RELATED_RESOURCE.getIdentifier())).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", RELATED_RESOURCE.getIdentifier()).param("from", twoHoursBefore.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); map = new ObjectMapper(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); } @@ -1146,7 +1148,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); // Looking for second schema Assert.assertEquals(2, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1154,7 +1156,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); // Looking for first AND second schema res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1163,7 +1165,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(4, result.length); // Looking for first, second AND invalid schema res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1173,7 +1175,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(4, result.length); // Looking for first, second AND invalid schema AND resource1 and resource2 res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1185,7 +1187,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(4, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1195,7 +1197,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1204,7 +1206,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1213,7 +1215,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1223,7 +1225,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1232,7 +1234,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -1241,7 +1243,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); } @@ -1250,7 +1252,7 @@ public void testFindRecordsByInvalidResourceId() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", UNKNOWN_RELATED_RESOURCE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -1263,13 +1265,13 @@ public void testFindRecordsByInvalidUploadDate() throws Exception { MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", RELATED_RESOURCE.getIdentifier()).param("until", oneHourBefore.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH).param("resourceId", RELATED_RESOURCE.getIdentifier()).param("from", twoHoursBefore.toString()).param("until", oneHourBefore.toString())).andDo(print()).andExpect(status().isOk()).andReturn(); map = new ObjectMapper(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -1283,7 +1285,7 @@ public void testFindRecordsByUnknownParameter() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -1309,12 +1311,12 @@ public void testGetMetadataDocumentWithUnknownSchema() throws Exception { @Test public void testUpdateRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); @@ -1322,17 +1324,15 @@ public void testUpdateRecord() throws Exception { file(recordFile). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -1342,19 +1342,19 @@ public void testUpdateRecord() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); } @Test public void testUpdateRecordWithWrongVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - record.setRecordVersion(0l); + DataResource record = mapper.readValue(body, DataResource.class); + record.setVersion("0"); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); @@ -1362,17 +1362,20 @@ public void testUpdateRecordWithWrongVersion() throws Exception { file(recordFile). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals(Long.valueOf(2l), record2.getRecordVersion());// version should be 2 - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); +// Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); +// Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record.getRelatedIdentifiers(), record2.getRelatedIdentifiers()); + Assert.assertEquals(2l, Long.parseLong(record2.getVersion()));// version should be 2 +// if (record.getAcl() != null) { +// Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); +// } + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -1382,7 +1385,7 @@ public void testUpdateRecordWithWrongVersion() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); // Check for old metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); content = result.getResponse().getContentAsString(); @@ -1391,21 +1394,21 @@ public void testUpdateRecordWithWrongVersion() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); } @Test public void testUpdateRecordIgnoreACL() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource oldRecord = mapper.readValue(body, DataResource.class); + DataResource record = mapper.readValue(body, DataResource.class); // Set all ACL to WRITE - for (AclEntry entry : record.getAcl()) { + for (AclEntry entry : record.getAcls()) { entry.setPermission(PERMISSION.WRITE); } MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1414,19 +1417,16 @@ public void testUpdateRecordIgnoreACL() throws Exception { file(recordFile). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(SchemaRegistryControllerTest.isSameSetOfAclEntries(record.getAcl(), record2.getAcl())); - Assert.assertFalse(SchemaRegistryControllerTest.isSameSetOfAclEntries(oldRecord.getAcl(), record2.getAcl())); - } - Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(oldRecord.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(oldRecord), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(oldRecord.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(oldRecord.getAcls(), record2.getAcls()); + Assert.assertTrue(oldRecord.getLastUpdate().isBefore(record2.getLastUpdate())); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); @@ -1435,18 +1435,14 @@ public void testUpdateRecordIgnoreACL() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); } @Test public void testUpdateRecordWithoutExplizitGet() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> acl = new HashSet<>(); - acl.add(new AclEntry("test", PERMISSION.READ)); - acl.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(acl); + String id = "testUpdateRecordWithoutExplizitGet"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1454,31 +1450,36 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). - file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + file(metadataFile)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + DataResource record2 = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); MockMultipartFile metadataFile2 = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(locationUri). file(recordFile2). - file(metadataFile2).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + file(metadataFile2). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals(record2.getDocumentHash(), record3.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); - Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); - Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1l);// version should be 1 higher - if (record2.getAcl() != null) { - Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); - } + DataResource record3 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); } @@ -1488,13 +1489,13 @@ public void testUpdateRecordWithSameDocument() throws Exception { MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); - MetadataRecord record1 = mapper.readValue(body, MetadataRecord.class); + DataResource record1 = mapper.readValue(body, DataResource.class); // Update without any changes. result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertEquals("Version shouldn't change!", record1.getRecordVersion(), record2.getRecordVersion()); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertEquals("Version shouldn't change!", record1.getVersion(), record2.getVersion()); } @Test @@ -1503,25 +1504,25 @@ public void testUpdateRecordWithSmallChangesInDocument() throws Exception { MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); - MetadataRecord record1 = mapper.readValue(body, MetadataRecord.class); + DataResource record1 = mapper.readValue(body, DataResource.class); // Update without any changes. result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals("Version should change!", record1.getRecordVersion(), record2.getRecordVersion()); - Assert.assertEquals("Version should incremented!", (long) record1.getRecordVersion(), (long) (record2.getRecordVersion() - 1l)); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertNotEquals("Version should change!", record1.getVersion(), record2.getVersion()); + Assert.assertEquals("Version should incremented!", Long.parseLong(record1.getVersion()), Long.parseLong(record2.getVersion()) - 1l); } @Test public void testUpdateRecordWithoutETag() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); @@ -1533,45 +1534,47 @@ public void testUpdateRecordWithoutETag() throws Exception { @Test public void testUpdateRecordWithWrongETag() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag") + "unknown"; String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile). - file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isPreconditionFailed()).andReturn(); + file(metadataFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isPreconditionFailed()). + andReturn(); } @Test public void testUpdateRecordWithoutRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); } @@ -1580,27 +1583,25 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { // Update only Json document ingestJsonSchemaRecord(); String metadataRecordId = createJsonMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", JSON_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -1620,28 +1621,26 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { @Test public void testUpdateRecordWithoutDocument() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getMetadataDocumentUri(), record2.getMetadataDocumentUri()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion());// version should be the same - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); } @@ -1649,59 +1648,61 @@ public void testUpdateRecordWithoutDocument() throws Exception { public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exception { String metadataRecordId = createDCMetadataRecord(); String expectedRelatedResource; - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - expectedRelatedResource = record2.getRelatedResource().getIdentifier() + "_NEW"; - record2.getRelatedResource().setIdentifier(expectedRelatedResource); + DataResource record = mapper.readValue(body, DataResource.class); + DataResource record2 = mapper.readValue(body, DataResource.class); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + expectedRelatedResource = relatedIdentifier.getValue() + "_NEW"; + relatedIdentifier.setValue(expectedRelatedResource); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); body = result.getResponse().getContentAsString(); etag = result.getResponse().getHeader("ETag"); - record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getMetadataDocumentUri(), record2.getMetadataDocumentUri()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion());// version should be the same - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - Assert.assertNotEquals("Related resource should be updated!", record.getRelatedResource().getIdentifier(), record2.getRelatedResource().getIdentifier()); - Assert.assertEquals("Related resource should be updated!", expectedRelatedResource, record2.getRelatedResource().getIdentifier()); + RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertNotEquals("Related resource should be updated!", relatedIdentifier1.getValue(), relatedIdentifier2.getValue()); + Assert.assertEquals("Related resource should be updated!", expectedRelatedResource, relatedIdentifier2.getValue()); // Test also updating related type only... - IdentifierType expectedIdentifierType = IdentifierType.ISBN; - record2.getRelatedResource().setIdentifierType(expectedIdentifierType); + IDENTIFIER_TYPE expectedIdentifierType = IDENTIFIER_TYPE.ISBN; + DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setIdentifierType(expectedIdentifierType); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); - Assert.assertEquals(record.getDocumentHash(), record3.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record3.getCreatedAt()); - Assert.assertEquals(record.getMetadataDocumentUri(), record3.getMetadataDocumentUri()); - Assert.assertEquals(record.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals(record.getRecordVersion(), record3.getRecordVersion());// version should be the same - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record3.getAcl())); - } + DataResource record3 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record3.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record3)); + Assert.assertEquals(record.getEtag(), record3.getEtag()); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record3.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record3.getAcls()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record3.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record3.getLastUpdate())); - Assert.assertEquals("Related resource should be the same!", record2.getRelatedResource().getIdentifier(), record3.getRelatedResource().getIdentifier()); - Assert.assertEquals("Related resource type should be changed!", expectedIdentifierType, record3.getRelatedResource().getIdentifierType()); + RelatedIdentifier relatedIdentifier3 = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier4 = DataResourceRecordUtil.getRelatedIdentifier(record3, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals("Related resource should be the same!", relatedIdentifier3.getValue(), relatedIdentifier4.getValue()); + Assert.assertEquals("Related resource type should be changed!", expectedIdentifierType, relatedIdentifier4.getIdentifierType()); } @@ -1723,7 +1724,7 @@ public void testUpdateRecordWithInvalidSetting4Json() throws Exception { public void testUpdateRecordWithLicense() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1731,8 +1732,12 @@ public void testUpdateRecordWithLicense() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - record.setLicenseUri(APACHE_2_LICENSE); + DataResource record = mapper.readValue(body, DataResource.class); + record.getRights().clear(); + Scheme apache = new Scheme(); + apache.setSchemeId("Apache-2.0"); + apache.setSchemeUri(APACHE_2_LICENSE); + record.getRights().add(apache); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); @@ -1745,20 +1750,22 @@ public void testUpdateRecordWithLicense() throws Exception { andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - Assert.assertNotNull(record2.getLicenseUri()); - Assert.assertTrue(record2.getLicenseUri().equals(APACHE_2_LICENSE)); + + Assert.assertNull("No license available", record.getRights()); + Assert.assertNotNull(record2.getRights()); + Assert.assertTrue(!record2.getRights().isEmpty()); + Assert.assertTrue(!record2.getRights().iterator().next().getSchemeUri().equals(APACHE_2_LICENSE)); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)). andDo(print()). @@ -1770,9 +1777,9 @@ public void testUpdateRecordWithLicense() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1780,8 +1787,8 @@ public void testUpdateRecordWithLicense() throws Exception { body = result.getResponse().getContentAsString(); mapper = new ObjectMapper(); - record = mapper.readValue(body, MetadataRecord.class); - record.setLicenseUri(null); + record = mapper.readValue(body, DataResource.class); + record.setRights(null); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). @@ -1792,19 +1799,17 @@ record = mapper.readValue(body, MetadataRecord.class); andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); - Assert.assertEquals(record2.getDocumentHash(), record3.getDocumentHash()); - Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); - Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), (long)record3.getRecordVersion());// version should be the same - if (record.getAcl() != null) { - Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); - } + DataResource record3 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()));// version should be the same + SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); - Assert.assertNull(record3.getLicenseUri()); + Assert.assertNull("Set of rights should be 'null'", record3.getRights()); } @@ -1812,7 +1817,7 @@ record = mapper.readValue(body, MetadataRecord.class); public void testDeleteRecordWithoutAuthentication() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); @@ -1827,14 +1832,14 @@ public void testDeleteRecord() throws Exception { // Get a list of all records MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isOk()). andReturn(); - int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; // Get Etag - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); // Delete record this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); @@ -1843,7 +1848,7 @@ public void testDeleteRecord() throws Exception { this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isPreconditionRequired()).andReturn(); // Recreation should be no problem. // //try to create after deletion (Should return HTTP GONE) -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchemaId("dc"); // record.setRelatedResource(RELATED_RESOURCE); // ObjectMapper mapper = new ObjectMapper(); @@ -1857,11 +1862,11 @@ public void testDeleteRecord() throws Exception { // List of records should be smaller afterwards result = this.mockMvc.perform(get(API_METADATA_PATH). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isOk()). andReturn(); - int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); } @@ -1874,13 +1879,13 @@ public void testGetAllVersionsOfRecord() throws Exception { // Read all versions this.mockMvc.perform(get(API_METADATA_PATH).param("id", id).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - Assert.assertEquals("Expect current version '" + version + "'", (Long) version, record.getRecordVersion());// version should be 1 higher + DataResource record = mapper.readValue(body, DataResource.class); + Assert.assertEquals("Expect current version '" + version + "'", (Long) version, Long.valueOf(record.getVersion()));// version should be 1 higher // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + id)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); @@ -1891,17 +1896,17 @@ public void testGetAllVersionsOfRecord() throws Exception { Assert.assertTrue(content.startsWith(dcMetadata)); -// Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); +// Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); // Get version of record as array // Read all versions (2 version2 available) result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", id).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); mapper = new ObjectMapper(); CollectionType mapCollectionType = mapper.getTypeFactory() - .constructCollectionType(List.class, MetadataRecord.class); - List<MetadataRecord> resultList = mapper.readValue(result.getResponse().getContentAsString(), mapCollectionType); + .constructCollectionType(List.class, DataResource.class); + List<DataResource> resultList = mapper.readValue(result.getResponse().getContentAsString(), mapCollectionType); HashSet<Long> versions = new HashSet<>(); - for (MetadataRecord item : resultList) { - versions.add(item.getRecordVersion()); + for (DataResource item : resultList) { + versions.add(Long.valueOf(item.getVersion())); } Assert.assertEquals(version, versions.size()); for (long index = 1; index <= version; index++) { @@ -2093,12 +2098,12 @@ public void testLandingPage4Metadata() throws Exception { .andDo(print()) .andExpect(status().isOk()); // Ingest a second version... - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + documentId).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + documentId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). @@ -2151,7 +2156,7 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { andReturn(); // Get Etag result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -2172,7 +2177,7 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { // Delete second time result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -2211,14 +2216,8 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { } private String createJsonMetadataRecord() throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(JSON_SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); - aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + String randomId = UUID.randomUUID().toString(); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(randomId, SCHEMA_ID); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -2227,7 +2226,7 @@ private String createJsonMetadataRecord() throws Exception { MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); - MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + DataResource result = mapper.readValue(andReturn.getResponse().getContentAsString(), DataResource.class); return result.getId(); } @@ -2237,15 +2236,15 @@ private String createDCMetadataRecord() throws Exception { } private String createDCMetadataRecordWithRelatedResource(String myRelatedResource, String schemaId) throws Exception { - ResourceIdentifier relatedResource = ResourceIdentifier.factoryInternalResourceIdentifier(myRelatedResource); - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(schemaId)); - record.setRelatedResource(relatedResource); + String randomId = UUID.randomUUID().toString(); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(randomId, SCHEMA_ID); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + relatedIdentifier.setIdentifierType(IDENTIFIER_TYPE.INTERNAL); + relatedIdentifier.setValue(myRelatedResource); Set<AclEntry> aclEntries = new HashSet<>(); aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + record.setAcls(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -2254,7 +2253,7 @@ private String createDCMetadataRecordWithRelatedResource(String myRelatedResourc MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); - MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + DataResource result = mapper.readValue(andReturn.getResponse().getContentAsString(), DataResource.class); return result.getId(); } @@ -2272,12 +2271,12 @@ private String ingestMetadataRecordWithVersion(String id, long version) throws E id = createDCMetadataRecord(); } else { // add new version - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); String newDocument = DC_DOCUMENT; for (int i = 0; i < version; i++) { newDocument = newDocument.concat(" "); @@ -2304,13 +2303,16 @@ private String ingestNewMetadataRecord(String id, long version) throws Exception id = createDCMetadataRecord(); } else { // add new version - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - record.setRelatedResource(ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING + version)); + DataResource record = mapper.readValue(body, DataResource.class); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + relatedIdentifier.setValue(RELATED_RESOURCE_STRING + version); + relatedIdentifier.setIdentifierType(IDENTIFIER_TYPE.INTERNAL); +// record.setRelatedResource(ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING + version)); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 0b2ba0f7..026f1d56 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1891,12 +1891,22 @@ public static DataResource createDataResource4Schema(String id) { return record; } + public static DataResource createDataResource4JsonDocument(String id, String schemaId) { + return createDataResource4Document(id, schemaId, DataResourceRecordUtil.JSON_METADATA_TYPE); + } + public static DataResource createDataResource4XmlDocument(String id, String schemaId) { + return createDataResource4Document(id, schemaId, DataResourceRecordUtil.XML_METADATA_TYPE); + } public static DataResource createDataResource4Document(String id, String schemaId) { + return createDataResource4Document(id, schemaId, DataResourceRecordUtil.XML_METADATA_TYPE); + } + public static DataResource createDataResource4Document(String id, String schemaId, String metadataType) { DataResource record = new DataResource(); record.setId(id); + record.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(id)); // mandatory element title has to be set setTitle(record, id); - record.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.XML_METADATA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); + record.setResourceType(ResourceType.createResourceType(metadataType, ResourceType.TYPE_GENERAL.MODEL)); RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE_STRING, null, null); relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); @@ -1904,8 +1914,11 @@ public static DataResource createDataResource4Document(String id, String schemaI relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaId, null, null); relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); record.getRelatedIdentifiers().add(relatedResource); - - record.getFormats().add(MediaType.APPLICATION_XML.toString()); + if (metadataType.contains("XML")) { + record.getFormats().add(MediaType.APPLICATION_XML.toString()); + } else { + record.getFormats().add(MediaType.APPLICATION_JSON.toString()); + } Set<AclEntry> aclEntries = new HashSet<>(); aclEntries.add(new AclEntry("test", PERMISSION.READ)); aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); From 1769afa07e04ed1a1f336183ebb3a41149d9169d Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 13 Mar 2024 08:24:31 +0100 Subject: [PATCH 017/181] Fix definition of endpoints for /api/v2. --- .../util/DataResourceRecordUtil.java | 6 ++ .../metastore2/web/IMetadataControllerV2.java | 6 +- .../web/ISchemaRegistryControllerV2.java | 4 +- .../test/SchemaRegistryControllerTestV2.java | 56 +++++++++---------- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index b0fb9575..65e58147 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -109,6 +109,12 @@ */ public class DataResourceRecordUtil { + public static final String RESOURCE_TYPE = "application/vnd.datacite.org+json"; + /** + * Mediatype for fetching a DataResource. + */ + public static final MediaType DATA_RESOURCE_MEDIA_TYPE = MediaType.valueOf(RESOURCE_TYPE); + /** * Separator for separating schemaId and schemaVersion. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index b1b2439d..2f9458fc 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -83,7 +83,7 @@ public ResponseEntity createRecord( @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = DataResource.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) - @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.metadata-record+json"}) + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datacite.org+json"}) public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, WebRequest wr, @@ -96,7 +96,7 @@ public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = DataResource.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) - @RequestMapping(value = {"/{id}/data"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.metadata-record+json"}) + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.content-information+json"}) public ResponseEntity<ContentInformation> getContentInformationById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, WebRequest wr, @@ -134,7 +134,7 @@ public ModelAndView getLandingpageById(@Parameter(description = "The record iden @ApiResponse(responseCode = "200", description = "OK and the metadata document is returned if the record exists and the user has sufficient permission."), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) - @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}) + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/xml", "application/json"}) @ResponseBody public ResponseEntity getMetadataDocumentById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java index fffd84f0..50c5acd8 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java @@ -85,7 +85,7 @@ public ResponseEntity<DataResource> createRecord( responses = { @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) - @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {MediaType.APPLICATION_JSON_VALUE}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datacite.org+json"}) @ResponseBody public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, @@ -140,7 +140,7 @@ public ResponseEntity validate(@Parameter(description = "The record identifier o responses = { @ApiResponse(responseCode = "200", description = "OK and the schema document is returned if the record exists and the user has sufficient permission."), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) - @RequestMapping(value = {"/{schemaId}/data"}, method = {RequestMethod.GET}, produces = {"application/json", "application/xml"}) + @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/json", "application/xml"}) @ResponseBody public ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 026f1d56..9f3009e8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -356,7 +356,7 @@ public void testCreateSchemaRecordWithLocationUri() throws Exception { String locationUri = result.getResponse().getHeader("Location"); String content = result.getResponse().getContentAsString(); - MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String content2 = result2.getResponse().getContentAsString(); validateDataResources(content, content2); @@ -589,7 +589,7 @@ public void testGetSchemaRecordByIdWithoutVersion() throws Exception { String schemaId = "testGetSchemaRecordByIdWithoutVersion".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); @@ -603,7 +603,7 @@ public void testGetSchemaRecordByIdWithVersion() throws Exception { String schemaId = "testGetSchemaRecordByIdWithVersion".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "1").header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "1").header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); @@ -616,7 +616,7 @@ public void testGetSchemaRecordByIdWithInvalidId() throws Exception { String schemaId = "testGetSchemaRecordByIdWithInvalidId".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + "cd"). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); @@ -629,7 +629,7 @@ public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { ingestXmlDataResource(schemaId); MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). param("version", "13"). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); @@ -640,7 +640,7 @@ public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { String schemaId = "testFindRecordsBySchemaIdWithAlternateEndpoint".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); @@ -651,7 +651,7 @@ public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { public void testFindRecordsBySchemaId() throws Exception { String schemaId = "testFindRecordsBySchemaId".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); @@ -839,7 +839,7 @@ public void testUpdateRecord() throws Exception { String newComment = "new comment"; String newLabel = "label changed!"; ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -889,7 +889,7 @@ public void testUpdateRecordRemovingLabel() throws Exception { String newComment = "new comment"; String newLabel = "label changed!"; ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -936,7 +936,7 @@ public void testUpdateRecordRemovingLabel() throws Exception { public void testUpdateRecordIgnoreACL() throws Exception { String schemaId = "updateRecord".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -991,7 +991,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { String schemaId = "updateMimetypeOfRecord".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1013,7 +1013,7 @@ public void testUpdateRecordWithIgnoringInvalidSetting4Xml() throws Exception { public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { String schemaId = "updateTypeOfRecord".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1034,7 +1034,7 @@ public void testUpdateRecordWithInvalidSetting4Xml() throws Exception { public void testUpdateRecordWithoutChanges() throws Exception { String schemaId = "updateRecordWithoutChanges".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1065,7 +1065,7 @@ public void testUpdateRecordWithoutChanges() throws Exception { public void testUpdateRecordAndDocument() throws Exception { String schemaId = "updateRecordAndDocument"; ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1108,7 +1108,7 @@ public void testUpdateRecordAndDocument() throws Exception { public void testUpdateRecordAndDocumentWithLicense() throws Exception { String schemaId = "updateRecordAndDocumentWithLicense".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1192,7 +1192,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { String schemaId = "updateRecordAndDocumentWithWrongVersion".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1231,7 +1231,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { public void testUpdateOnlyDocument() throws Exception { String schemaId = "updateRecordDocumentOnly".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1280,7 +1280,7 @@ public void testUpdateRecordWithSmallChangesInDocument() throws Exception { MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); -// MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); +// MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", CreateSchemaUtil.XML_SCHEMA_V1_TYPO.getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -1337,7 +1337,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { public void testUpdateRecordWithoutETag() throws Exception { String schemaId = "testUpdateRecordWithoutETag".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); @@ -1353,7 +1353,7 @@ public void testUpdateRecordWithoutETag() throws Exception { public void testUpdateRecordWithWrongETag() throws Exception { String schemaId = "testUpdateRecordWithWrongETag".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag") + "unknown"; String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); @@ -1367,10 +1367,10 @@ public void testUpdateRecordWithWrongETag() throws Exception { public void testUpdateRecordWithoutBody() throws Exception { String schemaId = "testUpdateRecordWithoutBody".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); - this.mockMvc.perform(put(API_SCHEMA_PATH + schemaId).header("If-Match", etag).contentType(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).content("{}")).andDo(print()).andExpect(status().isUnsupportedMediaType()).andReturn(); + this.mockMvc.perform(put(API_SCHEMA_PATH + schemaId).header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content("{}")).andDo(print()).andExpect(status().isUnsupportedMediaType()).andReturn(); } @Test @@ -1417,14 +1417,14 @@ public void testDeleteSchemaRecord() throws Exception { // Get a list of all records MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1440,7 +1440,7 @@ public void testDeleteSchemaRecord() throws Exception { file(recordFile). file(schemaFile)).andDo(print()).andExpect(status().isConflict()).andReturn(); //delete second time // should be really deleted -> gone - result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); etag = result.getResponse().getHeader("ETag"); this.mockMvc.perform(delete(API_SCHEMA_PATH + schemaId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); @@ -1450,7 +1450,7 @@ public void testDeleteSchemaRecord() throws Exception { file(schemaFile)).andDo(print()).andExpect(status().isGone()).andReturn(); // List of records should be smaller afterwards - result = this.mockMvc.perform(get(API_SCHEMA_PATH).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); } @@ -1833,7 +1833,7 @@ private void ingestSchemaWithVersion(String schemaId, long version) throws Excep MvcResult result; if (version > 1) { // Read ETag - result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String body = result.getResponse().getContentAsString(); DataResource oldRecord = mapper.readValue(body, DataResource.class); @@ -1857,7 +1857,7 @@ record = mapper.readValue(body, DataResource.class); } private void ingestNewSchemaRecord(String schemaId, long version) throws Exception { - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); From 12d9b6033cf92f75d6542d699c2f38cfe4040462 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 13 Mar 2024 15:16:11 +0100 Subject: [PATCH 018/181] Fix tests for schemas. --- .../util/DataResourceRecordUtil.java | 76 +++++++++++++------ .../test/MetadataControllerTestV2.java | 8 +- .../test/SchemaRegistryControllerTestV2.java | 24 +++++- 3 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 65e58147..80a46da9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -193,32 +193,14 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio metadataRecord.getFormats().add(document.getContentType()); } } - // Create schema record - SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(metadataRecord.getId()); - String type = metadataRecord.getResourceType().getValue(); - if (type.equals(JSON + SCHEMA_SUFFIX)) { - schemaRecord.setType(JSON); - } else { - if (type.equals(XML + SCHEMA_SUFFIX)) { - schemaRecord.setType(XML); - - } else { - throw new BadArgumentException("Please provide resource type for data resource '" + schemaRecord.getSchemaId() + "'"); - } - } metadataRecord.setVersion(Long.toString(1)); // create record. DataResource dataResource = metadataRecord; DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); // store document ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); - // Get URL for schema - String schemaUrl = getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion()); - schemaRecord.setVersion(applicationProperties.getAuditService().getCurrentVersion(dataResource.getId())); - schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); - schemaRecord.setDocumentHash(contentInformation.getHash()); - schemaRecord.setAlternateId(schemaUrl); + // Create schema record + SchemaRecord schemaRecord = createSchemaRecord(dataResource, contentInformation); MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); // Settings for OAI PMH @@ -226,7 +208,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio try { MetadataFormat metadataFormat = new MetadataFormat(); metadataFormat.setMetadataPrefix(schemaRecord.getSchemaId()); - metadataFormat.setSchema(schemaUrl); + metadataFormat.setSchema(schemaRecord.getAlternateId()); String documentString = new String(document.getBytes()); LOG.trace(documentString); String metadataNamespace = SchemaUtils.getTargetNamespaceFromSchema(document.getBytes()); @@ -1635,7 +1617,20 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp String schemaId, Long version) { LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); - SchemaRecord findBySchemaIdAndVersion = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + SchemaRecord findBySchemaIdAndVersion; + if (version == null) { + findBySchemaIdAndVersion = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(schemaId); + } else { + findBySchemaIdAndVersion = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + } + if (findBySchemaIdAndVersion == null) { + String errorMessage = "SchemaId '" + schemaId + "' doesn't exist."; + if (schemaRecordDao.existsSchemaRecordBySchemaIdAndVersion(schemaId, 1l)) { + errorMessage = "Version '" + version + "' of schema ID '" + schemaId + "' doesn't exist."; + } + LOG.error(errorMessage); + throw new ResourceNotFoundException(errorMessage); + } validateMetadataDocument(metastoreProperties, document, findBySchemaIdAndVersion); } // @@ -1897,7 +1892,10 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app if (version != null) { dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); } - ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); + ContentInformation newSchemaFile = ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); + SchemaRecord schemaRecord = createSchemaRecord(dataResource, newSchemaFile); + MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); + } } else { // validate if document is still valid due to changed record settings. @@ -1969,6 +1967,38 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro } } + /** + * Create schema record from DataResource and ContentInformation. + * + * @param dataResource Data resource + * @param contentInformation Content information + * @return schema record + */ + public static final SchemaRecord createSchemaRecord(DataResource dataResource, ContentInformation contentInformation) { + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(dataResource.getId()); + schemaRecord.setVersion(Long.valueOf(dataResource.getVersion())); + String type = dataResource.getResourceType().getValue(); + if (type.equals(JSON + SCHEMA_SUFFIX)) { + schemaRecord.setType(JSON); + } else { + if (type.equals(XML + SCHEMA_SUFFIX)) { + schemaRecord.setType(XML); + + } else { + throw new BadArgumentException("Please provide a valid resource type for data resource '" + schemaRecord.getSchemaId() + "'!\n" + + "One of ['" + JSON + SCHEMA_SUFFIX + "', '" + XML + SCHEMA_SUFFIX + "']"); + } + } + // Get URL for schema + String schemaUrl = getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion()); + schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); + schemaRecord.setDocumentHash(contentInformation.getHash()); + schemaRecord.setAlternateId(schemaUrl); + + return schemaRecord; + } + /** * Get String (URL) for accessing schema document via schemaId and version. * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 689cf8e6..8a719383 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -292,7 +292,7 @@ public void testCreateRecordWithHttpSchema() throws Exception { ingestHttpJsonSchemaRecord(); String id = "testCreateRecordWithHttpSchema"; String schemaId = SCHEMA_ID; - DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(id, schemaId); Set<AclEntry> aclEntries = new HashSet<>(); // aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); // aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); @@ -312,7 +312,7 @@ public void testCreateRecordWithHttpSchemaAndHash() throws Exception { ingestHttpJsonSchemaRecordWithHash(); String id = "testCreateRecordWithHttpSchemaAndHash"; String schemaId = JSON_HTTP_SCHEMA_ID_WITH_HASH; - DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(id, schemaId); Set<AclEntry> aclEntries = new HashSet<>(); // aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); // aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); @@ -2356,7 +2356,7 @@ private void ingestJsonSchemaRecord() throws Exception { private void ingestHttpJsonSchemaRecord() throws Exception { String schemaId = JSON_HTTP_SCHEMA_ID; - DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonSchema(schemaId); // MetadataSchemaRecord record = new MetadataSchemaRecord(); // record.setSchemaId(JSON_HTTP_SCHEMA_ID); // record.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); @@ -2376,7 +2376,7 @@ private void ingestHttpJsonSchemaRecord() throws Exception { private void ingestHttpJsonSchemaRecordWithHash() throws Exception { String schemaId = JSON_HTTP_SCHEMA_ID_WITH_HASH; - DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonSchema(schemaId); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 9f3009e8..9080c229 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -829,7 +829,7 @@ public void testValidateWithMissingSchemaFile() throws Exception { URI uri = new URI(contentUri); Files.delete(Paths.get(uri)); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isInternalServerError()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); } // Update only record @@ -1875,6 +1875,19 @@ private void ingestNewSchemaRecord(String schemaId, long version) throws Excepti andReturn(); } + public static DataResource createDataResource4JsonSchema(String id) { + DataResource record = createDataResource4Schema(id); + record.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.JSON + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + record.getFormats().clear(); + record.getFormats().add(MediaType.APPLICATION_JSON.toString()); + + return record; + } + + public static DataResource createDataResource4XmlSchema(String id) { + return createDataResource4Schema(id); + } + public static DataResource createDataResource4Schema(String id) { DataResource record = new DataResource(); record.setId(id); @@ -1894,12 +1907,15 @@ public static DataResource createDataResource4Schema(String id) { public static DataResource createDataResource4JsonDocument(String id, String schemaId) { return createDataResource4Document(id, schemaId, DataResourceRecordUtil.JSON_METADATA_TYPE); } + public static DataResource createDataResource4XmlDocument(String id, String schemaId) { return createDataResource4Document(id, schemaId, DataResourceRecordUtil.XML_METADATA_TYPE); } + public static DataResource createDataResource4Document(String id, String schemaId) { return createDataResource4Document(id, schemaId, DataResourceRecordUtil.XML_METADATA_TYPE); } + public static DataResource createDataResource4Document(String id, String schemaId, String metadataType) { DataResource record = new DataResource(); record.setId(id); @@ -1907,7 +1923,7 @@ public static DataResource createDataResource4Document(String id, String schemaI // mandatory element title has to be set setTitle(record, id); record.setResourceType(ResourceType.createResourceType(metadataType, ResourceType.TYPE_GENERAL.MODEL)); - + RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE_STRING, null, null); relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); record.getRelatedIdentifiers().add(relatedResource); @@ -2108,8 +2124,8 @@ public static void validateRelatedIdentifierSets(Set<RelatedIdentifier> first, S identical = (item.getIdentifierType() == item2.getIdentifierType()) && ((item.getValue() == item2.getValue()) || item.getValue().equals(item2.getValue())) && (item.getRelationType() == item2.getRelationType()) - && ((item.getScheme() == item2.getScheme()) || (item.getScheme().getSchemeId().equals(item2.getScheme().getSchemeId()) && - item.getScheme().getSchemeUri().equals(item2.getScheme().getSchemeUri()))); + && ((item.getScheme() == item2.getScheme()) || (item.getScheme().getSchemeId().equals(item2.getScheme().getSchemeId()) + && item.getScheme().getSchemeUri().equals(item2.getScheme().getSchemeUri()))); if (identical) { copy.remove(item2); break; From 1108530f335077d9a53a8d78e6f8339f925d578d Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 15 Mar 2024 17:57:47 +0100 Subject: [PATCH 019/181] Fix tests for new API --- .../util/DataResourceRecordUtil.java | 67 +++---- .../web/impl/MetadataControllerImplV2.java | 16 +- .../test/MetadataControllerTestV2.java | 168 ++++++------------ .../test/SchemaRegistryControllerTestV2.java | 6 +- 4 files changed, 97 insertions(+), 160 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 80a46da9..35fa9054 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -95,7 +95,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -257,33 +256,12 @@ public static DataResource createDataResourceRecord4Metadata(MetastoreConfigurat metadataRecord.getFormats().add(document.getContentType()); } } - // Create schema record - SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(metadataRecord.getId()); - String type = metadataRecord.getResourceType().getValue(); - if (type.equals(JSON + SCHEMA_SUFFIX) || type.equals(JSON + METADATA_SUFFIX)) { - schemaRecord.setType(JSON); - } else { - if (type.equals(XML + SCHEMA_SUFFIX) || type.equals(XML + METADATA_SUFFIX)) { - schemaRecord.setType(XML); - - } else { - throw new BadArgumentException("Please provide resource type for data resource '" + schemaRecord.getSchemaId() + "'"); - } - } metadataRecord.setVersion(Long.toString(1)); // create record. DataResource dataResource = metadataRecord; DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); // store document ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); - // Get URL for schema - String schemaUrl = getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion()); - schemaRecord.setVersion(applicationProperties.getAuditService().getCurrentVersion(dataResource.getId())); - schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); - schemaRecord.setDocumentHash(contentInformation.getHash()); - schemaRecord.setAlternateId(schemaUrl); - MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); return metadataRecord; } @@ -1616,22 +1594,26 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp MultipartFile document, String schemaId, Long version) { - LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaId, document); - SchemaRecord findBySchemaIdAndVersion; - if (version == null) { - findBySchemaIdAndVersion = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(schemaId); + LOG.trace("validateMetadataDocument {},SchemaID {}, Version {}, {}", metastoreProperties, schemaId, version, document); + SchemaRecord schemaRecord; + DataResource dataResource = DataResourceRecordUtil.getRecordById(metastoreProperties, schemaId); + if (dataResource == null) { + String message = "Unknown schemaID '" + schemaId + "'!"; + LOG.error(message); + throw new ResourceNotFoundException(message); + } + schemaId = dataResource.getId(); + if (version != null) { + schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); } else { - findBySchemaIdAndVersion = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + schemaRecord = schemaRecordDao.findBySchemaIdOrderByVersionDesc(schemaId).get(0); } - if (findBySchemaIdAndVersion == null) { - String errorMessage = "SchemaId '" + schemaId + "' doesn't exist."; - if (schemaRecordDao.existsSchemaRecordBySchemaIdAndVersion(schemaId, 1l)) { - errorMessage = "Version '" + version + "' of schema ID '" + schemaId + "' doesn't exist."; - } - LOG.error(errorMessage); - throw new ResourceNotFoundException(errorMessage); + if (schemaRecord == null) { + String message = "Unknown version '" + version + "' for schemaID '" + schemaId + "'!"; + LOG.error(message); + throw new ResourceNotFoundException(message); } - validateMetadataDocument(metastoreProperties, document, findBySchemaIdAndVersion); + validateMetadataDocument(metastoreProperties, document, schemaRecord); } // // /** @@ -1892,10 +1874,9 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app if (version != null) { dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); } - ContentInformation newSchemaFile = ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); - SchemaRecord schemaRecord = createSchemaRecord(dataResource, newSchemaFile); - MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); - + ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); + SchemaRecord schemaRecord = createSchemaRecord(dataResource, contentInformation); + MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); } } else { // validate if document is still valid due to changed record settings. @@ -1966,7 +1947,7 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro throw new UnprocessableEntityException(errorMessage.toString()); } } - + /** * Create schema record from DataResource and ContentInformation. * @@ -1977,7 +1958,6 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro public static final SchemaRecord createSchemaRecord(DataResource dataResource, ContentInformation contentInformation) { SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(dataResource.getId()); - schemaRecord.setVersion(Long.valueOf(dataResource.getVersion())); String type = dataResource.getResourceType().getValue(); if (type.equals(JSON + SCHEMA_SUFFIX)) { schemaRecord.setType(JSON); @@ -1990,8 +1970,9 @@ public static final SchemaRecord createSchemaRecord(DataResource dataResource, C + "One of ['" + JSON + SCHEMA_SUFFIX + "', '" + XML + SCHEMA_SUFFIX + "']"); } } - // Get URL for schema - String schemaUrl = getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion()); + Long currentVersion = Long.valueOf(dataResource.getVersion()); + String schemaUrl = getSchemaDocumentUri(dataResource.getId(),currentVersion); + schemaRecord.setVersion(currentVersion); schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); schemaRecord.setDocumentHash(contentInformation.getHash()); schemaRecord.setAlternateId(schemaUrl); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 684ea52d..71608cfe 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -196,7 +196,7 @@ public ResponseEntity createRecord( LOG.debug("Test for existing metadata record for given schema and resource"); RelatedIdentifier schemaIdentifier; - try { +// try { schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); switch (schemaIdentifier.getIdentifierType()) { case INTERNAL: @@ -204,16 +204,22 @@ public ResponseEntity createRecord( break; case URL: SchemaRecord schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); + if (schemaRecord == null) { + String message = "External URLs are not supported yet!\n" + + "But '" + schemaIdentifier.getValue() + "' seems not to be an internal one!"; + LOG.error(message); + throw new ResourceNotFoundException(message); + } schemaIdentifier.setValue(schemaRecord.getSchemaId()); schemaIdentifier.setIdentifierType(INTERNAL); break; default: throw new UnprocessableEntityException("Schema referenced by '" + schemaIdentifier.getIdentifierType().toString() + "' is not supported yet!"); } - } catch (ResourceNotFoundException rnfe) { - LOG.debug("Error checking for existing relations.", rnfe); - throw new UnprocessableEntityException("Schema ID seems to be invalid"); - } +// } catch (ResourceNotFoundException rnfe) { +// LOG.debug("Error checking for existing relations.", rnfe); +// throw new UnprocessableEntityException("Schema ID seems to be invalid"); +// } //Can't test this // boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId( // getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue(), diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 8a719383..e1305d33 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -75,6 +75,7 @@ import org.springframework.web.context.WebApplicationContext; import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.*; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.Scheme; @@ -291,7 +292,7 @@ public void testCreateRecord() throws Exception { public void testCreateRecordWithHttpSchema() throws Exception { ingestHttpJsonSchemaRecord(); String id = "testCreateRecordWithHttpSchema"; - String schemaId = SCHEMA_ID; + String schemaId = JSON_HTTP_SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(id, schemaId); Set<AclEntry> aclEntries = new HashSet<>(); // aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); @@ -332,14 +333,6 @@ public void testCreateRecordAlternateEndpoint() throws Exception { String id = "testCreateRecordAlternateEndpoint"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -354,15 +347,9 @@ public void testCreateRecordAlternateEndpoint() throws Exception { public void testCreateRecordWithRelatedResourceOfTypeUrl() throws Exception { String id = "testCreateRecordWithRelatedResourceOfTypeUrl"; String schemaId = SCHEMA_ID; - DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE_URL); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4XmlDocument(id, schemaId); + RelatedIdentifier relatedResourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + relatedResourceIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -377,8 +364,14 @@ public void testCreateRecordWithRelatedResourceOfTypeUrl() throws Exception { andReturn(); DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record.getRelatedIdentifiers(), result.getRelatedIdentifiers()); -// Assert.assertEquals("Type of related resource should be unchanged!", record.getRelatedIdentifiers().getIdentifierType(), result.getRelatedResource().getIdentifierType()); + RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(result, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals("Value of related resource should be unchanged!", + relatedIdentifier1.getValue(), + relatedIdentifier2.getValue()); + Assert.assertEquals("Type of related resource should be unchanged!", + relatedIdentifier1.getIdentifierType(), + relatedIdentifier2.getIdentifierType()); } @Test @@ -386,16 +379,6 @@ public void testCreateRecordWithValidUrlSchema() throws Exception { String id = "testCreateRecordWithValidUrlSchema"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -// // Get URL of schema -// String schemaUrl = getSchemaUrl(SCHEMA_ID); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(schemaUrl)); -// record.setRelatedResource(RELATED_RESOURCE); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -409,21 +392,14 @@ public void testCreateRecordWithValidUrlSchema() throws Exception { @Test public void testCreateRecordWithUrlSchemaNull() throws Exception { String id = "testCreateRecordWithUrlSchemaNull"; - String schemaId = SCHEMA_ID; + String schemaId = null; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item: record.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { item.setValue(null); + item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } } -// DataResource record = new DataResource(); -// -// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(null)); -// record.setRelatedResource(RELATED_RESOURCE); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -433,7 +409,7 @@ public void testCreateRecordWithUrlSchemaNull() throws Exception { file(recordFile). file(metadataFile)). andDo(print()). - andExpect(status().isBadRequest()). + andExpect(status().isNotFound()). andReturn(); } @@ -449,16 +425,6 @@ public void testCreateRecordWithInvalidUrl() throws Exception { item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } } -// DataResource record = new DataResource(); -// // Get URL of schema and remove first character -// String invalidSchemaUrl = getSchemaUrl(SCHEMA_ID).substring(1); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryUrlResourceIdentifier(invalidSchemaUrl)); -// record.setRelatedResource(RELATED_RESOURCE); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -468,7 +434,7 @@ public void testCreateRecordWithInvalidUrl() throws Exception { file(recordFile). file(metadataFile)). andDo(print()). - andExpect(status().isUnprocessableEntity()). + andExpect(status().isNotFound()). andReturn(); } @@ -503,7 +469,7 @@ public void testCreateRecordWithInvalidUrlSchema() throws Exception { file(recordFile). file(metadataFile)). andDo(print()). - andExpect(status().isUnprocessableEntity()). + andExpect(status().isNotFound()). andReturn(); } @@ -534,12 +500,14 @@ public void testCreateRecordWithAnyValidUrl() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile)). andDo(print()). - andExpect(status().isUnprocessableEntity()). + andExpect(status().isNotFound()). andReturn(); + String response = result.getResponse().getContentAsString(); + Assert.assertTrue(response, response.contains("URL")); } @Test @@ -547,15 +515,6 @@ public void testCreateRecordWithId() throws Exception { String id = "SomeValidId"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); -// record.setId("SomeValidId"); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -571,15 +530,11 @@ public void testCreateRecordWithInvalidId() throws Exception { String id = "http://localhost:8080/d1"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); -// record.setId("http://localhost:8080/d1"); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); + // XXXXX XXX XXXX XXX + // X X X X X X X + // X X X X X X X + // X X X X X X X + // X XXX XXXX XXX ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -595,15 +550,6 @@ public void testCreateRecordWithIdTwice() throws Exception { String id = "AnyValidId"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); -// record.setId("AnyValidId"); -// Set<AclEntry> aclEntries = new HashSet<>(); -//// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -//// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -//// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -630,14 +576,6 @@ public void testCreateRecordWithLocationUri() throws Exception { String id = "testCreateRecordWithLocationUri"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); -// Set<AclEntry> aclEntries = new HashSet<>(); -// aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); -// aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); -// record.setAcl(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -647,15 +585,18 @@ public void testCreateRecordWithLocationUri() throws Exception { file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); String locationUri = result.getResponse().getHeader("Location"); - String content = result.getResponse().getContentAsString(); + DataResource data1 = mapper.readValue(result.getResponse().getContentAsString(), DataResource.class); ObjectMapper map = new ObjectMapper(); - MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); - String content2 = result2.getResponse().getContentAsString(); - - Assert.assertEquals(content, content2); + MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + DataResource data2 = mapper.readValue(result2.getResponse().getContentAsString(), DataResource.class); + SchemaRegistryControllerTestV2.validateDataResources(data1, data2); } - + // XXXXX XXX XXXX XXX + // X X X X X X X + // X X X X X X X + // X X X X X X X + // X XXX XXXX XXX @Test public void testCreateInvalidRecord() throws Exception { String id = "testCreateInvalidRecord"; @@ -961,7 +902,7 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { Assert.assertTrue(res.getResponse().getContentAsString().contains("Conflict")); Assert.assertTrue(res.getResponse().getContentAsString().contains(id)); - //Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); + Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); } @Test @@ -1005,12 +946,15 @@ public void testGetRecordById() throws Exception { ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); + String locationUri = res.getResponse().getHeader("Location"); RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(result); Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); + //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema + Assert.assertNotEquals("file:///tmp/dc.xml", locationUri); } @Test @@ -1021,13 +965,14 @@ public void testGetRecordByIdWithVersion() throws Exception { ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); + String locationUri = res.getResponse().getHeader("Location"); RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(result); Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); -// Assert.assertNotEquals("file:///tmp/dc.xml", result.getEtag()); + Assert.assertNotEquals("file:///tmp/dc.xml", locationUri); } @Test @@ -1326,6 +1271,7 @@ public void testUpdateRecord() throws Exception { // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); + String locationUri = result.getResponse().getHeader("Location"); DataResource record2 = mapper.readValue(body, DataResource.class); // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); @@ -1336,13 +1282,14 @@ public void testUpdateRecord() throws Exception { Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); } @Test @@ -1379,22 +1326,23 @@ public void testUpdateRecordWithWrongVersion() throws Exception { Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri = result.getResponse().getHeader("Location"); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); // Check for old metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); content = result.getResponse().getContentAsString(); dcMetadata = DC_DOCUMENT; Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); } @Test @@ -1416,6 +1364,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). file(recordFile). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri = result.getResponse().getHeader("Location"); // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); @@ -1429,13 +1378,14 @@ public void testUpdateRecordIgnoreACL() throws Exception { Assert.assertTrue(oldRecord.getLastUpdate().isBefore(record2.getLastUpdate())); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); } @Test @@ -1730,6 +1680,7 @@ public void testUpdateRecordWithLicense() throws Exception { andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); + ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); @@ -1752,6 +1703,7 @@ public void testUpdateRecordWithLicense() throws Exception { // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); + String locationUri = result.getResponse().getHeader("Location"); DataResource record2 = mapper.readValue(body, DataResource.class); // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); @@ -1771,13 +1723,14 @@ public void testUpdateRecordWithLicense() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). @@ -2389,14 +2342,11 @@ private void ingestHttpJsonSchemaRecordWithHash() throws Exception { private String getSchemaUrl(String schemaId) throws Exception { String schemaUrl = null; - MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).header("Accept", ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); - DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); - for (RelatedIdentifier item: result.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { - schemaUrl = item.getValue(); - } - } + String content = res.getResponse().getContentAsString(); + ContentInformation result = map.readValue(res.getResponse().getContentAsString(), ContentInformation.class); + schemaUrl = result.getContentUri(); if (schemaUrl != null) schemaUrl = schemaUrl.replaceFirst("8080", "41421"); return schemaUrl; diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 9080c229..15702eb6 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -829,7 +829,7 @@ public void testValidateWithMissingSchemaFile() throws Exception { URI uri = new URI(contentUri); Files.delete(Paths.get(uri)); - this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().isNotFound()).andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId + "/validate").file("document", KIT_DOCUMENT.getBytes())).andDo(print()).andExpect(status().is5xxServerError()).andReturn(); } // Update only record @@ -1880,7 +1880,7 @@ public static DataResource createDataResource4JsonSchema(String id) { record.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.JSON + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); record.getFormats().clear(); record.getFormats().add(MediaType.APPLICATION_JSON.toString()); - + return record; } @@ -1931,7 +1931,7 @@ public static DataResource createDataResource4Document(String id, String schemaI relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); record.getRelatedIdentifiers().add(relatedResource); if (metadataType.contains("XML")) { - record.getFormats().add(MediaType.APPLICATION_XML.toString()); + record.getFormats().add(MediaType.APPLICATION_XML.toString()); } else { record.getFormats().add(MediaType.APPLICATION_JSON.toString()); } From 6546994b19315c41166ad6644d4916015de7cdef Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 20 Mar 2024 16:14:52 +0100 Subject: [PATCH 020/181] One more step towards datacite records. Changed also schemaRecord which causes errors. :-( --- .../metastore2/dao/ISchemaRecordDao.java | 10 +- .../metastore2/domain/SchemaRecord.java | 8 + .../runner/ElasticIndexerRunner.java | 2 +- .../util/DataResourceRecordUtil.java | 163 ++++++-- .../util/MetadataSchemaRecordUtil.java | 18 +- .../web/impl/MetadataControllerImplV2.java | 70 +--- .../metastore2/test/CreateSchemaUtil.java | 7 +- .../test/MetadataControllerTestV2.java | 366 +++++++++++------- .../test/SchemaRegistryControllerTestV2.java | 86 +++- 9 files changed, 486 insertions(+), 244 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java index ee07a2d6..6c1b288c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java @@ -29,11 +29,15 @@ public interface ISchemaRecordDao extends JpaRepository<SchemaRecord, String>, J boolean existsSchemaRecordBySchemaIdAndVersion(String schemaId, Long version); - SchemaRecord findBySchemaIdAndVersion(String schemaId, Long version); + SchemaRecord findBySchemaId(String schemaIdWithVersion); + +// SchemaRecord findBySchemaIdAndVersion(String schemaId, Long version); SchemaRecord findByAlternateId(String alternateId); - List<SchemaRecord> findBySchemaIdOrderByVersionDesc(String schemaId); +// List<SchemaRecord> findBySchemaIdOrderByVersionDesc(String schemaId); + + List<SchemaRecord> findBySchemaIdStartsWithOrderByVersionDesc(String schemaId); - SchemaRecord findFirstBySchemaIdOrderByVersionDesc(String schemaId); + SchemaRecord findFirstBySchemaIdStartsWithOrderByVersionDesc(String schemaId); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index ffcb9de7..b3587a4e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -57,4 +57,12 @@ public class SchemaRecord implements Serializable { private String documentHash; // @NotBlank(message = "Alternate id of schema document.") private String alternateId; + + public String getSchemaId() { + String pureSchemaId = null; + if (schemaId != null) { + pureSchemaId = schemaId.split("/")[0]; + } + return pureSchemaId; + } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 575c65af..a453b10b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -125,7 +125,7 @@ public void run(String... args) throws Exception { for (String index : indices) { LOG.info("Reindex '{}'", index); List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaIdAndLastUpdateAfter(index, updateDate.toInstant()); - List<SchemaRecord> findSchemaBySchemaId = schemaRecordDao.findBySchemaIdOrderByVersionDesc(index); + List<SchemaRecord> findSchemaBySchemaId = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(index + "/"); LOG.trace("Search for documents for schema '{}' and update date '{}'", index, updateDate); LOG.trace("No of documents: '{}'", findBySchemaId.size()); for (DataRecord item : findBySchemaId) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 35fa9054..53e20ff5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -75,6 +75,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -240,10 +241,9 @@ public static DataResource createDataResourceRecord4Metadata(MetastoreConfigurat // Do some checks first. metadataRecord = checkParameters(recordDocument, document, true); Objects.requireNonNull(metadataRecord); - if (metadataRecord.getId() == null) { - String message = "Mandatory attribute 'id' not found in record. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); + if (metadataRecord.getId() != null) { + // Optional id set. Check for valid ID + check4validId(metadataRecord, true); } // End of parameter checks // validate schema document / determine type if not given @@ -368,7 +368,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur MultipartFile document, UnaryOperator<String> supplier) { DataResource metadataRecord = null; - DataResource existingRecord; + DataResource updatedDataResource; // Do some checks first. if ((recordDocument == null || recordDocument.isEmpty()) && (document == null || document.isEmpty())) { @@ -391,22 +391,28 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); + LOG.trace("ETag: '{}'", dataResource.getEtag()); LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, dataResource); if (metadataRecord != null) { - existingRecord = metadataRecord; - // existingRecord = mergeDataResourceRecords(existingRecord, metadataRecord); + updatedDataResource = metadataRecord; + if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { + updatedDataResource.setAcls(dataResource.getAcls()); + } } else { - dataResource = DataResourceUtils.copyDataResource(dataResource); + updatedDataResource = DataResourceUtils.copyDataResource(dataResource); } + LOG.trace("ETag: '{}'", dataResource.getEtag()); + boolean noChanges = false; if (document != null) { - validateMetadataDocument(applicationProperties, document, dataResource.getId(), Long.valueOf(dataResource.getVersion())); + SchemaRecord schemaRecord = getSchemaRecordFromDataResource(updatedDataResource); + validateMetadataDocument(applicationProperties, document, schemaRecord); ContentInformation info; String fileName = document.getOriginalFilename(); - info = getContentInformationOfResource(applicationProperties, dataResource); + info = getContentInformationOfResource(applicationProperties, updatedDataResource); if (info != null) { fileName = info.getRelativePath(); noChanges = true; @@ -435,17 +441,19 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur LOG.trace("Updating schema document (and increment version)..."); String version = dataResource.getVersion(); if (version != null) { - dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); } ContentDataUtils.addFile(applicationProperties, dataResource, document, fileName, null, true, supplier); } } else { + ContentInformation info; + info = getContentInformationOfResource(applicationProperties, updatedDataResource); // validate if document is still valid due to changed record settings. // metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - URI metadataDocumentUri = getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()); + String metadataDocumentUri = info.getContentUri(); - Path metadataDocumentPath = Paths.get(metadataDocumentUri); + Path metadataDocumentPath = Paths.get(URI.create(metadataDocumentUri)); if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); @@ -467,7 +475,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur dataRecordDao.delete(dataRecord.get()); } } - dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); + dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, updatedDataResource, eTag, supplier); return dataResource; } @@ -506,7 +514,7 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application // No references to this schema available -> Ready for deletion if (findOne.isEmpty()) { DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); - List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdOrderByVersionDesc(id); + List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(id + "/"); for (SchemaRecord item : listOfSchemaIds) { LOG.trace("Delete entry for path '{}'", item.getSchemaDocumentUri()); List<Url2Path> findByPath = url2PathDao.findByPath(item.getSchemaDocumentUri()); @@ -1014,6 +1022,66 @@ public static ContentInformation getContentInformationByIdAndVersion(MetastoreCo return metastoreProperties.getContentInformationService().getContentInformation(recordId, null, version); } + public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, + String recordId) throws ResourceNotFoundException { + return getMetadataDocumentByIdAndVersion(metastoreProperties, recordId, null); + } + + public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, + String recordId, Long version) throws ResourceNotFoundException { + LOG.trace("Obtaining content information record with id {} and version {}.", recordId, version); + ContentInformation metadataRecord = getContentInformationByIdAndVersion(metastoreProperties, recordId, version); + + URI metadataDocumentUri = URI.create(metadataRecord.getContentUri()); + + Path metadataDocumentPath = Paths.get(metadataDocumentUri); + if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { + LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); + throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); + } + return metadataDocumentPath; + } + + /** + * Create specification for all listed schemaIds. + * + * @param specification Specification for search. + * @param schemaIds Provided schemaIDs... + * @return Specification with schemaIds added. + */ + public static Specification<DataResource> findBySchemaId(Specification<DataResource> specification, List<String> schemaIds) { + Specification<DataResource> specWithSchema = specification; + if (schemaIds != null) { + List<String> allSchemaIds = new ArrayList<>(); + for (String schemaId : schemaIds) { + allSchemaIds.add(schemaId); + List<SchemaRecord> allVersions = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); + for (SchemaRecord schemaRecord : allVersions) { + allSchemaIds.add(schemaRecord.getAlternateId()); + } + } + if (!allSchemaIds.isEmpty()) { + specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(allSchemaIds.toArray(String[]::new))); + } + } + return specWithSchema; + } + + /** + * Create specification for all listed schemaIds. + * + * @param specification Specification for search. + * @param relatedIds Provided schemaIDs... + * @return Specification with schemaIds added. + */ + public static Specification<DataResource> findByRelatedId(Specification<DataResource> specification, List<String> relatedIds) { + Specification<DataResource> specWithSchema = specification; + if ((relatedIds != null) && !relatedIds.isEmpty()) { + specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(relatedIds.toArray(String[]::new))); + } + return specWithSchema; + } + /** * Merge new metadata record in the existing one. * @@ -1268,11 +1336,11 @@ public static final void fixSchemaUrl(DataResource dataresource) { case 2: schemaId = tokenizer.nextToken(); version = Long.parseLong(tokenizer.nextToken()); - schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + schemaRecord = schemaRecordDao.findBySchemaId(schemaId + "/" + version); break; case 1: schemaId = tokenizer.nextToken(); - schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(schemaId); + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); break; default: throw new CustomInternalServerError("Invalid schemaId!"); @@ -1402,22 +1470,27 @@ public static RelatedIdentifier getRelatedIdentifier(DataResource dataResourceRe * @param metadataRecord Datacite Record. */ public static final void check4validSchemaId(DataResource metadataRecord) { - check4validId(metadataRecord); - String id = metadataRecord.getId(); - String lowerCaseId = id.toLowerCase(); // schema id should be lower case due to elasticsearch // alternate identifier is used to set id to a given id. + check4validId(metadataRecord, false); + } + + public static final void check4validId(DataResource metadataRecord, boolean allowUpperCase) { + String id = metadataRecord.getId(); + String lowerCaseId = id.toLowerCase(); + + if (allowUpperCase) { + lowerCaseId = id; + } metadataRecord.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(lowerCaseId)); if (!lowerCaseId.equals(id)) { metadataRecord.getAlternateIdentifiers().add(Identifier.factoryIdentifier(id, Identifier.IDENTIFIER_TYPE.OTHER)); } - } - public static final void check4validId(DataResource metadataRecord) { try { String value = URLEncoder.encode(metadataRecord.getId(), StandardCharsets.UTF_8.toString()); if (!value.equals(metadataRecord.getId())) { - String message = "Not a valid schema id! Encoded: " + value; + String message = "Not a valid ID! Encoded: " + value; LOG.error(message); throw new BadArgumentException(message); } @@ -1426,6 +1499,7 @@ public static final void check4validId(DataResource metadataRecord) { LOG.error(message); throw new CustomInternalServerError(message); } + } private static void validateMetadataSchemaDocument(MetastoreConfiguration metastoreProperties, DataResource schemaRecord, MultipartFile document) { @@ -1604,9 +1678,9 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp } schemaId = dataResource.getId(); if (version != null) { - schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + schemaRecord = schemaRecordDao.findBySchemaId(schemaId + "/" + version); } else { - schemaRecord = schemaRecordDao.findBySchemaIdOrderByVersionDesc(schemaId).get(0); + schemaRecord = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/").get(0); } if (schemaRecord == null) { String message = "Unknown version '" + version + "' for schemaID '" + schemaId + "'!"; @@ -1728,9 +1802,9 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v throw new BadArgumentException(message); } if (version != null) { - schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + schemaRecord = schemaRecordDao.findBySchemaId(schemaId + "/" + version); } else { - schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(schemaId); + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); } } case URL -> { @@ -1747,6 +1821,30 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v return schemaRecord; } + private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataResource) { + SchemaRecord schemaRecord = null; + RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataResource); + String schemaId = schemaIdentifier.getValue(); + switch (schemaIdentifier.getIdentifierType()) { + case URL: + schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); + break; + case INTERNAL: + String[] split = schemaId.split("/"); + if (split.length == 1) { + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); + } else { + schemaRecord = schemaRecordDao.findBySchemaId(schemaId); + } + break; + default: + String message = "Unsupported identifier type: '" + schemaIdentifier.getIdentifierType() + "'!"; + LOG.error(message); + throw new ResourceNotFoundException(message); + } + return schemaRecord; + } + private static SchemaRecord prepareResourceFromUrl(ResourceIdentifier identifier, Long version) { String url = identifier.getIdentifier(); Path pathToFile; @@ -1876,7 +1974,7 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app } ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); SchemaRecord schemaRecord = createSchemaRecord(dataResource, contentInformation); - MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); + MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); } } else { // validate if document is still valid due to changed record settings. @@ -1928,7 +2026,7 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro if (schemaIdentifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { findByAlternateId = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); } else { - findByAlternateId = schemaRecordDao.findBySchemaIdOrderByVersionDesc(schemaIdentifier.getValue()).get(0); + findByAlternateId = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaIdentifier.getValue() + "/"); } if (findByAlternateId != null) { try { @@ -1940,14 +2038,15 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro errorMessage.append(ex.getMessage()).append("\n"); } } else { - throw new CustomInternalServerError("No schema registries defined! "); + errorMessage.append("No matching schema found for '" + schemaIdentifier.getValue() + "'!"); } } if (!validationSuccess) { + LOG.error(errorMessage.toString()); throw new UnprocessableEntityException(errorMessage.toString()); } } - + /** * Create schema record from DataResource and ContentInformation. * @@ -1971,7 +2070,7 @@ public static final SchemaRecord createSchemaRecord(DataResource dataResource, C } } Long currentVersion = Long.valueOf(dataResource.getVersion()); - String schemaUrl = getSchemaDocumentUri(dataResource.getId(),currentVersion); + String schemaUrl = getSchemaDocumentUri(dataResource.getId(), currentVersion); schemaRecord.setVersion(currentVersion); schemaRecord.setSchemaDocumentUri(contentInformation.getContentUri()); schemaRecord.setDocumentHash(contentInformation.getHash()); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 32f55948..39ebef8e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -259,7 +259,7 @@ public static MetadataSchemaRecord updateMetadataSchemaRecord(MetastoreConfigura DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, dataResource); - SchemaRecord schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(dataResource.getId()); + SchemaRecord schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(dataResource.getId() + "/"); if (metadataRecord != null) { metadataRecord.setSchemaVersion(schemaRecord.getVersion()); MetadataSchemaRecord existingRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); @@ -351,7 +351,7 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application String id, String eTag, UnaryOperator<String> supplier) { - List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdOrderByVersionDesc(id); + List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(id + "/"); // Test for linked metadata documents for (SchemaRecord item : listOfSchemaIds) { List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaId(item.getSchemaId()); @@ -360,7 +360,7 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application } } DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); - listOfSchemaIds = schemaRecordDao.findBySchemaIdOrderByVersionDesc(id); + listOfSchemaIds = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(id + "/"); for (SchemaRecord item : listOfSchemaIds) { LOG.trace("Delete entry for path '{}'", item.getSchemaDocumentUri()); List<Url2Path> findByPath = url2PathDao.findByPath(item.getSchemaDocumentUri()); @@ -612,7 +612,7 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecord(RepoBaseConfigu SchemaRecord schemaRecord; try { LOG.debug("findByIDAndVersion {},{}", dataResource.getId(), metadataSchemaRecord.getSchemaVersion()); - schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(dataResource.getId(), metadataSchemaRecord.getSchemaVersion()); + schemaRecord = schemaRecordDao.findBySchemaId(dataResource.getId() + "/" + metadataSchemaRecord.getSchemaVersion()); metadataSchemaRecord.setSchemaDocumentUri(schemaRecord.getSchemaDocumentUri()); metadataSchemaRecord.setSchemaHash(schemaRecord.getDocumentHash()); } catch (NullPointerException npe) { @@ -742,9 +742,9 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v throw new BadArgumentException(message); } if (version != null) { - schemaRecord = schemaRecordDao.findBySchemaIdAndVersion(schemaId, version); + schemaRecord = schemaRecordDao.findBySchemaId(schemaId + "/" + version); } else { - schemaRecord = schemaRecordDao.findFirstBySchemaIdOrderByVersionDesc(schemaId); + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); } break; case URL: @@ -1128,9 +1128,9 @@ public static void saveNewSchemaRecord(SchemaRecord schemaRecord) { if (schemaRecordDao != null) { try { schemaRecord.setAlternateId(DataResourceRecordUtil.getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion())); -// if (new StringTokenizer(schemaRecord.getSchemaId()).countTokens() < 2) { -// schemaRecord.setSchemaId(schemaRecord.getSchemaId() + " " + schemaRecord.getVersion()); -// } + if (new StringTokenizer(schemaRecord.getSchemaId()).countTokens() < 2) { + schemaRecord.setSchemaId(schemaRecord.getSchemaId() + " " + schemaRecord.getVersion()); + } schemaRecordDao.save(schemaRecord); } catch (Exception npe) { LOG.error("Can't save schema record: " + schemaRecord, npe); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 71608cfe..984bf98c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -235,6 +235,7 @@ public ResponseEntity createRecord( // return ResponseEntity.status(HttpStatus.CONFLICT).body(message); // } DataResource result = DataResourceRecordUtil.createDataResourceRecord4Metadata(metadataConfig, recordDocument, document); + String eTag = result.getEtag(); // Successfully created metadata record. // long nano4 = System.nanoTime() / 1000000; LOG.trace("Metadata record successfully persisted. Returning result."); @@ -252,7 +253,7 @@ public ResponseEntity createRecord( messagingService.orElse(new LogfileMessagingService()). send(MetadataResourceMessage.factoryCreateMetadataMessage(result, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); - return ResponseEntity.created(locationUri).eTag("\"" + result.getEtag() + "\"").body(result); + return ResponseEntity.created(locationUri).eTag("\"" + eTag + "\"").body(result); } @Override @@ -271,8 +272,10 @@ public ResponseEntity<DataResource> getRecordById( LOG.trace("Get ETag of DataResource."); String etag = metadataRecord.getEtag(); DataResourceRecordUtil.fixSchemaUrl(metadataRecord); + URI locationUri; + locationUri = DataResourceRecordUtil.getMetadataDocumentUri(metadataRecord.getId(), metadataRecord.getVersion()); - return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); + return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(metadataRecord); } @Override @@ -282,11 +285,18 @@ public ResponseEntity<ContentInformation> getContentInformationById( WebRequest wr, HttpServletResponse hsr ) { - LOG.trace("Performing getRecordById({}, {}).", id, version); + LOG.trace("Performing getContentInformationById({}, {}).", id, version); LOG.trace("Obtaining metadata record with id {} and version {}.", id, version); ContentInformation contentInformation = DataResourceRecordUtil.getContentInformationByIdAndVersion(metadataConfig, id, version); - LOG.trace("Metadata record found. Prepare response."); + LOG.trace("ContentInformation record found. Prepare response..."); + DataResource minimalDataResource = DataResource.factoryNewDataResource(contentInformation.getParentResource().getId()); + URI locationUri; + locationUri = DataResourceRecordUtil.getMetadataDocumentUri(id, contentInformation.getFileVersion()); + contentInformation.setParentResource(minimalDataResource); + contentInformation.setContentUri(locationUri.toString()); + contentInformation.setRelativePath(null); + contentInformation.setVersioningService(null); return ResponseEntity.ok().body(contentInformation); } @@ -321,7 +331,7 @@ public ResponseEntity getMetadataDocumentById( ) { LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); - Path metadataDocumentPath = MetadataRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); + Path metadataDocumentPath = DataResourceRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); return ResponseEntity. ok(). @@ -390,7 +400,7 @@ public ResponseEntity<List<DataResource>> getRecords( return getAllVersions(id, pgbl); } // Search for resource type of MetadataSchemaRecord - Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResourceRecordUtil.METADATA_SUFFIX)); + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResourceRecordUtil.METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); // Add authentication if enabled if (metadataConfig.isAuthEnabled()) { boolean isAdmin; @@ -407,45 +417,8 @@ public ResponseEntity<List<DataResource>> getRecords( } } } - List<String> allRelatedIdentifiersSchema = new ArrayList<>(); - List<String> allRelatedIdentifiersResource = new ArrayList<>(); - -// File file = new File(new URIoa) - if (schemaIds != null) { - for (String schemaId : schemaIds) { - MetadataSchemaRecord currentSchemaRecord; - try { - currentSchemaRecord = MetadataRecordUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); - // Test for internal URI -> Transform to global URI. - if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { - ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); - currentSchemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); - } - allRelatedIdentifiersSchema.add(currentSchemaRecord.getSchemaDocumentUri()); - } catch (Exception rnfe) { - // schemaID not found set version to 1 - currentSchemaRecord = new MetadataSchemaRecord(); - currentSchemaRecord.setSchemaVersion(1l); - allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); - } - for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { - MetadataSchemaRecord schemaRecord = MetadataRecordUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); - // Test for internal URI -> Transform to global URI. - if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { - ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); - schemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); - } - allRelatedIdentifiersSchema.add(schemaRecord.getSchemaDocumentUri()); - } - } - Specification<DataResource> schemaSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersSchema.toArray(new String[allRelatedIdentifiersSchema.size()])); - spec = spec.and(schemaSpecification); - } - if (relatedIds != null) { - allRelatedIdentifiersResource.addAll(relatedIds); - Specification<DataResource> relResourceSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersResource.toArray(new String[allRelatedIdentifiersResource.size()])); - spec = spec.and(relResourceSpecification); - } + spec = DataResourceRecordUtil.findBySchemaId(spec, schemaIds); + spec = DataResourceRecordUtil.findByRelatedId(spec, relatedIds); if ((updateFrom != null) || (updateUntil != null)) { spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); } @@ -470,12 +443,6 @@ public ResponseEntity<List<DataResource>> getRecords( LOG.trace("Transforming Dataresource to DataResource"); List<DataResource> recordList = records.getContent(); -// List<DataResource> metadataList = new ArrayList<>(); -// recordList.forEach(metadataRecord -> { -//// DataResource item = MetadataRecordUtil.migrateToDataResource(metadataConfig, metadataRecord, false); -//// MetadataRecordUtil.fixMetadataDocumentUri(item); -// metadataList.add(metadataRecord); -// }); String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); @@ -499,6 +466,7 @@ public ResponseEntity updateRecord( LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); String etag = updateDataResource.getEtag(); + DataResourceRecordUtil.fixSchemaUrl(updateDataResource); URI locationUri; locationUri = DataResourceRecordUtil.getMetadataDocumentUri(updateDataResource.getId(), updateDataResource.getVersion()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java index e099fd4b..56cbbabc 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java @@ -497,16 +497,17 @@ public static MvcResult ingestOrUpdateXmlMetadataDocumentV2(MockMvc mockMvc, Str metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", metadataDocument.getBytes()); } result = mockMvc.perform(get("/api/v2/metadata/" + metadataId). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andReturn(); if (result.getResponse().getStatus() != HttpStatus.OK.value()) { // Create metadata document - MockMultipartHttpServletRequestBuilder file = MockMvcRequestBuilders.multipart("/api/v1/metadata/").file(recordFile); + MockMultipartHttpServletRequestBuilder file = MockMvcRequestBuilders.multipart("/api/v2/metadata/").file(recordFile); if (metadataFile != null) { file = file.file(metadataFile); } - MockHttpServletRequestBuilder header = file.header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken); + MockHttpServletRequestBuilder header = file.header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE); result = mockMvc.perform(header). andDo(print()). andExpect(expectedStatus). diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index e1305d33..ea1bf7ad 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -110,13 +110,12 @@ @TestPropertySource(properties = {"repo.search.url=http://localhost:41421"}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) public class MetadataControllerTestV2 { - + private static final String API_BASE_PATH = "/api/v2"; private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; private static final String API_METADATA_PATH = API_BASE_PATH + "/metadata/"; - private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/md/"; private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; @@ -366,10 +365,10 @@ public void testCreateRecordWithRelatedResourceOfTypeUrl() throws Exception { DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(result, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); - Assert.assertEquals("Value of related resource should be unchanged!", + Assert.assertEquals("Value of related resource should be unchanged!", relatedIdentifier1.getValue(), relatedIdentifier2.getValue()); - Assert.assertEquals("Type of related resource should be unchanged!", + Assert.assertEquals("Type of related resource should be unchanged!", relatedIdentifier1.getIdentifierType(), relatedIdentifier2.getIdentifierType()); } @@ -394,7 +393,7 @@ public void testCreateRecordWithUrlSchemaNull() throws Exception { String id = "testCreateRecordWithUrlSchemaNull"; String schemaId = null; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); - for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + for (RelatedIdentifier item : record.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { item.setValue(null); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); @@ -416,10 +415,10 @@ public void testCreateRecordWithUrlSchemaNull() throws Exception { @Test public void testCreateRecordWithInvalidUrl() throws Exception { String id = "testCreateRecordWithInvalidUrl"; - String invalidSchemaUrl = getSchemaUrl(SCHEMA_ID).substring(1); + String invalidSchemaUrl = getSchemaUrl(SCHEMA_ID).substring(1); String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); - for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + for (RelatedIdentifier item : record.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { item.setValue(invalidSchemaUrl); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); @@ -444,7 +443,7 @@ public void testCreateRecordWithInvalidUrlSchema() throws Exception { String schemaId = INVALID_SCHEMA; String urlWithInvalidSchema = getSchemaUrl(SCHEMA_ID).replace(SCHEMA_ID, INVALID_SCHEMA); DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); - for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + for (RelatedIdentifier item : record.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { item.setValue(urlWithInvalidSchema); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); @@ -479,7 +478,7 @@ public void testCreateRecordWithAnyValidUrl() throws Exception { String schemaId = INVALID_SCHEMA; String schemaUrl = "http://anyurl.example.org/shouldNotExist"; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); - for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + for (RelatedIdentifier item : record.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { item.setValue(schemaUrl); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); @@ -530,11 +529,6 @@ public void testCreateRecordWithInvalidId() throws Exception { String id = "http://localhost:8080/d1"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); - // XXXXX XXX XXXX XXX - // X X X X X X X - // X X X X X X X - // X X X X X X X - // X XXX XXXX XXX ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -558,7 +552,7 @@ public void testCreateRecordWithIdTwice() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); - for (RelatedIdentifier item: record.getRelatedIdentifiers()) { + for (RelatedIdentifier item : record.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR)) { item.setValue(RELATED_RESOURCE_2.getIdentifier()); } @@ -592,11 +586,7 @@ public void testCreateRecordWithLocationUri() throws Exception { DataResource data2 = mapper.readValue(result2.getResponse().getContentAsString(), DataResource.class); SchemaRegistryControllerTestV2.validateDataResources(data1, data2); } - // XXXXX XXX XXXX XXX - // X X X X X X X - // X X X X X X X - // X X X X X X X - // X XXX XXXX XXX + @Test public void testCreateInvalidRecord() throws Exception { String id = "testCreateInvalidRecord"; @@ -734,12 +724,9 @@ public void testCreateRecordWithEmptyAclSid() throws Exception { String id = "testCreateRecordWithEmptyAclSid"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); -// Set<AclEntry> aclEntries = new HashSet<>(); -// aclEntries.add(new AclEntry(null, PERMISSION.READ)); -// record.setAcl(aclEntries); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry(null, PERMISSION.READ)); + record.setAcls(aclEntries); ObjectMapper mapper = new ObjectMapper(); @@ -758,9 +745,6 @@ public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { String id = "testCreateRecordWithInvalidMetadataNamespace"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -776,9 +760,6 @@ public void testCreateRecordWithInvalidMetadata() throws Exception { String id = "testCreateRecordWithInvalidMetadata"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -793,7 +774,11 @@ public void testCreateRecordWithInvalidMetadata() throws Exception { public void testCreateRecordWithoutRecord() throws Exception { MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). - file(metadataFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + // no record defined! + file(metadataFile)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); } @Test @@ -801,14 +786,15 @@ public void testCreateRecordWithoutSchema() throws Exception { String id = "testCreateRecordWithoutSchema"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). - file(recordFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + // no schema defined! + file(recordFile)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); } @Test @@ -818,17 +804,16 @@ public void testCreateRecordWithBadRecord() throws Exception { DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); ObjectMapper mapper = new ObjectMapper(); -// DataResource record = new DataResource(); -// //schemaId is missing -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(null)); -// record.setRelatedResource(RELATED_RESOURCE); + //schemaId is missing + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + schemaIdentifier.setValue(null); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). - file(metadataFile)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + file(metadataFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } @Test @@ -838,11 +823,9 @@ public void testCreateRecordWithBadRecord2() throws Exception { String id = "testCreateRecordWithLocationUri"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + //remove related resource RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); record.getRelatedIdentifiers().remove(relatedIdentifier); -// DataResource record = new DataResource(); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - //related resource is missing MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); @@ -902,19 +885,13 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { Assert.assertTrue(res.getResponse().getContentAsString().contains("Conflict")); Assert.assertTrue(res.getResponse().getContentAsString().contains(id)); - Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); } @Test public void testCreateTwoVersions() throws Exception { - String id = "testCreateTwoVersions"; + String id = "testCreateTwoVersions"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); -// DataResource record = new DataResource(); -//// record.setId("my_id"); -// record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); -// record.setRelatedResource(RELATED_RESOURCE); -// Set<AclEntry> aclEntries = new HashSet<>(); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -929,6 +906,7 @@ public void testCreateTwoVersions() throws Exception { DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setValue(RELATED_RESOURCE_2.toString()); record.getAlternateIdentifiers().clear(); + record.setId(null); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). @@ -942,7 +920,7 @@ public void testCreateTwoVersions() throws Exception { public void testGetRecordById() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); @@ -961,7 +939,7 @@ public void testGetRecordById() throws Exception { public void testGetRecordByIdWithVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1").header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1").header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); ObjectMapper map = new ObjectMapper(); DataResource result = map.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertNotNull(result); @@ -978,12 +956,13 @@ public void testGetRecordByIdWithVersion() throws Exception { @Test public void testGetRecordByIdWithInvalidId() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + "cd"). + String wrongId = metadataRecordId.substring(1); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + wrongId). header("Accept", MediaType.APPLICATION_JSON_VALUE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); - Assert.assertTrue("Try to access invalid id!", result.getResponse().getContentAsString().contains("Metadata document with ID 'cd' doesn't exist!")); + Assert.assertTrue("Try to access invalid id!", result.getResponse().getContentAsString().contains("No content information for identifier " + wrongId)); } @Test @@ -991,7 +970,7 @@ public void testGetRecordByIdWithInvalidVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). param("version", "13"). - header("Accept", MediaType.APPLICATION_JSON_VALUE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); @@ -1007,6 +986,7 @@ public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { Assert.assertEquals(1, result.length); } +// ToDo @Test public void testFindRecordsBySchemaId() throws Exception { @@ -1035,24 +1015,24 @@ public void testFindRecordsByInvalidSchemaId() throws Exception { @Test public void testFindRecordsOfMultipleVersionsBySchemaId() throws Exception { String schemaId = "multipleSchemas".toLowerCase(Locale.getDefault()); - CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V1, metadataConfig.getJwtSecret(), false, status().isCreated()); - CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V2, metadataConfig.getJwtSecret(), true, status().isOk()); - CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V3, metadataConfig.getJwtSecret(), true, status().isOk()); + String schema1 = SchemaRegistryControllerTestV2.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V1, metadataConfig.getJwtSecret(), false, status().isCreated()); + String schema2 = SchemaRegistryControllerTestV2.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V2, metadataConfig.getJwtSecret(), true, status().isOk()); + String schema3 = SchemaRegistryControllerTestV2.ingestOrUpdateXmlSchemaRecord(mockMvc, schemaId, XML_SCHEMA_V3, metadataConfig.getJwtSecret(), true, status().isOk()); ObjectMapper map = new ObjectMapper(); String[] multipleVersions = {"1", "2", "3"}; // Ingest 1st version of document. - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 1l, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema1, 1l, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); // Ingest 2nd version of document - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 2l, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema2, 2l, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); // Ingest 3rd version of document - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 3l, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema3, 3l, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); @@ -1080,15 +1060,17 @@ public void testFindRecordsByResourceId() throws Exception { public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String relatedResource = "testFindRecordsBySchemaIdANDResourceId"; String relatedResource2 = "anotherTestFindRecordsBySchemaIdANDResourceId"; - String secondSchemaId = "schema_for_find_records_by_schema_and_resource_id"; - CreateSchemaUtil.ingestKitSchemaRecord(mockMvc, secondSchemaId, schemaConfig.getJwtSecret()); - String metadataRecordId = createDCMetadataRecordWithRelatedResource(relatedResource, SCHEMA_ID); - String metadataRecordId2 = createDCMetadataRecordWithRelatedResource(relatedResource2, SCHEMA_ID); + String firstSchemaId = "first_schema_for_find_records_by_schema_and_resource_id"; + String secondSchemaId = "second_schema_for_find_records_by_schema_and_resource_id"; + CreateSchemaUtil.ingestKitSchemaRecordV2(mockMvc, firstSchemaId, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestKitSchemaRecordV2(mockMvc, secondSchemaId, schemaConfig.getJwtSecret()); + String metadataRecordId = createDCMetadataRecordWithRelatedResource(relatedResource, firstSchemaId); + String metadataRecordId2 = createDCMetadataRecordWithRelatedResource(relatedResource2, firstSchemaId); String metadataRecordIdv2 = createDCMetadataRecordWithRelatedResource(relatedResource, secondSchemaId); String metadataRecordId2v2 = createDCMetadataRecordWithRelatedResource(relatedResource2, secondSchemaId); // Looking for first schema MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH). - param("schemaId", SCHEMA_ID)). + param("schemaId", firstSchemaId)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1105,7 +1087,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { Assert.assertEquals(2, result.length); // Looking for first AND second schema res = this.mockMvc.perform(get(API_METADATA_PATH). - param("schemaId", SCHEMA_ID). + param("schemaId", firstSchemaId). param("schemaId", secondSchemaId)). andDo(print()). andExpect(status().isOk()). @@ -1114,7 +1096,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { Assert.assertEquals(4, result.length); // Looking for first, second AND invalid schema res = this.mockMvc.perform(get(API_METADATA_PATH). - param("schemaId", SCHEMA_ID). + param("schemaId", firstSchemaId). param("schemaId", secondSchemaId). param("schemaId", "invalidschemaid")). andDo(print()). @@ -1124,7 +1106,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { Assert.assertEquals(4, result.length); // Looking for first, second AND invalid schema AND resource1 and resource2 res = this.mockMvc.perform(get(API_METADATA_PATH). - param("schemaId", SCHEMA_ID). + param("schemaId", firstSchemaId). param("schemaId", secondSchemaId). param("schemaId", "invalidschemaid"). param("resourceId", relatedResource). @@ -1136,7 +1118,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { Assert.assertEquals(4, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). - param("schemaId", SCHEMA_ID). + param("schemaId", firstSchemaId). param("resourceId", relatedResource). param("resourceId", relatedResource2)). andDo(print()). @@ -1146,7 +1128,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { Assert.assertEquals(2, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). - param("schemaId", SCHEMA_ID). + param("schemaId", firstSchemaId). param("resourceId", relatedResource)). andDo(print()). andExpect(status().isOk()). @@ -1155,7 +1137,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { Assert.assertEquals(1, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). - param("schemaId", SCHEMA_ID). + param("schemaId", firstSchemaId). param("resourceId", relatedResource2)). andDo(print()). andExpect(status().isOk()). @@ -1238,7 +1220,7 @@ public void testFindRecordsByUnknownParameter() throws Exception { @Test public void testGetSchemaDocument() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).accept(MediaType.APPLICATION_XML)).andDo(print()).andExpect(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT; @@ -1256,22 +1238,36 @@ public void testGetMetadataDocumentWithUnknownSchema() throws Exception { @Test public void testUpdateRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); - String etag = result.getResponse().getHeader("ETag"); + ObjectMapper mapper = new ObjectMapper(); + // Get ContentInformation of first version + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri = result.getResponse().getHeader("Location"); + String etag = result.getResponse().getHeader("ETag"); + body = result.getResponse().getContentAsString(); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). file(recordFile). - file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + file(metadataFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()).andExpect(status().isOk()).andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); body = result.getResponse().getContentAsString(); - String locationUri = result.getResponse().getHeader("Location"); DataResource record2 = mapper.readValue(body, DataResource.class); // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); @@ -1280,9 +1276,29 @@ public void testUpdateRecord() throws Exception { Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); + Assert.assertEquals(contentInformation1.getVersion() + 1, contentInformation2.getVersion().longValue()); + Assert.assertNotEquals(contentInformation1, contentInformation2); + Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); + Assert.assertNotEquals(contentInformation1.getFileVersion(), contentInformation2.getFileVersion()); + Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); + Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); + // Check for new metadata document. - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); - String locationUri2 = result.getResponse().getHeader("Location"); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_JSON)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; @@ -1295,7 +1311,7 @@ public void testUpdateRecord() throws Exception { @Test public void testUpdateRecordWithWrongVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1309,7 +1325,7 @@ public void testUpdateRecordWithWrongVersion() throws Exception { file(recordFile). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); @@ -1348,7 +1364,7 @@ public void testUpdateRecordWithWrongVersion() throws Exception { @Test public void testUpdateRecordIgnoreACL() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1366,7 +1382,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); String locationUri = result.getResponse().getHeader("Location"); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); @@ -1408,6 +1424,16 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); + // Check ContentInformation of first version + result = this.mockMvc.perform(get(locationUri). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String cibody = result.getResponse().getContentAsString(); + + ContentInformation contentInformation1 = mapper.readValue(cibody, ContentInformation.class); + DataResource record2 = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); MockMultipartFile metadataFile2 = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); @@ -1421,27 +1447,45 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); DataResource record3 = mapper.readValue(body, DataResource.class); // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); - Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record2.getRelatedIdentifiers(), record2.getRelatedIdentifiers()); Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()) - 1l);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + id). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); + Assert.assertEquals(contentInformation1.getVersion() + 1, contentInformation2.getVersion().longValue()); + Assert.assertNotEquals(contentInformation1, contentInformation2); + Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); + Assert.assertNotEquals(contentInformation1.getFileVersion(), contentInformation2.getFileVersion()); + Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); + Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); + Assert.assertEquals(DC_DOCUMENT.length(), contentInformation1.getSize()); + Assert.assertEquals(DC_DOCUMENT_VERSION_2.length(), contentInformation2.getSize()); } @Test public void testUpdateRecordWithSameDocument() throws Exception { + String id = "testUpdateRecordWithSameDocument"; ObjectMapper mapper = new ObjectMapper(); - MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, id, DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); DataResource record1 = mapper.readValue(body, DataResource.class); // Update without any changes. - result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, id, DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); @@ -1451,12 +1495,12 @@ public void testUpdateRecordWithSameDocument() throws Exception { @Test public void testUpdateRecordWithSmallChangesInDocument() throws Exception { ObjectMapper mapper = new ObjectMapper(); - MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); DataResource record1 = mapper.readValue(body, DataResource.class); // Update without any changes. - result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); @@ -1467,7 +1511,7 @@ public void testUpdateRecordWithSmallChangesInDocument() throws Exception { @Test public void testUpdateRecordWithoutETag() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1484,7 +1528,7 @@ public void testUpdateRecordWithoutETag() throws Exception { @Test public void testUpdateRecordWithWrongETag() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag") + "unknown"; String body = result.getResponse().getContentAsString(); @@ -1506,7 +1550,7 @@ public void testUpdateRecordWithWrongETag() throws Exception { @Test public void testUpdateRecordWithoutRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1533,7 +1577,7 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { // Update only Json document ingestJsonSchemaRecord(); String metadataRecordId = createJsonMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1571,38 +1615,68 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { @Test public void testUpdateRecordWithoutDocument() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); - String etag = result.getResponse().getHeader("ETag"); + ObjectMapper mapper = new ObjectMapper(); + // Check ContentInformation of first version + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + body = result.getResponse().getContentAsString(); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));// version should be the same SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); + Assert.assertNotEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1, contentInformation2); } @Test public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exception { String metadataRecordId = createDCMetadataRecord(); String expectedRelatedResource; - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); - String etag = result.getResponse().getHeader("ETag"); + ObjectMapper mapper = new ObjectMapper(); + // Check ContentInformation of first version + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + // Get dataresource record version 1 + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String etag = result.getResponse().getHeader("ETag"); + body = result.getResponse().getContentAsString(); + DataResource record = mapper.readValue(body, DataResource.class); DataResource record2 = mapper.readValue(body, DataResource.class); RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); @@ -1612,7 +1686,7 @@ public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exce result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); body = result.getResponse().getContentAsString(); etag = result.getResponse().getHeader("ETag"); @@ -1620,9 +1694,8 @@ public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exce // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(record.getVersion(), record2.getVersion());// version should be the same SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); @@ -1637,23 +1710,30 @@ public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exce result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); -// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); body = result.getResponse().getContentAsString(); DataResource record3 = mapper.readValue(body, DataResource.class); -// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); - SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record3.getDates()); - Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record3)); - Assert.assertEquals(record.getEtag(), record3.getEtag()); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record3.getVersion()) - 1l);// version should be 1 higher - SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record3.getAcls()); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record3.getEtag()); - Assert.assertTrue(record.getLastUpdate().isBefore(record3.getLastUpdate())); - RelatedIdentifier relatedIdentifier3 = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); - RelatedIdentifier relatedIdentifier4 = DataResourceRecordUtil.getRelatedIdentifier(record3, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); - Assert.assertEquals("Related resource should be the same!", relatedIdentifier3.getValue(), relatedIdentifier4.getValue()); - Assert.assertEquals("Related resource type should be changed!", expectedIdentifierType, relatedIdentifier4.getIdentifierType()); + SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record2), DataResourceRecordUtil.getSchemaIdentifier(record3)); + Assert.assertEquals(record2.getVersion(), record3.getVersion());// version should be the same + SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); + Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(record3, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals("Related resource should be the same!", relatedIdentifier1.getValue(), relatedIdentifier2.getValue()); + Assert.assertEquals("Related resource type should be updated!", expectedIdentifierType, relatedIdentifier2.getIdentifierType()); + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1, contentInformation2); } @Test @@ -1663,24 +1743,23 @@ public void testUpdateRecordWithInvalidSetting4Json() throws Exception { CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V2, schemaConfig.getJwtSecret(), true, status().isOk()); CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, alternativeSchemaId, 2l, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); // Change only version of schema to a version which is not valid. - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, alternativeSchemaId, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); // Change to a nonexistent version of schema. - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isNotFound()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isNotFound()); // Change to another schema - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); } @Test public void testUpdateRecordWithLicense() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MediaType.APPLICATION_JSON_VALUE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); @@ -1701,7 +1780,7 @@ public void testUpdateRecordWithLicense() throws Exception { andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); @@ -1732,7 +1811,7 @@ public void testUpdateRecordWithLicense() throws Exception { Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MediaType.APPLICATION_JSON_VALUE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1752,7 +1831,7 @@ record = mapper.readValue(body, DataResource.class); andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MediaType.APPLICATION_JSON_VALUE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); DataResource record3 = mapper.readValue(body, DataResource.class); @@ -1770,7 +1849,7 @@ record = mapper.readValue(body, DataResource.class); public void testDeleteRecordWithoutAuthentication() throws Exception { String metadataRecordId = createDCMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); @@ -1785,14 +1864,14 @@ public void testDeleteRecord() throws Exception { // Get a list of all records MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH). - header("Accept", MediaType.APPLICATION_JSON_VALUE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; // Get Etag - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); // Delete record this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId).header("If-Match", etag)).andDo(print()).andExpect(status().isNoContent()).andReturn(); @@ -1815,7 +1894,7 @@ public void testDeleteRecord() throws Exception { // List of records should be smaller afterwards result = this.mockMvc.perform(get(API_METADATA_PATH). - header("Accept", MediaType.APPLICATION_JSON_VALUE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1832,7 +1911,7 @@ public void testGetAllVersionsOfRecord() throws Exception { // Read all versions this.mockMvc.perform(get(API_METADATA_PATH).param("id", id).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -2051,7 +2130,7 @@ public void testLandingPage4Metadata() throws Exception { .andDo(print()) .andExpect(status().isOk()); // Ingest a second version... - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + documentId).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + documentId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -2109,7 +2188,7 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { andReturn(); // Get Etag result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MediaType.APPLICATION_JSON_VALUE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -2130,7 +2209,7 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { // Delete second time result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MediaType.APPLICATION_JSON_VALUE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -2190,7 +2269,7 @@ private String createDCMetadataRecord() throws Exception { private String createDCMetadataRecordWithRelatedResource(String myRelatedResource, String schemaId) throws Exception { String randomId = UUID.randomUUID().toString(); - DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(randomId, SCHEMA_ID); + DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(randomId, schemaId); RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); relatedIdentifier.setIdentifierType(IDENTIFIER_TYPE.INTERNAL); relatedIdentifier.setValue(myRelatedResource); @@ -2224,7 +2303,7 @@ private String ingestMetadataRecordWithVersion(String id, long version) throws E id = createDCMetadataRecord(); } else { // add new version - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -2256,7 +2335,7 @@ private String ingestNewMetadataRecord(String id, long version) throws Exception id = createDCMetadataRecord(); } else { // add new version - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", MediaType.APPLICATION_JSON_VALUE)).andDo(print()).andExpect(status().isOk()).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + id).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -2288,7 +2367,7 @@ private static RequestPostProcessor putMultipart() { // it's nice to extract int return request; }; } - + private void ingestJsonSchemaRecord() throws Exception { MetadataSchemaRecord record = new MetadataSchemaRecord(); record.setSchemaId(JSON_SCHEMA_ID); @@ -2347,8 +2426,9 @@ private String getSchemaUrl(String schemaId) throws Exception { String content = res.getResponse().getContentAsString(); ContentInformation result = map.readValue(res.getResponse().getContentAsString(), ContentInformation.class); schemaUrl = result.getContentUri(); - if (schemaUrl != null) + if (schemaUrl != null) { schemaUrl = schemaUrl.replaceFirst("8080", "41421"); + } return schemaUrl; } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 15702eb6..76b81b4f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.type.CollectionType; import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.MetadataRecord; @@ -34,6 +35,7 @@ import edu.kit.datamanager.repo.domain.Scheme; import edu.kit.datamanager.repo.domain.Title; import edu.kit.datamanager.repo.domain.acl.AclEntry; +import edu.kit.datamanager.util.AuthenticationHelper; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -65,6 +67,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; @@ -84,6 +87,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -1928,10 +1932,14 @@ public static DataResource createDataResource4Document(String id, String schemaI relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); record.getRelatedIdentifiers().add(relatedResource); relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaId, null, null); - relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + if ((schemaId != null) && schemaId.startsWith("http")) { + relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + } else { + relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + } record.getRelatedIdentifiers().add(relatedResource); if (metadataType.contains("XML")) { - record.getFormats().add(MediaType.APPLICATION_XML.toString()); + record.getFormats().add(MediaType.APPLICATION_XML.toString()); } else { record.getFormats().add(MediaType.APPLICATION_JSON.toString()); } @@ -1976,6 +1984,80 @@ private static RequestPostProcessor putMultipart() { // it's nice to extract int }; } + /** + * Update schema in MetaStore as user 'test_user'. If schema already exists + * and noUpdate is false update schema. + * + * @param mockMvc + * @param schemaId + * @param schemaContent + * @param jwtSecret + * @param noUpdate Only ingest or do update also + * @return + * @throws Exception + */ + public static String ingestOrUpdateXmlSchemaRecord(MockMvc mockMvc, String schemaId, String schemaContent, String jwtSecret, boolean update, ResultMatcher expectedStatus) throws Exception { + String locationUri = null; + jwtSecret = (jwtSecret == null) ? "jwtSecret" : jwtSecret; + String userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken("test_user", RepoUserRole.USER). + addSimpleClaim("email", "any@example.org"). + addSimpleClaim("orcid", "0000-0001-2345-6789"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(jwtSecret); + DataResource record = createDataResource4XmlSchema(schemaId); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); + record.setAcls(aclEntries); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile; + MockMultipartFile schemaFile; + recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", schemaContent.getBytes()); + // Test if schema is already registered. + MvcResult result = mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andReturn(); + if (result.getResponse().getStatus() != HttpStatus.OK.value()) { + + result = mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()).andExpect(expectedStatus).andReturn(); + if (result.getResponse().getStatus() == HttpStatus.CREATED.value()) { + locationUri = result.getResponse().getHeader("Location"); + } + } else { + if (update) { + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + record = mapper.readValue(body, DataResource.class); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + // Update metadata document + MockHttpServletRequestBuilder header = MockMvcRequestBuilders. + multipart(API_SCHEMA_PATH + schemaId). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart()); + result = mockMvc.perform(header). + andDo(print()). + andExpect(expectedStatus). + andReturn(); + if (result.getResponse().getStatus() == HttpStatus.OK.value()) { + locationUri = result.getResponse().getHeader("Location"); + } + } + + } + return locationUri; + } + private void testForNextVersion(String first, String second) { int index = first.lastIndexOf("="); int firstVersion = Integer.parseInt(first.substring(index + 1)); From db66c180215f9719cfa4992d6d40eb789cc9866b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Mar 2024 12:51:30 +0100 Subject: [PATCH 021/181] Old tests are running again. --- .../metastore2/dao/ISchemaRecordDao.java | 1 + .../metastore2/domain/SchemaRecord.java | 10 +- .../runner/ElasticIndexerRunner.java | 4 +- .../util/DataResourceRecordUtil.java | 4 +- .../util/MetadataSchemaRecordUtil.java | 18 +- .../metastore2/dao/ISchemaRecordDaoTest.java | 287 ++++++++++++++++++ .../JsonSchemaRegistryControllerTest.java | 3 +- .../test/SchemaRegistryControllerTest.java | 6 +- .../test/SchemaRegistryControllerTestV2.java | 2 +- 9 files changed, 316 insertions(+), 19 deletions(-) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java index 6c1b288c..19bb1d4d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java @@ -28,6 +28,7 @@ public interface ISchemaRecordDao extends JpaRepository<SchemaRecord, String>, JpaSpecificationExecutor<SchemaRecord> { boolean existsSchemaRecordBySchemaIdAndVersion(String schemaId, Long version); + boolean existsSchemaRecordBySchemaIdStartsWithAndVersion(String schemaId, Long version); SchemaRecord findBySchemaId(String schemaIdWithVersion); diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index b3587a4e..d7de54aa 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -58,10 +58,18 @@ public class SchemaRecord implements Serializable { // @NotBlank(message = "Alternate id of schema document.") private String alternateId; - public String getSchemaId() { + public String getSchemaIdWithoutVersion() { String pureSchemaId = null; if (schemaId != null) { + String split[] = schemaId.split("/"); pureSchemaId = schemaId.split("/")[0]; + if (split.length < 2) { + System.out.println("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"); + System.out.println(schemaId + "-> '" + split[0] + "', 'null'"); + } else { + System.out.println(schemaId + "-> '" + split[0] + "', '" + split[1] + "'"); + + } } return pureSchemaId; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index a453b10b..47130bd0 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -116,7 +116,7 @@ public void run(String... args) throws Exception { List<SchemaRecord> allSchemas = schemaRecordDao.findAll(PageRequest.of((int) page, (int) entriesPerPage)).getContent(); LOG.trace("Add '{}' schemas of '{}'", allSchemas.size(), noOfEntries); for (SchemaRecord item : allSchemas) { - indices.add(item.getSchemaId()); + indices.add(item.getSchemaIdWithoutVersion()); } page++; } while (page * entriesPerPage < noOfEntries); @@ -137,7 +137,7 @@ public void run(String... args) throws Exception { LOG.trace("Search for alternative schemaId (given as URL)"); DataRecord templateRecord = new DataRecord(); for (SchemaRecord debug : findSchemaBySchemaId) { - templateRecord.setSchemaId(debug.getSchemaId()); + templateRecord.setSchemaId(debug.getSchemaIdWithoutVersion()); templateRecord.setSchemaVersion(debug.getVersion()); List<Url2Path> findByPath1 = url2PathDao.findByPath(debug.getSchemaDocumentUri()); for (Url2Path path : findByPath1) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 53e20ff5..6c32c5a3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -207,7 +207,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio if (MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType())) { try { MetadataFormat metadataFormat = new MetadataFormat(); - metadataFormat.setMetadataPrefix(schemaRecord.getSchemaId()); + metadataFormat.setMetadataPrefix(schemaRecord.getSchemaIdWithoutVersion()); metadataFormat.setSchema(schemaRecord.getAlternateId()); String documentString = new String(document.getBytes()); LOG.trace(documentString); @@ -2065,7 +2065,7 @@ public static final SchemaRecord createSchemaRecord(DataResource dataResource, C schemaRecord.setType(XML); } else { - throw new BadArgumentException("Please provide a valid resource type for data resource '" + schemaRecord.getSchemaId() + "'!\n" + throw new BadArgumentException("Please provide a valid resource type for data resource '" + schemaRecord.getSchemaIdWithoutVersion() + "'!\n" + "One of ['" + JSON + SCHEMA_SUFFIX + "', '" + XML + SCHEMA_SUFFIX + "']"); } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 39ebef8e..9ca7f462 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -202,8 +202,8 @@ public static MetadataSchemaRecord createMetadataSchemaRecord(MetastoreConfigura if (MetadataSchemaRecord.SCHEMA_TYPE.XML.equals(schemaRecord.getType())) { try { MetadataFormat metadataFormat = new MetadataFormat(); - metadataFormat.setMetadataPrefix(schemaRecord.getSchemaId()); - metadataFormat.setSchema(getSchemaDocumentById.apply(schemaRecord.getSchemaId(), schemaRecord.getVersion())); + metadataFormat.setMetadataPrefix(schemaRecord.getSchemaIdWithoutVersion()); + metadataFormat.setSchema(getSchemaDocumentById.apply(schemaRecord.getSchemaIdWithoutVersion(), schemaRecord.getVersion())); String metadataNamespace = SchemaUtils.getTargetNamespaceFromSchema(document.getBytes()); metadataFormat.setMetadataNamespace(metadataNamespace); metadataFormatDao.save(metadataFormat); @@ -354,7 +354,7 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(id + "/"); // Test for linked metadata documents for (SchemaRecord item : listOfSchemaIds) { - List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaId(item.getSchemaId()); + List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaId(item.getSchemaIdWithoutVersion()); if (!findBySchemaId.isEmpty()) { throw new ResponseStatusException(HttpStatus.CONFLICT, "Conflict with existing metadata documents."); } @@ -710,7 +710,7 @@ private static void mergeSchemaRecord(SchemaRecord oldRecord, MetadataSchemaReco Objects.requireNonNull(newSettings); oldRecord.setDocumentHash(newSettings.getSchemaHash()); oldRecord.setSchemaDocumentUri(newSettings.getSchemaDocumentUri()); - oldRecord.setSchemaId(newSettings.getSchemaId()); +// oldRecord.setSchemaId(newSettings.getSchemaId() + "/" + newSettings.getSchemaVersion()); oldRecord.setVersion(newSettings.getSchemaVersion()); oldRecord.setType(newSettings.getType()); } @@ -895,7 +895,7 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp Path schemaDocumentPath = Paths.get(URI.create(schemaRecord.getSchemaDocumentUri())); if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { - LOG.error("Schema document with schemaId '{}'at path {} either does not exist or is no file or is not readable.", schemaRecord.getSchemaId(), schemaDocumentPath); + LOG.error("Schema document with schemaId '{}'at path {} either does not exist or is no file or is not readable.", schemaRecord.getSchemaIdWithoutVersion(), schemaDocumentPath); throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); } LOG.trace("obtain validator for type"); @@ -915,7 +915,7 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp } else { LOG.trace("Validator found."); - LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); + LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaIdWithoutVersion(), schemaRecord.getVersion(), applicableValidator); long nano4 = System.nanoTime() / 1000000; if (!applicableValidator.validateMetadataDocument(schemaDocumentPath.toFile(), inputStream)) { LOG.warn("Metadata document validation failed. -> " + applicableValidator.getErrorMessage()); @@ -1038,7 +1038,7 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast throw new UnprocessableEntityException(message); } else { LOG.trace("Validator found. Checking provided schema file."); - LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); + LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaIdWithoutVersion(), schemaRecord.getVersion(), applicableValidator); try (InputStream inputStream = new ByteArrayInputStream(document)) { if (!applicableValidator.isSchemaValid(inputStream)) { String message = "Metadata schema document validation failed. Returning HTTP UNPROCESSABLE_ENTITY."; @@ -1127,9 +1127,9 @@ private static SchemaRecord transformToSchemaRecord(MetadataSchemaRecord result) public static void saveNewSchemaRecord(SchemaRecord schemaRecord) { if (schemaRecordDao != null) { try { - schemaRecord.setAlternateId(DataResourceRecordUtil.getSchemaDocumentUri(schemaRecord.getSchemaId(), schemaRecord.getVersion())); + schemaRecord.setAlternateId(DataResourceRecordUtil.getSchemaDocumentUri(schemaRecord.getSchemaIdWithoutVersion(), schemaRecord.getVersion())); if (new StringTokenizer(schemaRecord.getSchemaId()).countTokens() < 2) { - schemaRecord.setSchemaId(schemaRecord.getSchemaId() + " " + schemaRecord.getVersion()); + schemaRecord.setSchemaId(schemaRecord.getSchemaId() + "/" + schemaRecord.getVersion()); } schemaRecordDao.save(schemaRecord); } catch (Exception npe) { diff --git a/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java b/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java new file mode 100644 index 00000000..f7103b9e --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java @@ -0,0 +1,287 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/UnitTests/JUnit4TestClass.java to edit this template + */ +package edu.kit.datamanager.metastore2.dao; + +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; + +/** + * + * @author hartmann-v + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41419"}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class ISchemaRecordDaoTest { + + @Autowired + ISchemaRecordDao schemaRecordDao; + + public ISchemaRecordDaoTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + prepareDataBase(); + } + + @After + public void tearDown() { + } + + /** + * Test of existsSchemaRecordBySchemaIdAndVersion method, of class + * ISchemaRecordDao. + */ + @Test + public void testExistsSchemaRecordBySchemaIdAndVersion() { + System.out.println("existsSchemaRecordBySchemaIdAndVersion"); + String schemaId = "schemaId"; + Long version = 1l; + ISchemaRecordDao instance = schemaRecordDao; + boolean expResult = false; + boolean result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); + assertEquals(expResult, result); + schemaId = "schemaId1"; + version = 4l; + result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); + assertEquals(expResult, result); + schemaId = "schemaId1"; + version = 3l; + result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); + assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. + schemaId = "schemaId1/3"; + version = 2l; + result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); + assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. + expResult = true; + schemaId = "schemaId1/3"; + version = 3l; + result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); + assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. + } + + /** + * Test of existsSchemaRecordBySchemaIdAndVersion method, of class + * ISchemaRecordDao. + */ + @Test + public void testExistsSchemaRecordBySchemaIdStartWithAndVersion() { + System.out.println("existsSchemaRecordBySchemaIdStartsWithAndVersion"); + String schemaId = "schemaId/"; + Long version = 1l; + ISchemaRecordDao instance = schemaRecordDao; + boolean expResult = false; + boolean result = instance.existsSchemaRecordBySchemaIdStartsWithAndVersion(schemaId, version); + assertEquals(expResult, result); + schemaId = "schemaId1"; + version = 4l; + result = instance.existsSchemaRecordBySchemaIdStartsWithAndVersion(schemaId, version); + expResult = true; + schemaId = "schemaId1"; + version = 3l; + result = instance.existsSchemaRecordBySchemaIdStartsWithAndVersion(schemaId, version); + assertEquals(expResult, result); + } + + /** + * Test of findBySchemaId method, of class ISchemaRecordDao. + */ + @Test + public void testFindBySchemaId() { + System.out.println("findBySchemaId"); + ISchemaRecordDao instance = schemaRecordDao; + String schemaIdWithVersion = "schemaId"; + SchemaRecord result = instance.findBySchemaId(schemaIdWithVersion); + assertNull(result); + schemaIdWithVersion = null; + result = instance.findBySchemaId(schemaIdWithVersion); + assertNull(result); + schemaIdWithVersion = "schemaId1/"; + result = instance.findBySchemaId(schemaIdWithVersion); + assertNull(result); + schemaIdWithVersion = "schemaId/1/1"; + result = instance.findBySchemaId(schemaIdWithVersion); + assertNull(result); + schemaIdWithVersion = "schemaId3/1"; + result = instance.findBySchemaId(schemaIdWithVersion); + assertNotNull(result); + System.out.println(result.toString()); + + } + + /** + * Test of findByAlternateId method, of class ISchemaRecordDao. + */ + @Test + public void testFindByAlternateId() { + System.out.println("findByAlternateId"); + ISchemaRecordDao instance = schemaRecordDao; + String alternateId = ""; + SchemaRecord result = instance.findByAlternateId(alternateId); + assertNull(result); + alternateId = "schemaId4"; + result = instance.findByAlternateId(alternateId); + assertNull(result); + alternateId = "1234"; + result = instance.findByAlternateId(alternateId); + assertNull(result); + alternateId = "documentUri2"; + result = instance.findByAlternateId(alternateId); + assertNull(result); + alternateId = "alternate"; + result = instance.findByAlternateId(alternateId); + assertNotNull(result); + } + + /** + * Test of findBySchemaIdStartsWithOrderByVersionDesc method, of class + * ISchemaRecordDao. + */ + @Test + public void testFindBySchemaIdStartsWithOrderByVersionDesc() { + System.out.println("findBySchemaIdStartsWithOrderByVersionDesc"); + ISchemaRecordDao instance = schemaRecordDao; + String schemaId = ""; + int expResult = 6; + List<SchemaRecord> result = instance.findBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertEquals(expResult, result.size()); + expResult = 0; + schemaId = "schemaId/"; + result = instance.findBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertEquals(expResult, result.size()); + schemaId = "documentUri4"; + result = instance.findBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertEquals(expResult, result.size()); + schemaId = "12345"; + result = instance.findBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertEquals(expResult, result.size()); + schemaId = "alter"; + result = instance.findBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertEquals(expResult, result.size()); + expResult = 1; + schemaId = "schemaId3"; + result = instance.findBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertEquals(expResult, result.size()); + expResult = 3; + schemaId = "schemaId1"; + result = instance.findBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertEquals(expResult, result.size()); + } + + /** + * Test of findFirstBySchemaIdStartsWithOrderByVersionDesc method, of class + * ISchemaRecordDao. + */ + @Test + public void testFindFirstBySchemaIdStartsWithOrderByVersionDesc() { + System.out.println("findFirstBySchemaIdStartsWithOrderByVersionDesc"); + ISchemaRecordDao instance = schemaRecordDao; + String schemaId = null; + SchemaRecord expResult = null; + SchemaRecord result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNull(result); + schemaId = ""; + int expectedVersion = 0; + result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNotNull(result); + schemaId = "schemaId/"; + result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNull(result); + schemaId = "documentUri4"; + result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNull(result); + schemaId = "12345"; + result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNull(result); + schemaId = "alter"; + result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNull(result); + expectedVersion = 1; + schemaId = "schemaId3"; + result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNotNull(result); + assertEquals(expectedVersion, result.getVersion().intValue()); + expectedVersion = 3; + schemaId = "schemaId1"; + result = instance.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId); + assertNotNull(result); + assertEquals(expectedVersion, result.getVersion().intValue()); + } + + private void prepareDataBase() { + String[][] datasets = { + {"schemaId1", "3", "documentUri13", "123", "alter"}, + {"schemaId1", "2", "documentUri12", "1234", "alterna"}, + {"schemaId1", "1", "documentUri11", "12345", "alternat"}, + {"schemaId2", "1", "documentUri2", "123456", "alternate"}, + {"schemaId3", "1", "documentUri3", "1234567", "alternateI"}, + {"schemaId4", "1", "documentUri4", "12345678", "alternateId"}}; + schemaRecordDao.deleteAll(); + for (String[] dataset : datasets) { + saveSchemaRecord(dataset[0], dataset[1], dataset[2], dataset[3], dataset[4]); + } + } + + private void saveSchemaRecord(String schemaId, + String version, + String schemaDocumentUri, + String documentHash, + String alternateId) { + + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setSchemaId(schemaId + "/" + version); + schemaRecord.setVersion(Long.parseLong(version)); + schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); + schemaRecord.setSchemaDocumentUri(schemaDocumentUri); + schemaRecord.setDocumentHash(documentHash); + schemaRecord.setAlternateId(alternateId); + schemaRecordDao.save(schemaRecord); + } + +} diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java index ec340cf0..7d71a128 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java @@ -11,6 +11,7 @@ import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtilTest; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; @@ -1096,7 +1097,7 @@ private void ingestSchemaRecord() throws Exception { contentInformationDao.save(ci); SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(dataResource.getId()); + schemaRecord.setSchemaId(dataResource.getId() + "/1"); schemaRecord.setVersion(1l); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); schemaRecord.setSchemaDocumentUri(ci.getContentUri()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index e6aaebc5..986dd410 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -1167,9 +1167,9 @@ public void testUpdateOnlyDocument() throws Exception { @Test public void testUpdateRecordWithSmallChangesInDocument() throws Exception { String schemaId = "updateRecordWithSmallChanges".toLowerCase(Locale.getDefault()); - SchemaRecord schemaRecord = new SchemaRecord(); + MetadataSchemaRecord schemaRecord = new MetadataSchemaRecord(); schemaRecord.setSchemaId(schemaId); - schemaRecord.setVersion(1l); + schemaRecord.setSchemaVersion(1l); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); ObjectMapper mapper = new ObjectMapper(); @@ -1693,7 +1693,7 @@ private void ingestSchemaRecord() throws Exception { ci = contentInformationDao.save(ci); SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(dataResource.getId()); + schemaRecord.setSchemaId(dataResource.getId() + "/1"); schemaRecord.setVersion(1l); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.valueOf(dataResource.getFormats().iterator().next())); schemaRecord.setSchemaDocumentUri(ci.getContentUri()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 76b81b4f..4e91a52a 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1797,7 +1797,7 @@ private void ingestSchemaRecord() throws Exception { ci = contentInformationDao.save(ci); SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaId(dataResource.getId()); + schemaRecord.setSchemaId(dataResource.getId() + "/1"); schemaRecord.setVersion(1l); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); schemaRecord.setSchemaDocumentUri(ci.getContentUri()); From 030ffb687498aaa9e8b475bdaf37ea7bd57dcc9d Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Mar 2024 17:50:53 +0100 Subject: [PATCH 022/181] Fix tests for v2. --- .../util/DataResourceRecordUtil.java | 10 ++-- .../metastore2/test/CreateSchemaUtil.java | 2 +- .../test/MetadataControllerTestV2.java | 55 +++++++++++-------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 6c32c5a3..6d0a2b45 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -440,10 +440,11 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur // Everything seems to be fine update document and increment version LOG.trace("Updating schema document (and increment version)..."); String version = dataResource.getVersion(); - if (version != null) { - updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + if (version == null) { + version = "0"; } - ContentDataUtils.addFile(applicationProperties, dataResource, document, fileName, null, true, supplier); + updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + ContentDataUtils.addFile(applicationProperties, updatedDataResource, document, fileName, null, true, supplier); } } else { @@ -461,8 +462,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur try { InputStream inputStream = Files.newInputStream(metadataDocumentPath); - ResourceIdentifier schema = ResourceIdentifier.factoryInternalResourceIdentifier(DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue()); - SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecord(schema, Long.parseLong(metadataRecord.getVersion())); + SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecordFromDataResource(dataResource); MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); } catch (IOException ex) { LOG.error("Error validating file!", ex); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java index 56cbbabc..b74a591c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java @@ -427,7 +427,7 @@ public static String ingestOrUpdateXmlSchemaRecordV2(MockMvc mockMvc, String sch schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", schemaContent.getBytes()); // Test if schema is already registered. MvcResult result = mockMvc.perform(get("/api/v2/schemas/" + schemaId). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andReturn(); if (result.getResponse().getStatus() != HttpStatus.OK.value()) { diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index ea1bf7ad..336269b8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -1372,9 +1372,10 @@ public void testUpdateRecordIgnoreACL() throws Exception { DataResource oldRecord = mapper.readValue(body, DataResource.class); DataResource record = mapper.readValue(body, DataResource.class); // Set all ACL to WRITE - for (AclEntry entry : record.getAcls()) { - entry.setPermission(PERMISSION.WRITE); - } + record.setAcls(null); +// for (AclEntry entry : record.getAcls()) { +// entry.setPermission(PERMISSION.WRITE); +// } MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). @@ -1392,17 +1393,21 @@ public void testUpdateRecordIgnoreACL() throws Exception { Assert.assertEquals(Long.parseLong(oldRecord.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(oldRecord.getAcls(), record2.getAcls()); Assert.assertTrue(oldRecord.getLastUpdate().isBefore(record2.getLastUpdate())); - // Check for new metadata document. - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); String locationUri2 = result.getResponse().getHeader("Location"); + + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; Assert.assertEquals(dcMetadata, content); - - Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); - } + } @Test public void testUpdateRecordWithoutExplizitGet() throws Exception { @@ -1739,13 +1744,13 @@ public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exce @Test public void testUpdateRecordWithInvalidSetting4Json() throws Exception { String alternativeSchemaId = "testupdate"; - CreateSchemaUtil.ingestXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V1, schemaConfig.getJwtSecret()); - CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V2, schemaConfig.getJwtSecret(), true, status().isOk()); - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, alternativeSchemaId, 2l, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlSchemaRecordV2(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V1, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestOrUpdateXmlSchemaRecordV2(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V2, schemaConfig.getJwtSecret(), true, status().isOk()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 2l, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); // Change only version of schema to a version which is not valid. CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); // Change to a nonexistent version of schema. - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isNotFound()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isBadRequest()); // Change to another schema CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); } @@ -1763,12 +1768,14 @@ public void testUpdateRecordWithLicense() throws Exception { ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); - record.getRights().clear(); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertTrue(record.getRights().isEmpty()); + record2.getRights().clear(); Scheme apache = new Scheme(); apache.setSchemeId("Apache-2.0"); apache.setSchemeUri(APACHE_2_LICENSE); - record.getRights().add(apache); - MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + record2.getRights().add(apache); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). @@ -1784,26 +1791,30 @@ public void testUpdateRecordWithLicense() throws Exception { body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); - DataResource record2 = mapper.readValue(body, DataResource.class); + record2 = mapper.readValue(body, DataResource.class); // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - Assert.assertNull("No license available", record.getRights()); + Assert.assertTrue("No license available", record.getRights().isEmpty()); Assert.assertNotNull(record2.getRights()); Assert.assertTrue(!record2.getRights().isEmpty()); - Assert.assertTrue(!record2.getRights().iterator().next().getSchemeUri().equals(APACHE_2_LICENSE)); + Assert.assertTrue(record2.getRights().iterator().next().getSchemeUri().equals(APACHE_2_LICENSE)); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)). andDo(print()). andExpect(status().isOk()). andReturn(); String locationUri2 = result.getResponse().getHeader("Location"); - String content = result.getResponse().getContentAsString(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; @@ -1820,7 +1831,7 @@ public void testUpdateRecordWithLicense() throws Exception { mapper = new ObjectMapper(); record = mapper.readValue(body, DataResource.class); - record.setRights(null); + record.getRights().clear(); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). @@ -1841,7 +1852,7 @@ record = mapper.readValue(body, DataResource.class); Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()));// version should be the same SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); - Assert.assertNull("Set of rights should be 'null'", record3.getRights()); + Assert.assertTrue("Set of rights should be 'empty'", record3.getRights().isEmpty()); } From 02971386224f56ca1c3936fa11bed13331ca952e Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 26 Mar 2024 13:18:03 +0100 Subject: [PATCH 023/181] Fix more tests... --- .../util/DataResourceRecordUtil.java | 44 ++-- .../web/ILandingPageControllerV2.java | 70 +++++++ .../web/impl/LandingPageControllerImplV2.java | 144 +++++++++++++ .../web/impl/MetadataControllerImplV2.java | 7 +- .../templates/metadata-landing-page-v2.html | 144 +++++++++++++ .../metastore2/test/CreateSchemaUtil.java | 67 +++++- .../test/MetadataControllerTestV2.java | 198 ++++++++++++++++-- .../test/SchemaRegistryControllerTestV2.java | 23 +- 8 files changed, 639 insertions(+), 58 deletions(-) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java create mode 100644 src/main/resources/templates/metadata-landing-page-v2.html diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 6d0a2b45..a5c08e01 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -118,7 +118,7 @@ public class DataResourceRecordUtil { /** * Separator for separating schemaId and schemaVersion. */ - public static final String SCHEMA_VERSION_SEPARATOR = ":"; + public static final String SCHEMA_VERSION_SEPARATOR = "/"; /** * Logger for messages. */ @@ -399,6 +399,9 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { updatedDataResource.setAcls(dataResource.getAcls()); } + if (updatedDataResource.getRights() == null) { + updatedDataResource.setRights(new HashSet<>()); + } } else { updatedDataResource = DataResourceUtils.copyDataResource(dataResource); } @@ -459,10 +462,10 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); } - + // test if document is still valid for updated(?) schema. try { InputStream inputStream = Files.newInputStream(metadataDocumentPath); - SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecordFromDataResource(dataResource); + SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecordFromDataResource(updatedDataResource); MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); } catch (IOException ex) { LOG.error("Error validating file!", ex); @@ -514,7 +517,7 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application // No references to this schema available -> Ready for deletion if (findOne.isEmpty()) { DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); - List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(id + "/"); + List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(id + SCHEMA_VERSION_SEPARATOR); for (SchemaRecord item : listOfSchemaIds) { LOG.trace("Delete entry for path '{}'", item.getSchemaDocumentUri()); List<Url2Path> findByPath = url2PathDao.findByPath(item.getSchemaDocumentUri()); @@ -1055,7 +1058,7 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou List<String> allSchemaIds = new ArrayList<>(); for (String schemaId : schemaIds) { allSchemaIds.add(schemaId); - List<SchemaRecord> allVersions = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); + List<SchemaRecord> allVersions = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); for (SchemaRecord schemaRecord : allVersions) { allSchemaIds.add(schemaRecord.getAlternateId()); } @@ -1328,7 +1331,7 @@ public static final void fixSchemaUrl(DataResource dataresource) { RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataresource); if ((schemaIdentifier != null) && (schemaIdentifier.getIdentifierType().equals(Identifier.IDENTIFIER_TYPE.INTERNAL))) { String value = schemaIdentifier.getValue(); - StringTokenizer tokenizer = new StringTokenizer(schemaIdentifier.getValue()); + StringTokenizer tokenizer = new StringTokenizer(schemaIdentifier.getValue(), SCHEMA_VERSION_SEPARATOR); Long version = null; String schemaId = null; SchemaRecord schemaRecord = null; @@ -1336,11 +1339,11 @@ public static final void fixSchemaUrl(DataResource dataresource) { case 2: schemaId = tokenizer.nextToken(); version = Long.parseLong(tokenizer.nextToken()); - schemaRecord = schemaRecordDao.findBySchemaId(schemaId + "/" + version); + schemaRecord = schemaRecordDao.findBySchemaId(schemaId + SCHEMA_VERSION_SEPARATOR + version); break; case 1: schemaId = tokenizer.nextToken(); - schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); break; default: throw new CustomInternalServerError("Invalid schemaId!"); @@ -1355,7 +1358,7 @@ public static final void fixSchemaUrl(DataResource dataresource) { public static void checkLicense(DataResource dataResource, String licenseUri) { if (licenseUri != null) { Set<Scheme> rights = dataResource.getRights(); - String licenseId = licenseUri.substring(licenseUri.lastIndexOf("/")); + String licenseId = licenseUri.substring(licenseUri.lastIndexOf(SCHEMA_VERSION_SEPARATOR)); Scheme license = Scheme.factoryScheme(licenseId, licenseUri); if (rights.isEmpty()) { rights.add(license); @@ -1678,9 +1681,9 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp } schemaId = dataResource.getId(); if (version != null) { - schemaRecord = schemaRecordDao.findBySchemaId(schemaId + "/" + version); + schemaRecord = schemaRecordDao.findBySchemaId(schemaId + SCHEMA_VERSION_SEPARATOR + version); } else { - schemaRecord = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/").get(0); + schemaRecord = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR).get(0); } if (schemaRecord == null) { String message = "Unknown version '" + version + "' for schemaID '" + schemaId + "'!"; @@ -1802,9 +1805,9 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v throw new BadArgumentException(message); } if (version != null) { - schemaRecord = schemaRecordDao.findBySchemaId(schemaId + "/" + version); + schemaRecord = schemaRecordDao.findBySchemaId(schemaId + SCHEMA_VERSION_SEPARATOR + version); } else { - schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); } } case URL -> { @@ -1830,9 +1833,9 @@ private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataRes schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); break; case INTERNAL: - String[] split = schemaId.split("/"); + String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); if (split.length == 1) { - schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + "/"); + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); } else { schemaRecord = schemaRecordDao.findBySchemaId(schemaId); } @@ -2022,11 +2025,18 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro StringBuilder errorMessage = new StringBuilder(); RelatedIdentifier schemaIdentifier = getSchemaIdentifier(metadataRecord); SchemaRecord findByAlternateId; - if (schemaIdentifier != null) { + if ((schemaIdentifier != null) && (schemaIdentifier.getValue() != null)) { if (schemaIdentifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { findByAlternateId = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); } else { - findByAlternateId = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaIdentifier.getValue() + "/"); + String schemaId = schemaIdentifier.getValue(); + String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); + + if (split.length > 1) { + findByAlternateId = schemaRecordDao.findBySchemaId(schemaId); + } else { + findByAlternateId = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(split[0] + SCHEMA_VERSION_SEPARATOR); + } } if (findByAlternateId != null) { try { diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java new file mode 100644 index 00000000..02849fef --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java @@ -0,0 +1,70 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web; + +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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 jakarta.servlet.http.HttpServletResponse; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.context.request.WebRequest; + +/** + * Interface for metadata documents controller. + */ +@ApiResponses(value = { + @ApiResponse(responseCode = "401", description = "Unauthorized is returned if authorization is required but was not provided."), + @ApiResponse(responseCode = "403", description = "Forbidden is returned if the caller has no sufficient privileges.")}) +public interface ILandingPageControllerV2 { + + @Operation(summary = "Get landing page of schema by schema id (and version).", description = "Show landing page by its schema id. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + + "Furthermore, a specific version of the schema can be returned by providing a version number as request parameter. If no version is specified, all versions will be returned.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the landingpage is returned if the id exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) + @RequestMapping(value = {"/schema-landing-page"}, method = {RequestMethod.GET}, produces = {"text/html"}) + public String getLandingPageOfSchemaWithId( + @Parameter(description = "The record identifier or schema identifier.", required = true) @RequestParam(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr, + Model model); + + @Operation(summary = "Get a landing page by id.", description = "Obtain a single record by its resource identifier. " + + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " + + "by providing a version number as request parameter.", + responses = { + @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataRecord.class))), + @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) + + @RequestMapping(value = {"/metadata-landing-page"}, method = {RequestMethod.GET}, produces = {"text/html"}) + public String getLandingPageOfMetadataDocumentWithId( + @Parameter(description = "The identifier of the metadata document.", required = true) @RequestParam(value = "id") String id, + @Parameter(description = "The version of the digital object. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr, + Model model); +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java new file mode 100644 index 00000000..550adb47 --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java @@ -0,0 +1,144 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web.impl; + +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; +import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; +import edu.kit.datamanager.metastore2.web.ILandingPageController; +import edu.kit.datamanager.repo.domain.DataResource; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.context.request.WebRequest; + +/** + * Controller for metadata documents. + */ +@Controller +@RequestMapping(value = "") +@Tag(name = "Landing Page") +@Schema(description = "Landing page for all digital objects stored in this repo.") +public class LandingPageControllerImplV2 implements ILandingPageControllerV2 { + + private static final Logger LOG = LoggerFactory.getLogger(LandingPageControllerImpl.class); + + private final MetastoreConfiguration metadataConfig; + + private final MetastoreConfiguration schemaConfig; + + /** + * Constructor for metadata documents controller. + * + * @param applicationProperties Configuration for controller. + * @param metadataConfig Configuration for metadata documents repository. + * @param schemaConfig Configuration for schema documents repository. + */ + public LandingPageControllerImpl(ApplicationProperties applicationProperties, + MetastoreConfiguration metadataConfig, + MetastoreConfiguration schemaConfig) { + this.metadataConfig = metadataConfig; + this.schemaConfig = schemaConfig; + LOG.info("------------------------------------------------------"); + LOG.info("------{}", this.metadataConfig); + LOG.info("------------------------------------------------------"); + } + + @Override + public String getLandingPageOfSchemaWithId(@RequestParam(value = "schemaId") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr, + Model model) { + LOG.trace("Performing getLandingPageOfSchemaWithId({}, {}).", id, version); + + //if security is enabled, include principal in query + LOG.debug("Performing a query for records with given id."); + MetadataSchemaRecord recordByIdAndVersion = MetadataSchemaRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); + List<MetadataSchemaRecord> recordList = new ArrayList<>(); + recordList.add(recordByIdAndVersion); + if (version == null) { + long totalNoOfElements = recordByIdAndVersion.getSchemaVersion(); + for (long size = totalNoOfElements - 1; size > 0; size--) { + recordList.add(MetadataSchemaRecordUtil.getRecordByIdAndVersion(schemaConfig, id, size)); + } + } + + LOG.trace("Fix URL for all schema records"); + List<MetadataSchemaRecord> metadataList = new ArrayList<>(); + recordList.forEach(metadataRecord -> { + MetadataSchemaRecordUtil.fixSchemaDocumentUri(metadataRecord); + metadataList.add(metadataRecord); + }); + + model.addAttribute("records", metadataList); + + return "schema-landing-page.html"; + } + + @Override + public String getLandingPageOfMetadataDocumentWithId(@PathVariable(value = "id") String id, + @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr, + Model model + ) { + + LOG.trace("Performing getLandingPageOfMetadataDocumentWithId({}, {}).", id, version); + + //if security is enabled, include principal in query + LOG.debug("Performing a query for all records with given id..."); + DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + List<MetadataRecord> recordList = new ArrayList<>(); + + recordList.add(recordByIdAndVersion); + if (version == null) { + long totalNoOfElements = Long.parseLong(recordByIdAndVersion.getVersion()); + for (long size = totalNoOfElements - 1; size > 0; size--) { + recordList.add(MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, size)); + } + } + + LOG.trace("Fix URL for all metadata records"); + List<MetadataRecord> metadataList = new ArrayList<>(); + recordList.forEach(metadataRecord -> { + MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); + metadataList.add(metadataRecord); + }); + + SchemaRecord schemaRecord = MetadataSchemaRecordUtil.getSchemaRecord(metadataList.get(0).getSchema(), metadataList.get(0).getSchemaVersion()); + + model.addAttribute("type", schemaRecord.getType()); + model.addAttribute("records", metadataList); + + return "metadata-landing-page-v2.html"; + } + +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 984bf98c..41624785 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -375,12 +375,9 @@ public ResponseEntity<List<DataResource>> getAllVersions( recordList.add(DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version)); } - LOG.trace("Transforming Dataresource to DataResource"); - List<DataResource> metadataList = new ArrayList<>(); - String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); - return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(metadataList); + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); } @Override @@ -395,7 +392,7 @@ public ResponseEntity<List<DataResource>> getRecords( HttpServletResponse hsr, UriComponentsBuilder ucb ) { - LOG.trace("Performing getRecords({}, {}, {}, {}).", relatedIds, schemaIds, updateFrom, updateUntil); + LOG.trace("Performing getRecords({}, {}, {}, {}, {}).", id, relatedIds, schemaIds, updateFrom, updateUntil); if (id != null) { return getAllVersions(id, pgbl); } diff --git a/src/main/resources/templates/metadata-landing-page-v2.html b/src/main/resources/templates/metadata-landing-page-v2.html new file mode 100644 index 00000000..ac0df88e --- /dev/null +++ b/src/main/resources/templates/metadata-landing-page-v2.html @@ -0,0 +1,144 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org" lang="en"> + <head> + <meta charset="utf-8" /> + <meta content="width=device-width, initial-scale=1.0" name="viewport" /> + <title>Landingpage for Metadata document + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + + + +
+ +
+
+

Entries

+
+
+ + + + + + + + + + + + + +
ID VersionTypeLast Update Related ResourceMetadataSchema
............ + + + +
+
+
+
+
+ + diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java index b74a591c..96a53045 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java @@ -408,6 +408,38 @@ public static String ingestXmlSchemaRecordV2(MockMvc mockMvc, String schemaId, S * @throws Exception */ public static String ingestOrUpdateXmlSchemaRecordV2(MockMvc mockMvc, String schemaId, String schemaContent, String jwtSecret, boolean update, ResultMatcher expectedStatus) throws Exception { + return ingestOrUpdateSchemaRecordV2(mockMvc, MediaType.APPLICATION_XML, schemaId, schemaContent, jwtSecret, update, expectedStatus); + } + + /** + * Update schema in MetaStore as user 'test_user'. If schema already exists + * and noUpdate is false update schema. + * + * @param mockMvc + * @param schemaId + * @param schemaContent + * @param jwtSecret + * @param noUpdate Only ingest or do update also + * @return + * @throws Exception + */ + public static String ingestOrUpdateJsonSchemaRecordV2(MockMvc mockMvc, String schemaId, String schemaContent, String jwtSecret, boolean update, ResultMatcher expectedStatus) throws Exception { + return ingestOrUpdateSchemaRecordV2(mockMvc, MediaType.APPLICATION_JSON, schemaId, schemaContent, jwtSecret, update, expectedStatus); + } + /** + * Update schema in MetaStore as user 'test_user'. If schema already exists + * and noUpdate is false update schema. + * + * @param mockMvc + * @param mediaType + * @param schemaId + * @param schemaContent + * @param jwtSecret + * @param noUpdate Only ingest or do update also + * @return + * @throws Exception + */ + public static String ingestOrUpdateSchemaRecordV2(MockMvc mockMvc, MediaType mediaType, String schemaId, String schemaContent, String jwtSecret, boolean update, ResultMatcher expectedStatus) throws Exception { String locationUri = null; jwtSecret = (jwtSecret == null) ? "jwtSecret" : jwtSecret; userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(otherUserPrincipal, RepoUserRole.USER). @@ -416,9 +448,14 @@ public static String ingestOrUpdateXmlSchemaRecordV2(MockMvc mockMvc, String sch addSimpleClaim("loginFailures", 0). addSimpleClaim("active", true). addSimpleClaim("locked", false).getCompactToken(jwtSecret); - DataResource record = SchemaRegistryControllerTestV2.createDataResource4Schema(schemaId); + DataResource record; + if (mediaType.toString().contains("xml")) { + record = SchemaRegistryControllerTestV2.createDataResource4XmlSchema(schemaId); + } else { + record = SchemaRegistryControllerTestV2.createDataResource4JsonSchema(schemaId); + } record.getAcls().add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); - + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile; @@ -479,10 +516,14 @@ public static MvcResult ingestOrUpdateXmlMetadataDocumentV2(MockMvc mockMvc, Str // Test if metadataId is already registered. MvcResult result = null; - - DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(metadataId, schemaId); + String versionAsString = null; if (version != null) { - record.setVersion(version.toString()); + versionAsString = version.toString(); + } + + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(metadataId, schemaId, versionAsString); + if (versionAsString != null) { + record.setVersion(versionAsString); } RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); relatedIdentifier.setValue("any"); @@ -518,10 +559,18 @@ public static MvcResult ingestOrUpdateXmlMetadataDocumentV2(MockMvc mockMvc, Str String body = result.getResponse().getContentAsString(); record = mapper.readValue(body, DataResource.class); relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); - relatedIdentifier.setValue(schemaId); - relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); - if (version != null) { - record.setVersion(version.toString()); + if ((schemaId != null) && schemaId.startsWith("http")) { + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + } else { + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + if (versionAsString != null) { + relatedIdentifier.setValue(schemaId + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + versionAsString); + } else { + relatedIdentifier.setValue(schemaId); + } + } + if (versionAsString != null) { + record.setVersion(versionAsString); } recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); // Update metadata document diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 336269b8..66a78483 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -1324,6 +1324,7 @@ public void testUpdateRecordWithWrongVersion() throws Exception { result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). file(recordFile). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); @@ -1341,17 +1342,26 @@ public void testUpdateRecordWithWrongVersion() throws Exception { SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Check for new metadata document. - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)).andDo(print()).andExpect(status().isOk()).andReturn(); - String locationUri = result.getResponse().getHeader("Location"); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; Assert.assertEquals(dcMetadata, content); - // Check for old metadata document. + // Check for old location URI. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); - String locationUri2 = result.getResponse().getHeader("Location"); + String locationUri = result.getResponse().getHeader("Location"); + // Check for old metadata document + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1"). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); content = result.getResponse().getContentAsString(); dcMetadata = DC_DOCUMENT; @@ -1407,7 +1417,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { String dcMetadata = DC_DOCUMENT_VERSION_2; Assert.assertEquals(dcMetadata, content); - } + } @Test public void testUpdateRecordWithoutExplizitGet() throws Exception { @@ -1429,7 +1439,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); - // Check ContentInformation of first version + // Check ContentInformation of first version result = this.mockMvc.perform(get(locationUri). accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). andDo(print()). @@ -1438,7 +1448,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String cibody = result.getResponse().getContentAsString(); ContentInformation contentInformation1 = mapper.readValue(cibody, ContentInformation.class); - + DataResource record2 = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); MockMultipartFile metadataFile2 = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); @@ -1556,6 +1566,7 @@ public void testUpdateRecordWithWrongETag() throws Exception { public void testUpdateRecordWithoutRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri = result.getResponse().getHeader("Location"); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1565,6 +1576,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); @@ -1573,16 +1585,17 @@ public void testUpdateRecordWithoutRecord() throws Exception { Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); } @Test public void testUpdateRecordWithoutRecord4Json() throws Exception { + String metadataRecordId = "testUpdateRecordWithoutRecord4Json"; // Update only Json document - ingestJsonSchemaRecord(); - String metadataRecordId = createJsonMetadataRecord(); - MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + CreateSchemaUtil.ingestOrUpdateJsonSchemaRecordV2(mockMvc, JSON_SCHEMA_ID, JSON_SCHEMA, schemaConfig.getJwtSecret(), false, status().isCreated()); + MvcResult result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, JSON_SCHEMA_ID, 1l, metadataRecordId, JSON_DOCUMENT_VERSION_1, schemaConfig.getJwtSecret(), false, status().isCreated()); + String locationUri = result.getResponse().getHeader("Location"); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1591,7 +1604,13 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", JSON_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). - file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); + file(metadataFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); @@ -1600,20 +1619,38 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); - Assert.assertEquals(record.getEtag().replace("version=1", "version=2"), record2.getEtag()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + param("version", "1"). + accept(MediaType.APPLICATION_JSON)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String content = result.getResponse().getContentAsString(); String jsonMetadata = JSON_DOCUMENT_VERSION_1; Assert.assertEquals(jsonMetadata, content); - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + param("version", "2"). + accept(MediaType.APPLICATION_JSON)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); content = result.getResponse().getContentAsString(); - jsonMetadata = JSON_DOCUMENT_VERSION_2; + Assert.assertEquals(jsonMetadata, content); + // Once more without explicit version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_JSON)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + content = result.getResponse().getContentAsString(); + Assert.assertEquals(jsonMetadata, content); } @@ -1630,10 +1667,10 @@ public void testUpdateRecordWithoutDocument() throws Exception { String body = result.getResponse().getContentAsString(); ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); - - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); String etag = result.getResponse().getHeader("ETag"); - body = result.getResponse().getContentAsString(); + body = result.getResponse().getContentAsString(); DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1814,7 +1851,7 @@ public void testUpdateRecordWithLicense() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - String content = result.getResponse().getContentAsString(); + String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT_VERSION_2; @@ -1842,6 +1879,107 @@ record = mapper.readValue(body, DataResource.class); andExpect(status().isOk()). andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + DataResource record3 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()));// version should be the same + SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); + Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + Assert.assertTrue("Set of rights should be 'empty'", record3.getRights().isEmpty()); + + } + + @Test + public void testUpdateRecordWithLicenseNull() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + DataResource record = mapper.readValue(body, DataResource.class); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertTrue(record.getRights().isEmpty()); + record2.getRights().clear(); + Scheme apache = new Scheme(); + apache.setSchemeId("Apache-2.0"); + apache.setSchemeUri(APACHE_2_LICENSE); + record2.getRights().add(apache); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + String locationUri = result.getResponse().getHeader("Location"); + + record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + + Assert.assertTrue("No license available", record.getRights().isEmpty()); + Assert.assertNotNull(record2.getRights()); + Assert.assertTrue(!record2.getRights().isEmpty()); + Assert.assertTrue(record2.getRights().iterator().next().getSchemeUri().equals(APACHE_2_LICENSE)); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + body = result.getResponse().getContentAsString(); + + mapper = new ObjectMapper(); + record = mapper.readValue(body, DataResource.class); + record.setRights(null); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); @@ -1930,7 +2068,11 @@ public void testGetAllVersionsOfRecord() throws Exception { DataResource record = mapper.readValue(body, DataResource.class); Assert.assertEquals("Expect current version '" + version + "'", (Long) version, Long.valueOf(record.getVersion()));// version should be 1 higher // Check for new metadata document. - result = this.mockMvc.perform(get(API_METADATA_PATH + id)).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + id). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT; @@ -2015,13 +2157,23 @@ public void testIssue52() throws Exception { result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + param("version", "1"). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); String content = result.getResponse().getContentAsString(); String dcMetadata = DC_DOCUMENT; // Assert.assertEquals(dcMetadata, content); - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isOk()).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + param("version", "2"). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); content = result.getResponse().getContentAsString(); Assert.assertNotEquals(dcMetadata, content); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 4e91a52a..77926a4b 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1909,18 +1909,30 @@ public static DataResource createDataResource4Schema(String id) { } public static DataResource createDataResource4JsonDocument(String id, String schemaId) { - return createDataResource4Document(id, schemaId, DataResourceRecordUtil.JSON_METADATA_TYPE); + return createDataResource4Document(id, schemaId, null, DataResourceRecordUtil.JSON_METADATA_TYPE); } public static DataResource createDataResource4XmlDocument(String id, String schemaId) { - return createDataResource4Document(id, schemaId, DataResourceRecordUtil.XML_METADATA_TYPE); + return createDataResource4Document(id, schemaId, null, DataResourceRecordUtil.XML_METADATA_TYPE); } public static DataResource createDataResource4Document(String id, String schemaId) { - return createDataResource4Document(id, schemaId, DataResourceRecordUtil.XML_METADATA_TYPE); + return createDataResource4Document(id, schemaId, null, DataResourceRecordUtil.XML_METADATA_TYPE); } - public static DataResource createDataResource4Document(String id, String schemaId, String metadataType) { + public static DataResource createDataResource4JsonDocument(String id, String schemaId, String version) { + return createDataResource4Document(id, schemaId, version, DataResourceRecordUtil.JSON_METADATA_TYPE); + } + + public static DataResource createDataResource4XmlDocument(String id, String schemaId, String version) { + return createDataResource4Document(id, schemaId, version, DataResourceRecordUtil.XML_METADATA_TYPE); + } + + public static DataResource createDataResource4Document(String id, String schemaId, String version) { + return createDataResource4Document(id, schemaId, version, DataResourceRecordUtil.XML_METADATA_TYPE); + } + + public static DataResource createDataResource4Document(String id, String schemaId, String version, String metadataType) { DataResource record = new DataResource(); record.setId(id); record.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(id)); @@ -1936,6 +1948,9 @@ public static DataResource createDataResource4Document(String id, String schemaI relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } else { relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + if (version != null) { + relatedResource.setValue(schemaId + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + version); + } } record.getRelatedIdentifiers().add(relatedResource); if (metadataType.contains("XML")) { From f225e4b3848ff5697088db7b209bf75b2effde65 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 2 Apr 2024 15:15:35 +0200 Subject: [PATCH 024/181] Only landing page fails. --- .../metastore2/domain/SchemaRecord.java | 7 - .../web/ILandingPageControllerV2.java | 4 +- .../web/impl/LandingPageControllerImplV2.java | 32 ++-- .../web/impl/MetadataSearchControllerV2.java | 154 ++++++++++++++++++ .../test/MetadataControllerTestV2.java | 9 +- 5 files changed, 173 insertions(+), 33 deletions(-) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index d7de54aa..1cea82d3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -63,13 +63,6 @@ public String getSchemaIdWithoutVersion() { if (schemaId != null) { String split[] = schemaId.split("/"); pureSchemaId = schemaId.split("/")[0]; - if (split.length < 2) { - System.out.println("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"); - System.out.println(schemaId + "-> '" + split[0] + "', 'null'"); - } else { - System.out.println(schemaId + "-> '" + split[0] + "', '" + split[1] + "'"); - - } } return pureSchemaId; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java index 02849fef..d794695c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java @@ -45,7 +45,7 @@ public interface ILandingPageControllerV2 { responses = { @ApiResponse(responseCode = "200", description = "OK and the landingpage is returned if the id exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) - @RequestMapping(value = {"/schema-landing-page"}, method = {RequestMethod.GET}, produces = {"text/html"}) + @RequestMapping(value = {"/schema-landing-page-v2"}, method = {RequestMethod.GET}, produces = {"text/html"}) public String getLandingPageOfSchemaWithId( @Parameter(description = "The record identifier or schema identifier.", required = true) @RequestParam(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, @@ -60,7 +60,7 @@ public String getLandingPageOfSchemaWithId( @ApiResponse(responseCode = "200", description = "OK and the record is returned if the record exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) - @RequestMapping(value = {"/metadata-landing-page"}, method = {RequestMethod.GET}, produces = {"text/html"}) + @RequestMapping(value = {"/metadata-landing-page-v2"}, method = {RequestMethod.GET}, produces = {"text/html"}) public String getLandingPageOfMetadataDocumentWithId( @Parameter(description = "The identifier of the metadata document.", required = true) @RequestParam(value = "id") String id, @Parameter(description = "The version of the digital object. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java index 550adb47..b36ba453 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java @@ -23,7 +23,7 @@ import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; -import edu.kit.datamanager.metastore2.web.ILandingPageController; +import edu.kit.datamanager.metastore2.web.ILandingPageControllerV2; import edu.kit.datamanager.repo.domain.DataResource; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; @@ -61,7 +61,7 @@ public class LandingPageControllerImplV2 implements ILandingPageControllerV2 { * @param metadataConfig Configuration for metadata documents repository. * @param schemaConfig Configuration for schema documents repository. */ - public LandingPageControllerImpl(ApplicationProperties applicationProperties, + public LandingPageControllerImplV2(ApplicationProperties applicationProperties, MetastoreConfiguration metadataConfig, MetastoreConfiguration schemaConfig) { this.metadataConfig = metadataConfig; @@ -81,20 +81,20 @@ public String getLandingPageOfSchemaWithId(@RequestParam(value = "schemaId") Str //if security is enabled, include principal in query LOG.debug("Performing a query for records with given id."); - MetadataSchemaRecord recordByIdAndVersion = MetadataSchemaRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); - List recordList = new ArrayList<>(); + DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); + List recordList = new ArrayList<>(); recordList.add(recordByIdAndVersion); if (version == null) { - long totalNoOfElements = recordByIdAndVersion.getSchemaVersion(); + long totalNoOfElements = Long.parseLong(recordByIdAndVersion.getVersion()); for (long size = totalNoOfElements - 1; size > 0; size--) { - recordList.add(MetadataSchemaRecordUtil.getRecordByIdAndVersion(schemaConfig, id, size)); + recordList.add(DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, size)); } } LOG.trace("Fix URL for all schema records"); - List metadataList = new ArrayList<>(); + List metadataList = new ArrayList<>(); recordList.forEach(metadataRecord -> { - MetadataSchemaRecordUtil.fixSchemaDocumentUri(metadataRecord); + DataResourceRecordUtil.fixSchemaUrl(metadataRecord); metadataList.add(metadataRecord); }); @@ -116,27 +116,19 @@ public String getLandingPageOfMetadataDocumentWithId(@PathVariable(value = "id") //if security is enabled, include principal in query LOG.debug("Performing a query for all records with given id..."); DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); - List recordList = new ArrayList<>(); + List recordList = new ArrayList<>(); recordList.add(recordByIdAndVersion); if (version == null) { long totalNoOfElements = Long.parseLong(recordByIdAndVersion.getVersion()); for (long size = totalNoOfElements - 1; size > 0; size--) { - recordList.add(MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, size)); + recordList.add(DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, size)); } } - LOG.trace("Fix URL for all metadata records"); - List metadataList = new ArrayList<>(); - recordList.forEach(metadataRecord -> { - MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); - metadataList.add(metadataRecord); - }); - - SchemaRecord schemaRecord = MetadataSchemaRecordUtil.getSchemaRecord(metadataList.get(0).getSchema(), metadataList.get(0).getSchemaVersion()); - model.addAttribute("type", schemaRecord.getType()); - model.addAttribute("records", metadataList); + model.addAttribute("type", recordList.get(0).getFormats().iterator().next()); + model.addAttribute("records", recordList); return "metadata-landing-page-v2.html"; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java new file mode 100644 index 00000000..e484e097 --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java @@ -0,0 +1,154 @@ +/* + * Copyright 2019 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.web.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.kit.datamanager.configuration.SearchConfiguration; +import edu.kit.datamanager.util.ElasticSearchUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springdoc.core.converters.models.PageableAsQueryParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.mvc.ProxyExchange; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * Controller for search entities. + */ +@Controller +@RequestMapping(value = "/api/v2/metadata") +@Tag(name = "Metadata Repository") +@Schema(description = "Metadata Resource Management") +public class MetadataSearchControllerV2 { + + private static final Logger LOG = LoggerFactory.getLogger(MetadataSearchControllerV2.class); + + private static final String SEARCH_PATH_POSTFIX = "/_search"; + + @Autowired + private SearchConfiguration searchConfiguration; + + /** + * Constructor with configuration. + * + * @param searchConfiguration Configuration for search. + */ + public MetadataSearchControllerV2(SearchConfiguration searchConfiguration) { + this.searchConfiguration = searchConfiguration; + LOG.info("------------------------------------------------------"); + LOG.info("------{}", this.searchConfiguration); + LOG.info("------------------------------------------------------"); + } + + @PostMapping("/{schemaId}/search") + @Operation(summary = "Search for metadata document/records", + description = "Search for metadata document/records using the configured Elastic backend. " + + "This endpoint serves as direct proxy to the RESTful endpoint of Elastic. " + + "In the body, a query document following the Elastic query format has to be provided. " + + "Format errors are returned directly from Elastic. " + + "This endpoint also supports authentication and authorization. " + + "User information obtained via JWT is applied to the provided query as " + + "post filter to restrict only to accessible resources. If a post filter " + + "was already provided with the query it will be replaced. " + + "Furthermore, this endpoint supports pagination. " + + "'page' and 'size' query parameters are translated into the Elastic attributes " + + "'from' and 'size' automatically, if not already provided within the query by the caller.", + security = { + @SecurityRequirement(name = "bearer-jwt")}, + responses = { + @ApiResponse(responseCode = "200", description = "OK and the search result is returned.") + }) + @ResponseBody + @PageableAsQueryParam + public ResponseEntity proxy(@RequestBody JsonNode body, + @Parameter(description = "Contains all schemaIds " + + "to which the records refer as comma-separated values. " + + "Regular expressions are also allowed. " + + "See https://www.elastic.co/guide/en/elasticsearch/reference/7.17/multi-index.html", required = true) @PathVariable(value = "schemaId") String schemaIds, + ProxyExchange proxy, + @Parameter(hidden = true) final Pageable pgbl) throws Exception { + + // Prepare query with authorization + prepareQuery(body, pgbl); + LOG.trace("Redirect post to " + searchConfiguration.getUrl() + "/" + schemaIds + SEARCH_PATH_POSTFIX); + + return proxy.uri(searchConfiguration.getUrl() + "/" + schemaIds + SEARCH_PATH_POSTFIX).post(); + } + + @PostMapping("/search") + @Operation(summary = "Search for metadata document/records", + description = "Search for metadata document/records using the configured Elastic backend. " + + "This endpoint serves as direct proxy to the RESTful endpoint of Elastic. " + + "In the body, a query document following the Elastic query format has to be provided. " + + "Format errors are returned directly from Elastic. " + + "This endpoint also supports authentication and authorization. " + + "User information obtained via JWT is applied to the provided query as " + + "post filter to restrict only to accessible resources. If a post filter " + + "was already provided with the query it will be replaced. " + + "Furthermore, this endpoint supports pagination. " + + "'page' and 'size' query parameters are translated into the Elastic attributes " + + "'from' and 'size' automatically, if not already provided within the query by the caller.", + security = { + @SecurityRequirement(name = "bearer-jwt")}, + responses = { + @ApiResponse(responseCode = "200", description = "OK and the search result is returned.") + }) + @ResponseBody + @PageableAsQueryParam + public ResponseEntity proxy(@RequestBody JsonNode body, + ProxyExchange proxy, + @Parameter(hidden = true) final Pageable pgbl) throws Exception { + + // Prepare query with authorization + prepareQuery(body, pgbl); + LOG.trace("Redirect post to " + searchConfiguration.getUrl() + SEARCH_PATH_POSTFIX); + + return proxy.uri(searchConfiguration.getUrl() + SEARCH_PATH_POSTFIX).post(); + } + + /** + * Prepare query for elasticsearch. + * + * @param body query + * @param pgbl page information + * @return Prepared query with post filter for authorization. + */ + private ObjectNode prepareQuery(JsonNode body, Pageable pgbl) { + LOG.trace("Provided Elastic query: '{}'", body.toString()); + + // Set or replace post-filter + ObjectNode on = (ObjectNode) body; + ElasticSearchUtil.addPaginationInformation(on, pgbl.getPageNumber(), pgbl.getPageSize()); + ElasticSearchUtil.buildPostFilter(on); + + LOG.trace("Generated elastic query with post filter: '{}'", on.toPrettyString()); + return on; + } +} diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 66a78483..8d2d9f43 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -107,6 +107,7 @@ @TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/md/schema"}) @TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/md/metadata"}) @TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@TestPropertySource(properties = {"metastore.metadata.landingpage=/metadata-landing-page-v2?id=$(id)&version=$(version)"}) @TestPropertySource(properties = {"repo.search.url=http://localhost:41421"}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) public class MetadataControllerTestV2 { @@ -2273,7 +2274,7 @@ public void testLandingPage4Metadata() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=")) + .andExpect(redirectedUrl("/metadata-landing-page-v2?id=" + documentId + "&version=")) .andReturn(); String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); this.mockMvc.perform(get(redirectedUrl) @@ -2339,7 +2340,7 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { // Deletion of schema shouldn't work // Get ETag. MvcResult result = mockMvc.perform(get(API_SCHEMA_PATH + schemaId). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -2391,7 +2392,7 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { andReturn(); // But it's still available result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -2404,7 +2405,7 @@ public void testDeleteSchemaWithLinkedDocument() throws Exception { andReturn(); // Now it' gone result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). - header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); From bea486dde0b44539aeccab261812829afd0dfeea Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 3 Apr 2024 07:16:50 +0200 Subject: [PATCH 025/181] All tests running. --- .../util/DataResourceRecordUtil.java | 153 ++---------------- .../web/impl/LandingPageControllerImplV2.java | 11 +- .../templates/metadata-landing-page-v2.html | 144 ----------------- .../test/MetadataControllerTestV2.java | 12 +- 4 files changed, 30 insertions(+), 290 deletions(-) delete mode 100644 src/main/resources/templates/metadata-landing-page-v2.html diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index a5c08e01..82e45a5b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -446,7 +446,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur if (version == null) { version = "0"; } - updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); ContentDataUtils.addFile(applicationProperties, updatedDataResource, document, fileName, null, true, supplier); } @@ -553,116 +553,18 @@ public static void deleteDataResourceRecord(MetastoreConfiguration applicationPr } } - /** - * Migrate metadata record to data resource. - * - * @param applicationProperties Configuration settings of repository. - * @param metadataRecord Metadata record to migrate. - * @return Data resource of metadata record. - */ - public static DataResource migrateToDataResource(RepoBaseConfiguration applicationProperties, - MetadataRecord metadataRecord) { - DataResource dataResource; - if (metadataRecord.getId() != null) { - try { - dataResource = applicationProperties.getDataResourceService().findById(metadataRecord.getId(), metadataRecord.getRecordVersion()); - dataResource = DataResourceUtils.copyDataResource(dataResource); - } catch (ResourceNotFoundException rnfe) { - LOG.error("Error catching DataResource for " + metadataRecord.getId() + " -> " + rnfe.getMessage()); - dataResource = DataResource.factoryNewDataResource(metadataRecord.getId()); - dataResource.setVersion("1"); - } - } else { - dataResource = new DataResource(); - dataResource.setVersion("1"); - } - dataResource.setAcls(metadataRecord.getAcl()); - if (metadataRecord.getCreatedAt() != null) { - boolean createDateExists = false; - Set dates = dataResource.getDates(); - for (edu.kit.datamanager.repo.domain.Date d : dates) { - if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { - LOG.trace("Creation date entry found."); - createDateExists = true; - break; - } - } - if (!createDateExists) { - dataResource.getDates().add(Date.factoryDate(metadataRecord.getCreatedAt(), Date.DATE_TYPE.CREATED)); - } - } - Set identifiers = dataResource.getAlternateIdentifiers(); - if (metadataRecord.getPid() != null) { - ResourceIdentifier identifier = metadataRecord.getPid(); - MetadataSchemaRecordUtil.checkAlternateIdentifier(identifiers, identifier.getIdentifier(), Identifier.IDENTIFIER_TYPE.valueOf(identifier.getIdentifierType().name())); - } else { - LOG.trace("Remove existing identifiers (others than URL)..."); - Set removeItems = new HashSet<>(); - for (Identifier item : identifiers) { - if (item.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { - LOG.trace("... {}, {}", item.getValue(), item.getIdentifierType()); - removeItems.add(item); - } - } - identifiers.removeAll(removeItems); - } - boolean relationFound = false; - boolean schemaIdFound = false; - for (RelatedIdentifier relatedIds : dataResource.getRelatedIdentifiers()) { - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { - LOG.trace("Set relation to '{}'", metadataRecord.getRelatedResource()); - relatedIds.setValue(metadataRecord.getRelatedResource().getIdentifier()); - relatedIds.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(metadataRecord.getRelatedResource().getIdentifierType().name())); - relationFound = true; - } - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { - updateRelatedIdentifierForSchema(relatedIds, metadataRecord); - schemaIdFound = true; - } - } - if (!relationFound) { - RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, metadataRecord.getRelatedResource().getIdentifier(), null, null); - relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(metadataRecord.getRelatedResource().getIdentifierType().name())); - dataResource.getRelatedIdentifiers().add(relatedResource); - } - if (!schemaIdFound) { - RelatedIdentifier schemaId = updateRelatedIdentifierForSchema(null, metadataRecord); - dataResource.getRelatedIdentifiers().add(schemaId); - } - String defaultTitle = "Metadata 4 metastore"; - boolean titleExists = false; - for (Title title : dataResource.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER && title.getValue().equals(defaultTitle)) { - titleExists = true; - } - } - if (!titleExists) { - dataResource.getTitles().add(Title.factoryTitle(defaultTitle, Title.TYPE.OTHER)); - } - dataResource.setResourceType(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE)); - checkLicense(dataResource, metadataRecord.getLicenseUri()); - - return dataResource; - } - /** * Migrate data resource to metadata record. * * @param applicationProperties Configuration settings of repository. * @param dataResource Data resource to migrate. - * @param provideETag Flag for calculating etag. * @return Metadata record of data resource. */ - public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration applicationProperties, - DataResource dataResource, - boolean provideETag) { - long nano1 = System.nanoTime() / 1000000; + public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration applicationProperties, + DataResource dataResource) { MetadataRecord metadataRecord = new MetadataRecord(); if (dataResource != null) { metadataRecord.setId(dataResource.getId()); - if (provideETag) { - metadataRecord.setETag(dataResource.getEtag()); - } metadataRecord.setAcl(dataResource.getAcls()); for (edu.kit.datamanager.repo.domain.Date d : dataResource.getDates()) { @@ -726,44 +628,19 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli throw new BadArgumentException(message); } DataRecord dataRecord = null; - long nano2 = System.nanoTime() / 1000000; - Optional dataRecordResult = dataRecordDao.findByMetadataIdAndVersion(dataResource.getId(), recordVersion); - long nano3 = System.nanoTime() / 1000000; - long nano4 = nano3; - boolean isAvailable = false; - boolean saveDataRecord = false; - if (dataRecordResult.isPresent()) { - LOG.trace("Get document URI from DataRecord."); - dataRecord = dataRecordResult.get(); - nano4 = System.nanoTime() / 1000000; - metadataRecord.setMetadataDocumentUri(dataRecord.getMetadataDocumentUri()); - metadataRecord.setDocumentHash(dataRecord.getDocumentHash()); - metadataRecord.setSchemaVersion(dataRecord.getSchemaVersion()); - isAvailable = true; - } else { - saveDataRecord = true; - } - if (!isAvailable) { - LOG.trace("Get document URI from ContentInformation."); - ContentInformation info; - info = getContentInformationOfResource(applicationProperties, dataResource); - nano4 = System.nanoTime() / 1000000; - if (info != null) { - metadataRecord.setDocumentHash(info.getHash()); - metadataRecord.setMetadataDocumentUri(info.getContentUri()); - MetadataSchemaRecord currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(schemaConfig, metadataRecord.getSchema()); - metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); - if (saveDataRecord) { - saveNewDataRecord(metadataRecord); - } - } + LOG.trace("Get document URI from ContentInformation."); + ContentInformation info; + info = getContentInformationOfResource(applicationProperties, dataResource); + if (info != null) { + metadataRecord.setDocumentHash(info.getHash()); + metadataRecord.setMetadataDocumentUri(info.getContentUri()); + MetadataSchemaRecord currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(schemaConfig, metadataRecord.getSchema()); + metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); } // Only one license allowed. So don't worry about size of set. if (!dataResource.getRights().isEmpty()) { metadataRecord.setLicenseUri(dataResource.getRights().iterator().next().getSchemeUri()); } - long nano5 = System.nanoTime() / 1000000; - LOG.info("Migrate to MetadataRecord, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, provideETag); } return metadataRecord; @@ -1830,8 +1707,8 @@ private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataRes String schemaId = schemaIdentifier.getValue(); switch (schemaIdentifier.getIdentifierType()) { case URL: - schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); - break; + schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); + break; case INTERNAL: String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); if (split.length == 1) { @@ -2031,9 +1908,9 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro } else { String schemaId = schemaIdentifier.getValue(); String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); - + if (split.length > 1) { - findByAlternateId = schemaRecordDao.findBySchemaId(schemaId); + findByAlternateId = schemaRecordDao.findBySchemaId(schemaId); } else { findByAlternateId = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(split[0] + SCHEMA_VERSION_SEPARATOR); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java index b36ba453..b4ebc9bc 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java @@ -125,12 +125,19 @@ public String getLandingPageOfMetadataDocumentWithId(@PathVariable(value = "id") recordList.add(DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, size)); } } + List resultList = new ArrayList<>(); + for (DataResource item : recordList) { + DataResourceRecordUtil.fixSchemaUrl(item); + MetadataRecord metadataRecord = DataResourceRecordUtil.migrateToMetadataRecordV2(metadataConfig, item); + + resultList.add(metadataRecord); + } model.addAttribute("type", recordList.get(0).getFormats().iterator().next()); - model.addAttribute("records", recordList); + model.addAttribute("records", resultList); - return "metadata-landing-page-v2.html"; + return "metadata-landing-page.html"; } } diff --git a/src/main/resources/templates/metadata-landing-page-v2.html b/src/main/resources/templates/metadata-landing-page-v2.html deleted file mode 100644 index ac0df88e..00000000 --- a/src/main/resources/templates/metadata-landing-page-v2.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - Landingpage for Metadata document - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- - - - -
- -
-
-

Entries

-
-
- - - - - - - - - - - - - -
ID VersionTypeLast Update Related ResourceMetadataSchema
............ - - - -
-
-
-
-
- - diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 8d2d9f43..c55313d8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -2224,7 +2224,7 @@ public void testLandingPage4MetadataUnknownID() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/metadata-landing-page?id=anything&version=")) + .andExpect(redirectedUrl("/metadata-landing-page-v2?id=anything&version=")) .andReturn(); String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); this.mockMvc.perform(get(redirectedUrl) @@ -2239,7 +2239,7 @@ public void testLandingPage4MetadataWithSchemaId() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/metadata-landing-page?id=" + SCHEMA_ID + "&version=")) + .andExpect(redirectedUrl("/metadata-landing-page-v2?id=" + SCHEMA_ID + "&version=")) .andReturn(); String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); this.mockMvc.perform(get(redirectedUrl) @@ -2257,7 +2257,7 @@ public void testLandingPage4MetadataWrongVersion() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=2")) + .andExpect(redirectedUrl("/metadata-landing-page-v2?id=" + documentId + "&version=2")) .andReturn(); String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); this.mockMvc.perform(get(redirectedUrl) @@ -2286,7 +2286,7 @@ public void testLandingPage4Metadata() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=1")) + .andExpect(redirectedUrl("/metadata-landing-page-v2?id=" + documentId + "&version=1")) .andReturn(); redirectedUrl = andReturn.getResponse().getRedirectedUrl(); this.mockMvc.perform(get(redirectedUrl) @@ -2310,7 +2310,7 @@ public void testLandingPage4Metadata() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=2")) + .andExpect(redirectedUrl("/metadata-landing-page-v2?id=" + documentId + "&version=2")) .andReturn(); redirectedUrl = andReturn.getResponse().getRedirectedUrl(); this.mockMvc.perform(get(redirectedUrl) @@ -2321,7 +2321,7 @@ public void testLandingPage4Metadata() throws Exception { .accept("text/html")) .andDo(print()) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/metadata-landing-page?id=" + documentId + "&version=")) + .andExpect(redirectedUrl("/metadata-landing-page-v2?id=" + documentId + "&version=")) .andReturn(); redirectedUrl = andReturn.getResponse().getRedirectedUrl(); this.mockMvc.perform(get(redirectedUrl) From 6fde24668a38560bb80b9b40e11e853a7bf4924c Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 15 Apr 2024 16:04:51 +0200 Subject: [PATCH 026/181] First step towards documentation for JSON documents. --- ...tryControllerDocumentation4JsonTestV2.java | 574 ++++++++++++++++++ .../test/SchemaRegistryControllerTestV2.java | 39 +- 2 files changed, 607 insertions(+), 6 deletions(-) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java new file mode 100644 index 00000000..28fc1255 --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java @@ -0,0 +1,574 @@ +/* + * Copyright 2018 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.documentation; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.test.SchemaRegistryControllerTestV2; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Comparator; +import java.util.stream.Stream; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.restdocs.operation.preprocess.Preprocessors; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + * + */ +//@ActiveProfiles("doc") +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41427"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_doc_json;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/restdocu/json/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/restdocu/json/metadata"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@TestPropertySource(properties = {"server.error.include-message=always"}) +public class SchemaRegistryControllerDocumentation4JsonTestV2 { + + private MockMvc mockMvc; + @Autowired + private WebApplicationContext context; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + private final static String EXAMPLE_SCHEMA_ID = "my_first_json"; + private final static String ANOTHER_SCHEMA_ID = "another_json"; + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/restdocu/json/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private final static String SCHEMA_V1 = "{\n" + + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + + private final static String SCHEMA_V2 = "{\n" + + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\",\n" + + " \"date\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " },\n" + + " \"date\": {\n" + + " \"type\": \"string\",\n" + + " \"format\": \"date\",\n" + + " \"title\": \"Date\",\n" + + " \"description\": \"Date of object\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + + private final static String SCHEMA_V3 = "{\n" + + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\",\n" + + " \"date\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " },\n" + + " \"date\": {\n" + + " \"type\": \"string\",\n" + + " \"format\": \"date\",\n" + + " \"title\": \"Date\",\n" + + " \"description\": \"Date of object\"\n" + + " },\n" + + " \"note\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Note\",\n" + + " \"description\": \"Additonal information about object\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + + private final static String ANOTHER_SCHEMA = "{\n" + + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json/example\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Another Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"description\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"description\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Description\",\n" + + " \"description\": \"Any description.\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + + private final static String DOCUMENT_V1 = "{\n" + + "\"title\": \"My first JSON document\"\n" + + "}"; + + private final static String DOCUMENT_V2 = "{\n" + + "\"title\": \"My second JSON document\",\n" + + "\"date\": \"2018-07-02\"\n" + + "}"; + + private final static String DOCUMENT_V3 = "{\n" + + "\"title\": \"My third JSON document\",\n" + + "\"date\": \"2018-07-02\",\n" + + "\"note\": \"since version 3 notes are allowed\"\n" + + "}"; + private static final ResourceIdentifier RELATED_RESOURCE = ResourceIdentifier.factoryUrlResourceIdentifier("https://repo/anyResourceId"); + + @Before + public void setUp() throws JsonProcessingException { + try { + try (Stream walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + try (Stream walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation) + .uris().withPort(8040).and() + .operationPreprocessors() + .withRequestDefaults(prettyPrint()) + .withResponseDefaults(Preprocessors.removeHeaders("X-Content-Type-Options", "X-XSS-Protection", "X-Frame-Options"), prettyPrint())) + .build(); + } + + @Test + public void documentSchemaRegistry4Json() throws Exception { + DataResource schemaRecord = new DataResource(); + String contextPath = "/metastore"; + String endpointSchema = contextPath + "/api/v2/schemas/"; + String endpointMetadata = contextPath + "/api/v2/metadata/"; + // 1. Registering metadata schema + //************************************************************************** + schemaRecord.setId(EXAMPLE_SCHEMA_ID); + SchemaRegistryControllerTestV2.setTitle(schemaRecord, "Title for " + EXAMPLE_SCHEMA_ID); +// schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", SCHEMA_V1.getBytes()); + MockMultipartFile recordFile = new MockMultipartFile("record", "schema-record4json.json", "application/json", new ByteArrayInputStream(mapper.writeValueAsString(schemaRecord).getBytes())); + + //create resource and obtain location from response header + String location = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema). + file(schemaFile). + file(recordFile). + contextPath(contextPath)). + andDo(document("v2-register-json-schema")). + andExpect(status().isCreated()). + andReturn().getResponse().getHeader("Location"); + String exampleSchemaV1 = location; + + Assert.assertNotNull(location); + // 2. Getting metadata Schema Record + //************************************************************************** + // Get single metadata schema record + String etag = this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(document("v2-get-json-schema-record")). + andExpect(status().isOk()). + andReturn().getResponse().getHeader("ETag"); + + // 3. Getting metadata Schema document + //************************************************************************** + // Get metadata schema + this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath)). + andDo(document("v2-get-json-schema-document")). + andExpect(status().isOk()). + andReturn().getResponse(); + // 4. Update to second version of schema + //************************************************************************** + //update schema document and create new version + schemaFile = new MockMultipartFile("schema", "schema-v2.json", "application/json", SCHEMA_V2.getBytes()); + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + file(schemaFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(document("v2-update-json-schema-v2")). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + String exampleSchemaV2 = result.getResponse().getHeader("location"); + + // 5. Update to third version of schema + //************************************************************************** + schemaFile = new MockMultipartFile("schema", "schema-v3.json", "application/json", SCHEMA_V3.getBytes()); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + file(schemaFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(document("v2-update-json-schema-v3")). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + String exampleSchemaV3 = result.getResponse().getHeader("ETag"); + // 6. Registering another metadata schema + //************************************************************************** + schemaRecord.setId(ANOTHER_SCHEMA_ID); + + schemaFile = new MockMultipartFile("schema", "another-schema.json", "application/xml", ANOTHER_SCHEMA.getBytes()); + recordFile = new MockMultipartFile("record", "another-schema-record.json", "application/json", new ByteArrayInputStream(mapper.writeValueAsString(schemaRecord).getBytes())); + + location = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema). + file(schemaFile). + file(recordFile). + contextPath(contextPath)). + andDo(document("v2-register-another-json-schema")). + andExpect(status().isCreated()). + andReturn().getResponse().getHeader("Location"); + + Assert.assertNotNull(location); + // 7. List all schema records (only current schemas) + //************************************************************************** + this.mockMvc.perform(get(endpointSchema). + contextPath(contextPath)). + andDo(document("v2-get-all-json-schemas")). + andExpect(status().isOk()). + andReturn().getResponse(); + + this.mockMvc.perform(get(endpointSchema). + param("page", Integer.toString(0)). + param("size", Integer.toString(20)). + contextPath(contextPath)). + andDo(document("v2-get-all-json-schemas-pagination")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 8. List all versions of a schema + //************************************************************************** + this.mockMvc.perform(get(endpointSchema). + param("schemaId", EXAMPLE_SCHEMA_ID). + contextPath(contextPath)). + andDo(document("v2-get-all-versions-of-a-json-schema")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 9. Getting current schema + //************************************************************************** + this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath)). + andDo(document("v2-get-json-schema-v3")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 10. Getting specific version of a schema + //************************************************************************** + this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + param("version", "1"). + contextPath(contextPath)). + andDo(document("v2-get-json-schema-v1")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 11. Validate metadata document + //************************************************************************** + MockMultipartFile metadataFile_v3 = new MockMultipartFile("document", "metadata-v3.json", "application/json", DOCUMENT_V3.getBytes()); + // 11 a) Validate with version=1 --> invalid + //************************************************************************** + this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID + "/validate"). + file(metadataFile_v3) + .queryParam("version", "1"). + contextPath(contextPath)). + andDo(document("v2-validate-json-document-v1")). + andExpect(status().isUnprocessableEntity()). + andReturn().getResponse(); + // 11 b) Validate without version --> version 3 (should be valid) + //************************************************************************** + this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID + "/validate"). + file(metadataFile_v3). + contextPath(contextPath)). + andDo(document("v2-validate-json-document-v3")). + andExpect(status().isNoContent()). + andReturn().getResponse(); + + // 12. Update metadata Schema Record + //************************************************************************** + // Update metadata record to allow admin to edit schema as well. + result = this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + mapper = new ObjectMapper(); + schemaRecord = mapper.readValue(body, DataResource.class); + schemaRecord.getAcls().add(new AclEntry("admin", PERMISSION.ADMINISTRATE)); + + recordFile = new MockMultipartFile("record", "schema-record4json-v4.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + file(recordFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(document("v2-update-json-schema-record")). + andExpect(status().isOk()). + andReturn().getResponse(); + + //************************************************************************** + // metadata management + //************************************************************************** + // 1. Ingest metadata document + //************************************************************************** + // Create a metadata record. + DataResource metadataRecord = new DataResource(); + SchemaRegistryControllerTestV2.setTitle(metadataRecord, "Title of first metadata document"); + metadataRecord.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.JSON_METADATA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); + +// record.setId("my_id"); + SchemaRegistryControllerTestV2.setRelatedResource(metadataRecord, RELATED_RESOURCE.getIdentifier()); + SchemaRegistryControllerTestV2.setRelatedSchema(metadataRecord, exampleSchemaV1); + + recordFile = new MockMultipartFile("record", "metadata-record4json.json", "application/json", mapper.writeValueAsString(metadataRecord).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.json", "application/json", DOCUMENT_V1.getBytes()); + + location = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointMetadata). + file(recordFile). + file(metadataFile). + contextPath(contextPath)). + andDo(document("v2-ingest-json-metadata-document")). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn().getResponse().getHeader("Location"); + // Get URL + String newLocation = location.split("[?]")[0]; + + // 2. Accessing metadata document + //************************************************************************** + this.mockMvc.perform(get(location).accept("application/json"). + contextPath(contextPath)). + andDo(document("v2-get-json-metadata-document")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 3. Accessing metadata record + //************************************************************************** + this.mockMvc.perform(get(location).accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE). + contextPath(contextPath)). + andDo(document("v2-get-json-metadata-record")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 4. Update metadata record & document + //************************************************************************** + result = this.mockMvc.perform(get(location). + contextPath(contextPath). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + body = result.getResponse().getContentAsString(); + + mapper = new ObjectMapper(); + DataResource record = mapper.readValue(body, DataResource.class); + SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV2); + recordFile = new MockMultipartFile("record", "metadata-record4json-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); + metadataFile = new MockMultipartFile("document", "metadata-v2.json", "application/xml", DOCUMENT_V2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(location). + file(recordFile). + file(metadataFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(print()). + andDo(document("v2-update-json-metadata-record-v2")). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + location = result.getResponse().getHeader("Location"); + // 5. Update metadata record + //************************************************************************** + // update once more to newest version of schema + // Get Etag + this.mockMvc.perform(get(location). + contextPath(contextPath). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(document("v2-get-json-metadata-record-v2")). + andExpect(status().isOk()). + andReturn().getResponse(); + SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV3); + recordFile = new MockMultipartFile("record", "metadata-record4json-v3.json", "application/json", mapper.writeValueAsString(record).getBytes()); + metadataFile = new MockMultipartFile("document", "metadata-v3.json", "application/xml", DOCUMENT_V3.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(newLocation). + file(recordFile). + file(metadataFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(print()). + andDo(document("v2-update-json-metadata-record-v3")). + andExpect(status().isOk()). + andReturn(); + location = result.getResponse().getHeader("Location"); + this.mockMvc.perform(get(location). + contextPath(contextPath)). + andDo(document("v2-get-json-metadata-document-v3")). + andExpect(status().isOk()). + andReturn().getResponse(); + // 6. List all versions of a record + //************************************************************************** + String resourceId = record.getId(); + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("id", resourceId)). + andDo(print()). + andDo(document("v2-list-all-versions-of-json-metadata-document")). + andExpect(status().isOk()). + andReturn(); + + // 7. Find a metadata record. + //************************************************************************** + // find all metadata for a resource + Instant oneHourBefore = Instant.now().minusSeconds(3600); + Instant twoHoursBefore = Instant.now().minusSeconds(7200); + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("resoureId", RELATED_RESOURCE.getIdentifier())). + andDo(print()). + andDo(document("v2-find-json-metadata-record-resource")). + andExpect(status().isOk()). + andReturn(); + + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("from", twoHoursBefore.toString())). + andDo(print()). + andDo(document("v2-find-json-metadata-record-from")). + andExpect(status().isOk()). + andReturn(); + + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("from", twoHoursBefore.toString()). + param("until", oneHourBefore.toString())). + andDo(print()). + andDo(document("v2-find-json-metadata-record-from-to")). + andExpect(status().isOk()). + andReturn(); + + } + + private static RequestPostProcessor putMultipart() { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setMethod("PUT"); + return request; + }; + } + +} diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 77926a4b..2ee794b6 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1964,6 +1964,33 @@ public static DataResource createDataResource4Document(String id, String schemaI record.setAcls(aclEntries); return record; } + + public static void setRelatedResource(DataResource dataResource, String relatedResource) { + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + if (relatedIdentifier != null) { + relatedIdentifier.setValue(relatedResource); + } else { + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, relatedResource, null, null); + dataResource.getRelatedIdentifiers().add(relatedIdentifier); + } + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + + } + public static void setRelatedSchema(DataResource dataResource, String relatedSchema) { + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + if (relatedIdentifier != null) { + relatedIdentifier.setValue(relatedSchema); + } else { + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, relatedSchema, null, null); + dataResource.getRelatedIdentifiers().add(relatedIdentifier); + } + if (relatedSchema.startsWith("http")) { + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + } else { + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + } + + } private String createKitMetadataRecord(String schemaId) throws Exception { String documentId = "kit"; @@ -2467,7 +2494,7 @@ public static void validateTitles(Set first, Set<Title> second) { } } - private static String getTitle(DataResource record) { + public static String getTitle(DataResource record) { String returnValue = null; for (Title item : record.getTitles()) { if (item.getTitleType() == null) { @@ -2478,7 +2505,7 @@ private static String getTitle(DataResource record) { return returnValue; } - private static void setTitle(DataResource record, String title) { + public static void setTitle(DataResource record, String title) { boolean addTitle = true; for (Title item : record.getTitles()) { if (item.getTitleType() == null) { @@ -2492,7 +2519,7 @@ private static void setTitle(DataResource record, String title) { } } - private static String getDefinition(DataResource record) { + public static String getDefinition(DataResource record) { String returnValue = null; for (Description item : record.getDescriptions()) { if (item.getType() == Description.TYPE.TECHNICAL_INFO) { @@ -2503,7 +2530,7 @@ private static String getDefinition(DataResource record) { return returnValue; } - private static void setDefinition(DataResource record, String definition) { + public static void setDefinition(DataResource record, String definition) { boolean addDefinition = true; for (Description item : record.getDescriptions()) { if (item.getType() == Description.TYPE.TECHNICAL_INFO) { @@ -2517,7 +2544,7 @@ private static void setDefinition(DataResource record, String definition) { } } - private static String getComment(DataResource record) { + public static String getComment(DataResource record) { String returnValue = null; for (Description item : record.getDescriptions()) { if (item.getType() == Description.TYPE.OTHER) { @@ -2528,7 +2555,7 @@ private static String getComment(DataResource record) { return returnValue; } - private static void setComment(DataResource record, String comment) { + public static void setComment(DataResource record, String comment) { boolean addComment = true; for (Description item : record.getDescriptions()) { if (item.getType() == Description.TYPE.OTHER) { From 4fca67dc4561dea24306e7740095ef4f67a57758 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 16 Apr 2024 09:24:22 +0200 Subject: [PATCH 027/181] Fix typo in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 630cb1f5..29116063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Switch to GitHub Packages. -- Loge for SCC (Scientific Computing Centre) +- Logo for SCC (Scientific Computing Centre) ### Libs - Bump com.google.errorprone:error_prone_core from 2.24.0 to 2.24.1 From 8ab618f87a315b1b942e6693e1c800129ad302f3 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 22 Apr 2024 07:45:47 +0200 Subject: [PATCH 028/181] First step towards documenting API V2. --- docs/documentationV2.adoc | 1475 +++++++++++++++++ ...tryControllerDocumentation4JsonTestV2.java | 7 +- ...RegistryControllerDocumentationTestV2.java | 558 +++++++ .../test/SchemaRegistryControllerTestV2.java | 50 +- 4 files changed, 2086 insertions(+), 4 deletions(-) create mode 100644 docs/documentationV2.adoc create mode 100644 src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java diff --git a/docs/documentationV2.adoc b/docs/documentationV2.adoc new file mode 100644 index 00000000..ed0d4740 --- /dev/null +++ b/docs/documentationV2.adoc @@ -0,0 +1,1475 @@ += KIT Data Manager Documentation - MetaStore Service +v{version}, KIT Data Manager Development Team, {date} +:doctype: book +:icons: font +:source-highlighter: highlightjs +:highlightjs-theme: github +:toc: left +:toclevels: 4 +:sectlinks: +:sectnums: + +[introduction] += RESTful API Description + +== Introduction + +In this documentation, the basics of the KIT Data Manager RESTful API of the MetaStore Service are described. You will be guided through the first steps of +register an XML schema, update it. After that add an appropriate metadata document to MetaStore which may be linked to a data resource. + +This documentation assumes, that you have an instance of the KIT Data Manager MetaStore repository service installed locally. If the repository is running on another +host or port you should change hostname and/or port accordingly. Furthermore, the examples assume that you are using the repository without authentication +and authorization, which is provided by another service. If you plan to use this optional service, please refer to its documentation first to see how the +examples in this documentation have to be modified in order to work with authentication. Typically, this should be achieved by simple adding an additional header +entry. + +The example structure is identical for all examples below. Each example starts with a CURL command that can be run by copy&paste to your console/terminal window. +The second part shows the HTTP request sent to the server including arguments and required headers. Finally, the third block shows the response comming from the server. +In between, special characteristics of the calls are explained together with additional, optional arguments or alternative responses. + +[NOTE] +For technical reasons, all metadata resources shown in the examples contain all fields, e.g. also empty lists or fields with value 'null'. You may ignore most of them +as long as they are not needed. Some of them will be assigned by the server, others remain empty or null as long as you don't assign any value to them. +All fields mandatory at creation time are explained in the resource creation example. + +=== Building the URL +The URL for accessing the MetaStore REST endpoints is constructed as follows: + +1. Protocol (e.g., http, https) +2. Host name (e.g. localhost, www.example.org) +3. Port (e.g. 8040) +4. Context path (e.g. /metastore) +5. Endpoint (e.g. /api/v1/metadata) + +For example, to list all the schema records in your local MetaStore, you need to +run the following in your browser: http://localhost:8040/metastore/api/v1/schemas + +In former versions (< 1.3.0), no context path was provided by default. + +[[XML]] +== XML (Schema) +[[ChapterMetadataSchemaHandling4Xml]] +=== Schema Registration and Management + +In this first section, the handling of schema resources is explained. It all starts with creating your first xml schema resource. The model of a metadata schema record looks +like this: +[source,options="nowrap"] +---- +{ + "schemaId" : "...", + "schemaVersion" : 1, + "mimeType" : "...", + "type" : "...", + "createdAt" : "...", + "lastUpdate" : "...", + "acl" : [ { + "id" : 1, + "sid" : "...", + "permission" : "..." + } ], + "licenseUri" : "...", + "schemaDocumentUri" : "...", + "schemaHash" : "...", + "locked" : false +} +---- +At least the following elements are expected to be provided by the user: + +[point] +- schemaId: A unique label for the schema. +- type: XML or JSON. For XSD schemas this should be 'XML' + +In addition, ACL may be useful to make schema readable/editable by others. +This will be of interest while accessing/updating an existing schema.(if authorization is enabled) + +[NOTE] +License URI is optional. It's new since 1.5.0. + +=== Registering a Metadata Schema Document + +The following example shows the creation of the first xsd schema only providing mandatory fields mentioned above: +[source,options="nowrap"] +---- +schema-record.json: +{ + "schemaId" : "my_first_xsd", + "type" : "XML" +} +---- +[source,options="nowrap"] +---- +schema.xsd: +<xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> +---- + +include::{snippets}/v2-register-schema/curl-request.adoc[] + +You can see, that most of the sent metadata schema record is empty. Only schemaId, mimeType and type are provided by the user. HTTP-wise the call looks as follows: + +include::{snippets}/v2-register-schema/http-request.adoc[] + +As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: + +include::{snippets}/v2-register-schema/http-response.adoc[] + +What you see is, that the metadata schema record looks different from the original document. All remaining elements received a value by the server. +Furthermore, you'll find an ETag header with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for +all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. + +==== Getting a Metadata Schema Record + +For obtaining one metadata schema record you have to provide the value of the field 'schemaId'. +[NOTE] +As 'Accept' field you have to provide 'application/vnd.datamanager.schema-record+json' otherwise you will +get the metadata schema instead. + +include::{snippets}/v2-get-schema-record/curl-request.adoc[] + +In the actual HTTP request just access the path of the resource using the base path and the 'schemaid'. +Be aware that you also have to provide the 'Accept' field. + +include::{snippets}/v2-get-schema-record/http-request.adoc[] + +As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. + +include::{snippets}/v2-get-schema-record/http-response.adoc[] + +==== Getting a Metadata Schema Document + +For obtaining accessible metadata schemas you also have to provide the 'schemaId'. +For accessing schema document you don't have to provide the 'Accept' header. + +include::{snippets}/v2-get-schema-document/curl-request.adoc[] + +In the actual HTTP request there is nothing special. You just access the path of the resource using the base path and the 'schemaId'. + +include::{snippets}/v2-get-schema-document/http-request.adoc[] + +As a result, you receive the XSD schema send before. + +include::{snippets}/v2-get-schema-document/http-response.adoc[] + +==== Updating a Metadata Schema Document (add mandatory 'date' field) + +[NOTE] +Updating a metadata schema document will not break old metadata documents. +As every update results in a new version 'old' metadata schema documents are still +available. + +For updating an existing metadata schema (record) a valid ETag is needed. The actual ETag +is available via the HTTP GET call of the metadata schema record. (see above) +Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. + +[source,options="nowrap"] +---- +schema-v2.xsd: +<xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + <xs:element name="date" type="xs:date"/> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> +---- + +include::{snippets}/v2-update-schema-v2/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-schema-v2/http-request.adoc[] + +As a result, you receive the updated schema record and in the HTTP response header +the new location URL and the ETag. + +include::{snippets}/v2-update-schema-v2/http-response.adoc[] + +==== Updating a Metadata Schema Document (add optional 'note' field) + +For updating existing metadata schema document we have to provide the new ETag. +Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. + +[source,options="nowrap"] +---- +schema-v3.xsd: +<xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + <xs:element name="date" type="xs:date"/> + <xs:element name="note" type="xs:string" minOccurs="0"/> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> +---- + +include::{snippets}/v2-update-schema-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-schema-v3/http-request.adoc[] + +As a result, you receive the updated schema record and in the HTTP response header +the new location URL and the ETag. + +include::{snippets}/v2-update-schema-v3/http-response.adoc[] + +The updated schema record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. + +=== Registering another Metadata Schema Document + +The following example shows the creation of another xsd schema only providing mandatory fields mentioned above: +[source,options="nowrap"] +---- +another-schema-record.json: +{ + "schemaId" : "another_xsd", + "type" : "XML" +} +---- +[source,options="nowrap"] +---- +another-schema.xsd: +<xs:schema targetNamespace="http://www.example.org/schema/xsd/example" + xmlns="http://www.example.org/schema/xsd/example" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="description" type="xs:string"/> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> +---- + +include::{snippets}/v2-register-another-schema/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-register-another-schema/http-request.adoc[] + +As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: + +include::{snippets}/v2-register-another-schema/http-response.adoc[] + +Now there are two schemaIds registered in the metadata schema registry. + +==== Getting a List of Metadata Schema Records + +Obtaining all accessible metadata schema records. + +include::{snippets}/v2-get-all-schemas/curl-request.adoc[] + +Same for HTTP request: + +include::{snippets}/v2-get-all-schemas/http-request.adoc[] + +As a result, you receive a list of metadata schema records. + +include::{snippets}/v2-get-all-schemas/http-response.adoc[] + +[NOTE] +Only the current version of each schemaId is listed. + +[NOTE] +The header contains the field 'Content-Range" which displays delivered indices and the maximum number of available schema records. +If there are more than 20 schemas registered you have to provide page and/or size as additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** +- size: Number of entries per page. + +The modified HTTP request with pagination looks like follows: + +include::{snippets}/v2-get-all-schemas-pagination/http-request.adoc[] + +==== Getting a List of all Schema Records for a Specific SchemaId + +If you want to obtain all versions of a specific schema you may add the schemaId as +a filter parameter. This may look like this: + +include::{snippets}/v2-get-all-versions-of-a-schema/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-all-versions-of-a-schema/http-request.adoc[] + +As a result, you receive a list of metadata schema records in descending order. +(current version first) + +include::{snippets}/v2-get-all-versions-of-a-schema/http-response.adoc[] + +==== Getting current Version of Metadata Schema Document + +To get the current version of the metadata schema document just send an HTTP GET with the linked +'schemaId': + +include::{snippets}/v2-get-schema-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-schema-v3/http-request.adoc[] + +As a result, you receive the XSD schema document sent before. + +include::{snippets}/v2-get-schema-v3/http-response.adoc[] + +==== Getting a specific Version of Metadata Schema Document + +To get a specific version of the metadata schema document just send an HTTP GET with the linked +'schemaId' and the version number you are looking for as query parameter: + +include::{snippets}/v2-get-schema-v1/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-schema-v1/http-request.adoc[] + +As a result, you receive the initial XSD schema document (version 1). + +include::{snippets}/v2-get-schema-v1/http-response.adoc[] + +==== Validating Metadata Document + +Before an ingest of metadata is made the metadata should be successfully validated. Otherwise +the ingest may be rejected. Select the schema and the schemaVersion to validate given document. +[source,options="nowrap"] +---- +metadata-v3.xml: +<?xml version='1.0' encoding='utf-8'?> +<example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My third XML document</example:title> + <example:date>2018-07-02</example:date> + <example:note>since version 3 notes are allowed</example:note> +</example:metadata> +---- + +On a first step validation with the old schema will be done: + +include::{snippets}/v2-validate-document-v1/curl-request.adoc[] + +Same for the HTTP request. The schemaVersion number is set by a query parameter. + +include::{snippets}/v2-validate-document-v1/http-request.adoc[] + +As a result, you receive 422 as HTTP status and an error message holding some information about the error. +(Unfortunately not documented here due to technical reasons.) + +include::{snippets}/v2-validate-document-v1/http-response.adoc[] + +The document holds a mandatory and an optional field introduced in the second and third version of schema. +Let's try to validate with third version of schema. Only version number will be different. (if no query +parameter is available the current version will be selected) + +include::{snippets}/v2-validate-document-v3/curl-request.adoc[] + +Same for the HTTP request. + +include::{snippets}/v2-validate-document-v3/http-request.adoc[] + +Everything should be fine now. As a result, you receive 204 as HTTP status and no further content. + +include::{snippets}/v2-validate-document-v3/http-response.adoc[] + +==== Update Metadata Schema Record +In case of authorization it may be neccessary to update metadata record to be accessible +by others. To do so an update has to be made. In this example we introduce a +user called 'admin' and give him all rights. + +[source,options="nowrap"] +---- +schema-record-v4.json +{ + "schemaId":"my_first_xsd", + "mimeType":"application/xml", + "type":"XML", + "acl":[ + { + "id":33, + "sid":"SELF", + "permission":"ADMINISTRATE" + }, + { + "id":null, + "sid":"admin", + "admin":"ADMINISTRATE" + } + ] +} +---- +include::{snippets}/v2-update-schema-record/curl-request.adoc[] + +Same for the HTTP request. + +include::{snippets}/v2-update-schema-record/http-request.adoc[] + +As a result, you receive 200 as HTTP status, the updated metadata schema record and the +updated ETag and location in the HTTP response header. + +include::{snippets}/v2-update-schema-record/http-response.adoc[] + +After the update the following fields has changed: + +- schemaVersion number increased by one. +- lastUpdate to the date of the last update (set by server) +- acl additional ACL entry (set during update) + +=== Metadata Management +After registration of a schema metadata may be added to MetaStore. +In this section, the handling of metadata resources is explained. +It all starts with creating your first metadata resource. The model of a metadata record looks +like this: +[source,options="nowrap"] +---- +{ + "id": "...", + "relatedResource": { + "identifier": "...", + "identifierType": "..." + }, + "createdAt": "...", + "lastUpdate": "...", + "recordVersion": 1, + "schema": { + "identifier": "...", + "identifierType": "..." + }, + "schemaVersion": 1, + "acl": [{ + "id": 1, + "sid": "...", + "permission": "..." + }], + "licenseUri": "...", + "metadataDocumentUri": "...", + "documentHash": "..." +} +---- +At least the following elements are expected to be provided by the user: + +[point] +- schema: Identifier of the linked schema. (INTERNAL and URL supported as type) +- relatedResource: The link to the resource. + +In addition, ACL may be useful to make metadata editable by others. (This will be of interest while updating an existing metadata) + +[NOTE] +If linked schema is identified by its schemaId the INTERNAL type has to be used. + +[NOTE] +License URI is optional. It's new since 1.5.0. + +==== Register/Ingest a Metadata Record with Metadata Document + +The following example shows the creation of the first metadata record and its metadata only providing mandatory fields mentioned above: +[source,options="nowrap"] +---- +metadata-record.json: +{ + "relatedResource": { + "identifier": "anyResourceId", + "identifierType": "INTERNAL" + }, + "schema": { + "identifier": "my_first_xsd", + "identifierType": "INTERNAL" + }, + "schemaVersion": 1 +} +---- +[source,options="nowrap"] +---- +metadata.xml: +<?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My first XML document</example:title> +</example:metadata> +---- +The schemaId used while registering metadata schema has to be used to link the metadata with the +approbriate metadata schema. +include::{snippets}/v2-ingest-metadata-document/curl-request.adoc[] + +You can see, that most of the sent metadata schema record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: + +include::{snippets}/v2-ingest-metadata-document/http-request.adoc[] + +As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: + +include::{snippets}/v2-ingest-metadata-document/http-response.adoc[] + +What you see is, that the metadata record looks different from the original document. All remaining elements received a value by the server. +In the header you'll find a location URL to access the ingested metadata and an ETag with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for +all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. + +==== Accessing Metadata Document +For accessing the metadata the location URL provided before may be used. +The URL is compiled by the id of the metadata and its version. +include::{snippets}/v2-get-metadata-document/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-metadata-document/http-request.adoc[] + +The linked metadata will be returned. The result is sent back to the user and will look that way: + +include::{snippets}/v2-get-metadata-document/http-response.adoc[] + +What you see is, that the metadata is untouched. + +==== Accessing Metadata Record +For accessing the metadata record the same URL as before has to be used. +The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". +Then the command line looks like this: +include::{snippets}/v2-get-metadata-record/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-metadata-record/http-request.adoc[] + +The linked metadata will be returned. The result is sent back to the user and will look that way: + +include::{snippets}/v2-get-metadata-record/http-response.adoc[] + +You also get the metadata record seen before. + + +==== Updating a Metadata Record (edit ACL entries) & Metadata Document + +The following example shows the update of the metadata record and metadata document +to a newer version of the schema. As mentioned before the +ETag is needed: +[source,options="nowrap"] +---- +metadata-record-v2.json: +{ + "relatedResource": { + "identifier": "anyResourceId", + "identifierType": "INTERNAL" + }, + "schema": { + "identifier": "my_first_xsd", + "identifierType": "INTERNAL" + }, + "schemaVersion": "2", + "acl": [ { + "id":null, + "sid":"guest", + "permission":"READ" + } ] +} +---- +[source,options="nowrap"] +---- +metadata-v2.xml: +<?xml version='1.0' encoding='utf-8'?> +<example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My second XML document</example:title> + <example:date>2018-07-02</example:date> +</example:metadata> +---- +include::{snippets}/v2-update-metadata-record-v2/curl-request.adoc[] + +You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-metadata-record-v2/http-request.adoc[] + +You will get the new metadata record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. +include::{snippets}/v2-update-metadata-record-v2/http-response.adoc[] + +What you see is, that the metadata record looks different from the original document. + +==== Updating Metadata Record & Document +Repeat the last step and update to the current version. As mentioned before the +ETag is needed. As the ETag has changed in the meanwhile you first have to get the +new ETag. +include::{snippets}/v2-get-metadata-record-v2/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-metadata-record-v2/http-request.adoc[] + +You will get the new metadata record with the new ETag. +include::{snippets}/v2-get-metadata-record-v2/http-response.adoc[] + +Now you can update metadata due to new version of schema using the new Etag. +[source,options="nowrap"] +---- +metadata-record-v3.json: +{ + "relatedResource": { + "identifier": "anyResourceId", + "identifierType": "INTERNAL" + }, + "schema": { + "identifier": "my_first_xsd", + "identifierType": "INTERNAL" + }, + "schemaVersion": "3" +} +---- +[source,options="nowrap"] +---- +metadata-v3.xml: +<?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My third XML document</example:title> + <example:date>2018-07-02</example:date> + <example:note>since version 3 notes are allowed</example:note> +</example:metadata> +---- + +include::{snippets}/v2-update-metadata-record-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-metadata-record-v3/http-request.adoc[] + +You will get the new metadata record. +include::{snippets}/v2-update-metadata-record-v3/http-response.adoc[] + + +Now you can access the updated metadata via the URI in the HTTP response header. +include::{snippets}/v2-get-metadata-document-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-metadata-document-v3/http-request.adoc[] + +You will get the updated metadata. +include::{snippets}/v2-get-metadata-document-v3/http-response.adoc[] + + +==== Find a Metadata Record +Search will find all current metadata records. +There are some filters available which may be combined. +All filters for the metadata records are set via query parameters. +The following filters are allowed: + +- id +- resourceId +- from +- until + +[NOTE] +The header contains the field 'Content-Range" which displays delivered indices and the maximum number of available schema records. +If there are more than 20 metadata records registered you have to provide page and/or size as additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** +- size: Number of entries per page. + +==== Getting a List of all Metadata Records for a Specific Metadata Document + +If you want to obtain all versions of a specific resource you may add 'id' as +a filter parameter. This may look like this: + +include::{snippets}/v2-list-all-versions-of-metadata-document/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-list-all-versions-of-metadata-document/http-request.adoc[] + +As a result, you receive a list of metadata records in descending order. +(current version first) + +include::{snippets}/v2-list-all-versions-of-metadata-document/http-response.adoc[] + +===== Find by resourceId +If you want to find all records belonging to an external resource. MetaStore may +hold multiple metadata documents per resource. (Nevertheless only one per registered +schema) + +Command line: +include::{snippets}/v2-find-metadata-record-resource/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-find-metadata-record-resource/http-request.adoc[] + +You will get the current version of the metadata record(s). +include::{snippets}/v2-find-metadata-record-resource/http-response.adoc[] + +===== Find after a specific date +If you want to find all metadata records updated after a specific date. + +Command line: +include::{snippets}/v2-find-metadata-record-from/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-find-metadata-record-from/http-request.adoc[] + +You will get the current version metadata records updated ln the last 2 hours. +include::{snippets}/v2-find-metadata-record-from/http-response.adoc[] + +===== Find in a specific date range +If you want to find all metadata records updated in a specific date range. + +Command line: +include::{snippets}/v2-find-metadata-record-from-to/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-find-metadata-record-from-to/http-request.adoc[] + +You will get an empty array as no metadata record exists in the given range: +include::{snippets}/v2-find-metadata-record-from-to/http-response.adoc[] + + +[[JSON]] +== JSON (Schema) +[[ChapterMetadataSchemaHandling4Json]] +=== Schema Registration and Management + +In this section, the handling of json schema resources is explained. It all starts with creating your first json schema resource. The model of a metadata schema record looks +like this: +[source,options="nowrap"] +---- +{ + "schemaId" : "...", + "schemaVersion" : 1, + "mimeType" : "...", + "type" : "...", + "createdAt" : "...", + "lastUpdate" : "...", + "acl" : [ { + "id" : 1, + "sid" : "...", + "permission" : "..." + } ], + "licenseUri" : "...", + "schemaDocumentUri" : "...", + "schemaHash" : "...", + "locked" : false +} +---- +At least the following elements are expected to be provided by the user: + +[point] +- schemaId: A unique label for the schema. +- mimeType: The resource type must be assigned by the user. For JSON schemas this should be 'application/json' +- type: XML or JSON. For JSON schemas this should be 'JSON' + +In addition, ACL may be useful to make schema editable by others. (This will be of interest while updating an existing schema) + +[NOTE] +License URI is optional. It's new since 1.5.0. + +=== Registering a Metadata Schema Document + +The following example shows the creation of the first json schema only providing mandatory fields mentioned above: +[source,options="nowrap"] +---- +schema-record4json.json: +{ + "schemaId" : "my_first_json", + "type" : "JSON" +} +---- +[source,options="nowrap"] +---- +schema.json: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + } + }, + "additionalProperties": false +} +---- + +include::{snippets}/v2-register-json-schema/curl-request.adoc[] + +You can see, that most of the sent metadata schema record is empty. Only schemaId, mimeType and type are provided by the user. HTTP-wise the call looks as follows: + +include::{snippets}/v2-register-json-schema/http-request.adoc[] + +As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: + +include::{snippets}/v2-register-json-schema/http-response.adoc[] + +What you see is, that the metadata schema record looks different from the original document. All remaining elements received a value by the server. +Furthermore, you'll find an ETag header with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for +all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. + +==== Getting a Metadata Schema Record + +For obtaining one metadata schema record you have to provide the value of the field 'schemaId'. +[NOTE] +As 'Accept' field you have to provide 'application/vnd.datamanager.schema-record+json' otherwise you will +get the metadata schema instead. + +include::{snippets}/v2-get-json-schema-record/curl-request.adoc[] + +In the actual HTTP request just access the path of the resource using the base path and the 'schemaid'. +Be aware that you also have to provide the 'Accept' field. + +include::{snippets}/v2-get-json-schema-record/http-request.adoc[] + +As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. + +include::{snippets}/v2-get-json-schema-record/http-response.adoc[] + +==== Getting a Metadata Schema Document + +For obtaining accessible metadata schemas you also have to provide the 'schemaId'. +For accessing schema document you don't have to provide the 'Accept' header. + +include::{snippets}/v2-get-json-schema-document/curl-request.adoc[] + +In the actual HTTP request there is nothing special. You just access the path of the resource using the base path and the 'schemaId'. + +include::{snippets}/v2-get-json-schema-document/http-request.adoc[] + +As a result, you receive the XSD schema send before. + +include::{snippets}/v2-get-json-schema-document/http-response.adoc[] + +==== Updating a Metadata Schema Document (add mandatory 'date' field) + +[NOTE] +Updating a metadata schema document will not break old metadata documents. +As every update results in a new version 'old' metadata schema documents are still +available. + +For updating an existing metadata schema (record) a valid ETag is needed. The actual ETag +is available via the HTTP GET call of the metadata schema record. (see above) +Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. + +[source,options="nowrap"] +---- +schema-v2.json: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title", + "date" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + }, + "date": { + "type": "string", + "format": "date", + "title": "Date", + "description": "Date of object" + } + }, + "additionalProperties": false +} +---- + +include::{snippets}/v2-update-json-schema-v2/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-json-schema-v2/http-request.adoc[] + +As a result, you receive the updated schema record and in the HTTP response header +the new location URL and the ETag. + +include::{snippets}/v2-update-json-schema-v2/http-response.adoc[] + +==== Updating a Metadata Schema Document (add optional 'note' field) + +For updating existing metadata schema document we have to provide the new ETag. +Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. + +[source,options="nowrap"] +---- +schema-v3.json: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title", + "date" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + }, + "date": { + "type": "string", + "format": "date", + "title": "Date", + "description": "Date of object" + }, + "note": { + "type": "string", + "title": "Note", + "description": "Additonal information about object" + } + }, + "additionalProperties": false +} +---- + +include::{snippets}/v2-update-json-schema-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-json-schema-v3/http-request.adoc[] + +As a result, you receive the updated schema record and in the HTTP response header +the new location URL and the ETag. + +include::{snippets}/v2-update-json-schema-v3/http-response.adoc[] + +The updated schema record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. + +=== Registering another Metadata Schema Document + +The following example shows the creation of another json schema only providing mandatory fields mentioned above: +[source,options="nowrap"] +---- +another-schema-record4json.json: +{ + "schemaId" : "another_json", + "type" : "JSON" +} +---- +[source,options="nowrap"] +---- +another-schema.json: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json/example", + "type": "object", + "title": "Another Json schema for tests", + "default": {}, + "required": [ + "description" + ], + "properties": { + "description": { + "type": "string", + "title": "Description", + "description": "Any description." + } + }, + "additionalProperties": false +} +---- + +include::{snippets}/v2-register-another-json-schema/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-register-another-json-schema/http-request.adoc[] + +As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: + +include::{snippets}/v2-register-another-json-schema/http-response.adoc[] + +Now there are two schemaIds registered in the metadata schema registry. + +==== Getting a List of Metadata Schema Records + +Obtaining all accessible metadata schema records. + +include::{snippets}/v2-get-all-json-schemas/curl-request.adoc[] + +Same for HTTP request: + +include::{snippets}/v2-get-all-json-schemas/http-request.adoc[] + +As a result, you receive a list of metadata schema records. + +include::{snippets}/v2-get-all-json-schemas/http-response.adoc[] + +[NOTE] +Only the current version of each schemaId is listed. + +[NOTE] +The header contains the field 'Content-Range" which displays delivered indices and the maximum number of available schema records. +If there are more than 20 schemas registered you have to provide page and/or size as additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** +- size: Number of entries per page. + +The modified HTTP request with pagination looks like follows: + +include::{snippets}/v2-get-all-json-schemas-pagination/http-request.adoc[] + +==== Getting a List of all Schema Records for a Specific SchemaId + +If you want to obtain all versions of a specific schema you may add the schemaId as +a filter parameter. This may look like this: + +include::{snippets}/v2-get-all-versions-of-a-json-schema/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-all-versions-of-a-json-schema/http-request.adoc[] + +As a result, you receive a list of metadata schema records in descending order. +(current version first) + +include::{snippets}/v2-get-all-versions-of-a-json-schema/http-response.adoc[] + +==== Getting current Version of Metadata Schema Document + +To get the current version of the metadata schema document just send an HTTP GET with the linked +'schemaId': + +include::{snippets}/v2-get-json-schema-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-json-schema-v3/http-request.adoc[] + +As a result, you receive the XSD schema document sent before. + +include::{snippets}/v2-get-json-schema-v3/http-response.adoc[] + +==== Getting a specific Version of Metadata Schema Document + +To get a specific version of the metadata schema document just send an HTTP GET with the linked +'schemaId' and the version number you are looking for as query parameter: + +include::{snippets}/v2-get-json-schema-v1/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-json-schema-v1/http-request.adoc[] + +As a result, you receive the initial XSD schema document (version 1). + +include::{snippets}/v2-get-json-schema-v1/http-response.adoc[] + +==== Validating Metadata Document + +Before an ingest of metadata is made the metadata should be successfully validated. Otherwise +the ingest may be rejected. Select the schema and the schemaVersion to validate given document. +[source,options="nowrap"] +---- +metadata-v3.json: +{ +"title": "My third JSON document", +"date": "2018-07-02", +"note": "since version 3 notes are allowed" +} +---- + +On a first step validation with the old schema will be done: + +include::{snippets}/v2-validate-json-document-v1/curl-request.adoc[] + +Same for the HTTP request. The schemaVersion number is set by a query parameter. + +include::{snippets}/v2-validate-json-document-v1/http-request.adoc[] + +As a result, you receive 422 as HTTP status and an error message holding some information about the error. +(Unfortunately not documented here due to technical reasons.) + +include::{snippets}/v2-validate-json-document-v1/http-response.adoc[] + +The document holds a mandatory and an optional field introduced in the second and third version of schema. +Let's try to validate with third version of schema. Only version number will be different. (if no query +parameter is available the current version will be selected) + +include::{snippets}/v2-validate-json-document-v3/curl-request.adoc[] + +Same for the HTTP request. + +include::{snippets}/v2-validate-json-document-v3/http-request.adoc[] + +Everything should be fine now. As a result, you receive 204 as HTTP status and no further content. + +include::{snippets}/v2-validate-json-document-v3/http-response.adoc[] + +==== Update Metadata Schema Record +In case of authorization it may be neccessary to update metadata record to be accessible +by others. To do so an update has to be made. In this example we introduce a +user called 'admin' and give him all rights. + +[source,options="nowrap"] +---- +schema-record4json-v4.json +{ + "schemaId" : "my_first_json", + "mimeType" : "application/json", + "type" : "JSON", + "acl" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + }, { + "id" : 3, + "sid" : "admin", + "permission" : "ADMINISTRATE" + } ] +} +---- +include::{snippets}/v2-update-json-schema-record/curl-request.adoc[] + +Same for the HTTP request. + +include::{snippets}/v2-update-json-schema-record/http-request.adoc[] + +As a result, you receive 200 as HTTP status, the updated metadata schema record and the +updated ETag and location in the HTTP response header. + +include::{snippets}/v2-update-json-schema-record/http-response.adoc[] + +After the update the following fields has changed: + +- schemaVersion number increased by one. +- lastUpdate to the date of the last update (set by server) +- acl additional ACL entry (set during update) + +=== Metadata Management +After registration of a schema metadata may be added to MetaStore. +In this section, the handling of metadata resources is explained. +It all starts with creating your first metadata resource. The model of a metadata record looks +like this: +[source,options="nowrap"] +---- +{ + "id": "...", + "relatedResource": { + "identifier": "...", + "identifierType": "..." + }, + "createdAt": "...", + "lastUpdate": "...", + "schema": { + "identifier": "...", + "identifierType": "..." + }, + "schemaVersion": 1, + "recordVersion": 1, + "acl": [{ + "id": 3, + "sid": "...", + "permission": "..." + }], + "licenseUri": "...", + "metadataDocumentUri": "...", + "documentHash": "..." +} +---- +At least the following elements are expected to be provided by the user: + +[point] +- schema: Identifier of the linked schema. (INTERNAL and URL supported as type) +- relatedResource: The link to the resource. + +In addition, ACL may be useful to make metadata editable by others. (This will be of interest while updating an existing metadata) + +[NOTE] +License URI is optional. It's new since 1.5.0. + +==== Register/Ingest a Metadata Record with Metadata Document + +The following example shows the creation of the first metadata record and its metadata only providing mandatory fields mentioned above: +[source,options="nowrap"] +---- +metadata-record4json.json: +{ + "relatedResource": { + "identifier": "anyResourceId", + "identifierType": "INTERNAL" + }, + "schema": { + "identifier": "my_first_json", + "identifierType": "INTERNAL" + }, + "schemaVersion": 1 +} +---- +[source,options="nowrap"] +---- +metadata.json: +{ + "title": "My first JSON document" +} +---- +The schemaId used while registering metadata schema has to be used to link the metadata with the +approbriate metadata schema. +include::{snippets}/v2-ingest-json-metadata-document/curl-request.adoc[] + +You can see, that most of the sent metadata schema record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: + +include::{snippets}/v2-ingest-json-metadata-document/http-request.adoc[] + +As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: + +include::{snippets}/v2-ingest-json-metadata-document/http-response.adoc[] + +What you see is, that the metadata record looks different from the original document. All remaining elements received a value by the server. +In the header you'll find a location URL to access the ingested metadata and an ETag with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for +all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. + +==== Accessing Metadata Document +For accessing the metadata the location URL provided before may be used. +The URL is compiled by the id of the metadata and its version. +include::{snippets}/v2-get-json-metadata-document/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-json-metadata-document/http-request.adoc[] + +The linked metadata will be returned. The result is sent back to the user and will look that way: + +include::{snippets}/v2-get-json-metadata-document/http-response.adoc[] + +What you see is, that the metadata is untouched. + +==== Accessing Metadata Record +For accessing the metadata record the same URL as before has to be used. +The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". +Then the command line looks like this: +include::{snippets}/v2-get-json-metadata-record/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-json-metadata-record/http-request.adoc[] + +The linked metadata will be returned. The result is sent back to the user and will look that way: + +include::{snippets}/v2-get-json-metadata-record/http-response.adoc[] + +You also get the metadata record seen before. + + +==== Updating a Metadata Record (edit ACL entries) + +The following example shows the update of the metadata record. As mentioned before the +ETag is needed: +[source,options="nowrap"] +---- +metadata-record4json-v2.json: +{ + "relatedResource": { + "identifier": "anyResourceId", + "identifierType": "INTERNAL" + }, + "schema": { + "identifier": "my_first_json", + "identifierType": "INTERNAL" + }, + "schemaVersion": "2", + "acl": [ { + "id":null, + "sid":"guest", + "permission":"READ" + } ] +} +---- +[source,options="nowrap"] +---- +metadata-v2.json: +{ +"title": "My second JSON document", +"date": "2018-07-02" +} +---- +include::{snippets}/v2-update-json-metadata-record-v2/curl-request.adoc[] + +You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-json-metadata-record-v2/http-request.adoc[] + +You will get the new metadata record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. +include::{snippets}/v2-update-json-metadata-record-v2/http-response.adoc[] + +What you see is, that the metadata record looks different from the original document. + +==== Updating Metadata Record & Document +Repeat the last step and update to the current version. As mentioned before the +ETag is needed. As the ETag has changed in the meanwhile you first have to get the +new ETag. +include::{snippets}/v2-get-json-metadata-record-v2/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-json-metadata-record-v2/http-request.adoc[] + +You will get the new metadata record with the new ETag. +include::{snippets}/v2-get-json-metadata-record-v2/http-response.adoc[] + +Now you can update metadata due to new version of schema using the new Etag. +[source,options="nowrap"] +---- +metadata-record4json-v3.json: +{ + "relatedResource": { + "identifier": "anyResourceId", + "identifierType": "INTERNAL" + }, + "schema": { + "identifier": "my_first_json", + "identifierType": "INTERNAL" + }, + "schemaVersion": "3" +} +---- +[source,options="nowrap"] +---- +metadata-v3.json: +{ +"title": "My third JSON document", +"date": "2018-07-02", +"note": "since version 3 notes are allowed" +} +---- + +include::{snippets}/v2-update-json-metadata-record-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-update-json-metadata-record-v3/http-request.adoc[] + +You will get the new metadata record. +include::{snippets}/v2-update-json-metadata-record-v3/http-response.adoc[] + + +Now you can access the updated metadata via the URI in the HTTP response header. +include::{snippets}/v2-get-json-metadata-document-v3/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-get-json-metadata-document-v3/http-request.adoc[] + +You will get the updated metadata. +include::{snippets}/v2-get-json-metadata-document-v3/http-response.adoc[] + + +==== Find a Metadata Record +Search will find all current metadata records. +There are some filters available which may be combined. +All filters for the metadata records are set via query parameters. +The following filters are allowed: + +- id +- resourceId +- from +- until + +[NOTE] +The header contains the field 'Content-Range" which displays delivered indices and the maximum number of available schema records. +If there are more than 20 metadata records registered you have to provide page and/or size as additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** +- size: Number of entries per page. + +==== Getting a List of all Metadata Records for a Specific Metadata Document + +If you want to obtain all versions of a specific resource you may add 'id' as +a filter parameter. This may look like this: + +include::{snippets}/v2-list-all-versions-of-json-metadata-document/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-list-all-versions-of-json-metadata-document/http-request.adoc[] + +As a result, you receive a list of metadata records in descending order. +(current version first) + +include::{snippets}/v2-list-all-versions-of-json-metadata-document/http-response.adoc[] + +===== Find by resourceId +If you want to find all records belonging to an external resource. MetaStore may +hold multiple metadata documents per resource. (Nevertheless only one per registered +schema) + +Command line: +include::{snippets}/v2-find-json-metadata-record-resource/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-find-json-metadata-record-resource/http-request.adoc[] + +You will get the current version metadata record. +include::{snippets}/v2-find-json-metadata-record-resource/http-response.adoc[] + +===== Find after a specific date +If you want to find all metadata records updated after a specific date. + +Command line: +include::{snippets}/v2-find-json-metadata-record-from/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-find-json-metadata-record-from/http-request.adoc[] + +You will get the current version metadata records updated ln the last 2 hours. +include::{snippets}/v2-find-json-metadata-record-from/http-response.adoc[] + +===== Find in a specific date range +If you want to find all metadata records updated in a specific date range. + +Command line: +include::{snippets}/v2-find-json-metadata-record-from-to/curl-request.adoc[] + +HTTP-wise the call looks as follows: + +include::{snippets}/v2-find-json-metadata-record-from-to/http-request.adoc[] + +You will get an empty array as no metadata record exists in the given range: +include::{snippets}/v2-find-json-metadata-record-from-to/http-response.adoc[] + + + +== Remarks on Working with Versions + +While working with versions you should keep some particularities in mind. Access to version is only possible for single resources. There is e.g. no way to obtain all resources in version 2 from the server. +If a specific version of a resource is returned, the obtained ETag also relates to this specific version. Therefore, you should NOT use this ETag for any update operation as the operation will fail with response +code 412 (PRECONDITION FAILED). Consequently, it is also NOT allowed to modify a format version of a resource. If you want to rollback to a previous version, you should obtain the resource and submit a PUT request +of the entire document which will result in a new version equal to the previous state unless there were changes you are not allowed to apply (anymore), e.g. if permissions have changed. diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java index 28fc1255..88be93e7 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java @@ -254,7 +254,10 @@ public void documentSchemaRegistry4Json() throws Exception { //************************************************************************** schemaRecord.setId(EXAMPLE_SCHEMA_ID); SchemaRegistryControllerTestV2.setTitle(schemaRecord, "Title for " + EXAMPLE_SCHEMA_ID); -// schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); + SchemaRegistryControllerTestV2.setComment(schemaRecord, "Comment for " + EXAMPLE_SCHEMA_ID); + SchemaRegistryControllerTestV2.setDefinition(schemaRecord, "Definition for " + EXAMPLE_SCHEMA_ID); + SchemaRegistryControllerTestV2.setLabel(schemaRecord, "Labels for " + EXAMPLE_SCHEMA_ID); + ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); @@ -315,7 +318,7 @@ public void documentSchemaRegistry4Json() throws Exception { andExpect(status().isOk()). andReturn(); etag = result.getResponse().getHeader("ETag"); - String exampleSchemaV3 = result.getResponse().getHeader("ETag"); + String exampleSchemaV3 = result.getResponse().getHeader("location"); // 6. Registering another metadata schema //************************************************************************** schemaRecord.setId(ANOTHER_SCHEMA_ID); diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java new file mode 100644 index 00000000..7917d9fb --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java @@ -0,0 +1,558 @@ +/* + * Copyright 2018 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.documentation; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.test.SchemaRegistryControllerTestV2; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Comparator; +import java.util.stream.Stream; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.restdocs.operation.preprocess.Preprocessors; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + * + */ +//@ActiveProfiles("doc") +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41425"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_xml_doc;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/restdocu/xml/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/restdocu/xml/metadata"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@TestPropertySource(properties = {"server.error.include-message=always"}) +public class SchemaRegistryControllerDocumentationTestV2 { + + private MockMvc mockMvc; + @Autowired + private WebApplicationContext context; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + private final static String EXAMPLE_SCHEMA_ID = "my_first_xsd"; + private final static String ANOTHER_SCHEMA_ID = "another_xsd"; + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/restdocu/xml/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private final static String SCHEMA_V1 = "<xs:schema targetNamespace=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" + + " elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n" + + "\n" + + "<xs:element name=\"metadata\">\n" + + " <xs:complexType>\n" + + " <xs:sequence>\n" + + " <xs:element name=\"title\" type=\"xs:string\"/>\n" + + " </xs:sequence>\n" + + " </xs:complexType>\n" + + "</xs:element>\n" + + "\n" + + "</xs:schema>"; + private final static String SCHEMA_V2 = "<xs:schema targetNamespace=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" + + " elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n" + + "\n" + + "<xs:element name=\"metadata\">\n" + + " <xs:complexType>\n" + + " <xs:sequence>\n" + + " <xs:element name=\"title\" type=\"xs:string\"/>\n" + + " <xs:element name=\"date\" type=\"xs:date\"/>\n" + + " </xs:sequence>\n" + + " </xs:complexType>\n" + + "</xs:element>\n" + + "\n" + + "</xs:schema>"; + private final static String SCHEMA_V3 = "<xs:schema targetNamespace=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" + + " elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n" + + "\n" + + "<xs:element name=\"metadata\">\n" + + " <xs:complexType>\n" + + " <xs:sequence>\n" + + " <xs:element name=\"title\" type=\"xs:string\"/>\n" + + " <xs:element name=\"date\" type=\"xs:date\"/>\n" + + " <xs:element name=\"note\" type=\"xs:string\" minOccurs=\"0\"/>\n" + + " </xs:sequence>\n" + + " </xs:complexType>\n" + + "</xs:element>\n" + + "\n" + + "</xs:schema>"; + + private final static String ANOTHER_SCHEMA = "<xs:schema targetNamespace=\"http://www.example.org/schema/xsd/example\"\n" + + " xmlns=\"http://www.example.org/schema/xsd/example\"\n" + + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" + + " elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n" + + "\n" + + "<xs:element name=\"metadata\">\n" + + " <xs:complexType>\n" + + " <xs:sequence>\n" + + " <xs:element name=\"description\" type=\"xs:string\"/>\n" + + " </xs:sequence>\n" + + " </xs:complexType>\n" + + "</xs:element>\n" + + "\n" + + "</xs:schema>"; + + private final static String DOCUMENT_V1 = "<?xml version='1.0' encoding='utf-8'?>\n" + + "<example:metadata xmlns:example=\"http://www.example.org/schema/xsd/\" >\n" + + " <example:title>My first XML document</example:title>\n" + + "</example:metadata>"; + + private final static String DOCUMENT_V2 = "<?xml version='1.0' encoding='utf-8'?>\n" + + "<example:metadata xmlns:example=\"http://www.example.org/schema/xsd/\" >\n" + + " <example:title>My second XML document</example:title>\n" + + " <example:date>2018-07-02</example:date>\n" + + "</example:metadata>"; + + private final static String DOCUMENT_V3 = "<?xml version='1.0' encoding='utf-8'?>\n" + + "<example:metadata xmlns:example=\"http://www.example.org/schema/xsd/\" >\n" + + " <example:title>My third XML document</example:title>\n" + + " <example:date>2018-07-02</example:date>\n" + + " <example:note>since version 3 notes are allowed</example:note>\n" + + "</example:metadata>"; + private static final ResourceIdentifier RELATED_RESOURCE = ResourceIdentifier.factoryUrlResourceIdentifier("https://repo/anyResourceId"); + + @Before + public void setUp() throws JsonProcessingException { + try { + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation) + .uris().withPort(8040).and() + .operationPreprocessors() + .withRequestDefaults(prettyPrint()) + .withResponseDefaults(Preprocessors.removeHeaders("X-Content-Type-Options", "X-XSS-Protection", "X-Frame-Options"), prettyPrint())) + .build(); + } + + /** + * Workflow for documentation: // 1. Registering metadata schema // 2. Getting + * metadata Schema Record // 3. Getting metadata Schema document // 4. Update + * to second version of schema // 5. Update to third version of schema // 6. + * Registering another metadata schema // 7. List all schemas (only 1 Schema) + * // 8. List all versions of a schema // 9. Getting current schema // 10. + * Getting specific version of a schema // 11. Validate metadata document // + * 11 a) Validate with version=1 // 11 b) Validate without version // 12. + * Update metadata Schema Record // Metadata management // 1. Ingest Metadata + * document // 2. Accessing metadata document // 3. Accessing metadata record + * // 4. Update metadata record & document // 5. Update metadata record // 6. + * List all Versions of a record // 7. Find a metadata record. + * + * @throws Exception + */ + @Test + public void documentSchemaRegistry() throws Exception { + DataResource schemaRecord = new DataResource(); + String contextPath = "/metastore"; + String endpointSchema = contextPath + "/api/v2/schemas/"; + String endpointMetadata = contextPath + "/api/v2/metadata/"; + // 1. Registering metadata schema + //************************************************************************** + schemaRecord.setId(EXAMPLE_SCHEMA_ID); + SchemaRegistryControllerTestV2.setTitle(schemaRecord, "Title for " + EXAMPLE_SCHEMA_ID); + SchemaRegistryControllerTestV2.setComment(schemaRecord, "Comment for " + EXAMPLE_SCHEMA_ID); + SchemaRegistryControllerTestV2.setDefinition(schemaRecord, "Definition for " + EXAMPLE_SCHEMA_ID); + SchemaRegistryControllerTestV2.setLabel(schemaRecord, "Labels for " + EXAMPLE_SCHEMA_ID); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", SCHEMA_V1.getBytes()); + MockMultipartFile recordFile = new MockMultipartFile("record", "schema-record.json", "application/json", new ByteArrayInputStream(mapper.writeValueAsString(schemaRecord).getBytes())); + + //create resource and obtain location from response header + String location = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema). + file(schemaFile). + file(recordFile). + contextPath(contextPath)). + andDo(document("v2-register-schema")). + andExpect(status().isCreated()). + andReturn().getResponse().getHeader("Location"); + String exampleSchemaV1 = location; + + Assert.assertNotNull(location); + // 2. Getting metadata Schema Record + //************************************************************************** + // Get single metadata schema record + String etag = this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(document("v2-get-schema-record")). + andExpect(status().isOk()). + andReturn().getResponse().getHeader("ETag"); + + // 3. Getting metadata Schema document + //************************************************************************** + // Get metadata schema + this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath)). + andDo(document("v2-get-schema-document")). + andExpect(status().isOk()). + andReturn().getResponse(); + // 4. Update to second version of schema + //************************************************************************** + //update schema document and create new version + schemaFile = new MockMultipartFile("schema", "schema-v2.xsd", "application/xml", SCHEMA_V2.getBytes()); + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + file(schemaFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(document("v2-update-schema-v2")). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + String exampleSchemaV2 = result.getResponse().getHeader("location"); + + // 5. Update to third version of schema + //************************************************************************** + schemaFile = new MockMultipartFile("schema", "schema-v3.xsd", "application/xml", SCHEMA_V3.getBytes()); + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + file(schemaFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(document("v2-update-schema-v3")). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + String exampleSchemaV3 = result.getResponse().getHeader("location"); + // 6. Registering another metadata schema + //************************************************************************** + schemaRecord.setId(ANOTHER_SCHEMA_ID); + + schemaFile = new MockMultipartFile("schema", "another-schema.xsd", "application/xml", ANOTHER_SCHEMA.getBytes()); + recordFile = new MockMultipartFile("record", "another-schema-record.json", "application/json", new ByteArrayInputStream(mapper.writeValueAsString(schemaRecord).getBytes())); + + location = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema). + file(schemaFile). + file(recordFile). + contextPath(contextPath)). + andDo(document("v2-register-another-schema")). + andExpect(status().isCreated()). + andReturn().getResponse().getHeader("Location"); + + Assert.assertNotNull(location); + // 7. List all schema records (only current schemas) + //************************************************************************** + this.mockMvc.perform(get(endpointSchema). + contextPath(contextPath)). + andDo(document("v2-get-all-schemas")). + andExpect(status().isOk()). + andReturn().getResponse(); + + this.mockMvc.perform(get(endpointSchema). + contextPath(contextPath). + param("page", Integer.toString(0)). + param("size", Integer.toString(20))). + andDo(document("v2-get-all-schemas-pagination")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 8. List all versions of a schema + //************************************************************************** + this.mockMvc.perform(get(endpointSchema). + contextPath(contextPath). + param("schemaId", EXAMPLE_SCHEMA_ID)). + andDo(document("v2-get-all-versions-of-a-schema")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 9. Getting current schema + //************************************************************************** + this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath)). + andDo(document("v2-get-schema-v3")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 10. Getting specific version of a schema + //************************************************************************** + this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath). + param("version", "1")). + andDo(document("v2-get-schema-v1")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 11. Validate metadata document + //************************************************************************** + MockMultipartFile metadataFile_v3 = new MockMultipartFile("document", "metadata-v3.xml", "application/xml", DOCUMENT_V3.getBytes()); + // 11 a) Validate with version=1 --> invalid + //************************************************************************** + this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID + "/validate"). + file(metadataFile_v3). + contextPath(contextPath). + queryParam("version", "1")). + andDo(document("v2-validate-document-v1")). + andExpect(status().isUnprocessableEntity()). + andReturn().getResponse(); + // 11 b) Validate without version --> version 3 (should be valid) + //************************************************************************** + this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID + "/validate"). + file(metadataFile_v3). + contextPath(contextPath)). + andDo(document("v2-validate-document-v3")). + andExpect(status().isNoContent()). + andReturn().getResponse(); + // 12. Update metadata Schema Record + //************************************************************************** + // Update metadata record to allow admin to edit schema as well. + result = this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + mapper = new ObjectMapper(); + schemaRecord = mapper.readValue(body, DataResource.class); + schemaRecord.getAcls().add(new AclEntry("admin", PERMISSION.ADMINISTRATE)); + + recordFile = new MockMultipartFile("record", "schema-record-v4.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + file(recordFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(document("v2-update-schema-record")). + andExpect(status().isOk()). + andReturn().getResponse(); + + //************************************************************************** + // Metadata management + //************************************************************************** + // 1. Ingest metadata document + //************************************************************************** + // Create a metadata record. + DataResource metadataRecord = new DataResource(); + SchemaRegistryControllerTestV2.setTitle(metadataRecord, "Title of first XML metadata document"); + metadataRecord.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.XML_METADATA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); + +// record.setId("my_id"); + SchemaRegistryControllerTestV2.setRelatedResource(metadataRecord, RELATED_RESOURCE.getIdentifier()); + SchemaRegistryControllerTestV2.setRelatedSchema(metadataRecord, exampleSchemaV1); + + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(metadataRecord).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DOCUMENT_V1.getBytes()); + + location = this.mockMvc.perform(MockMvcRequestBuilders.multipart(endpointMetadata). + file(recordFile). + file(metadataFile). + contextPath(contextPath)). + andDo(document("v2-ingest-metadata-document")). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn().getResponse().getHeader("Location"); + // Get URL + String newLocation = location.split("[?]")[0]; + + // 2. Accessing metadata document + //************************************************************************** + this.mockMvc.perform(get(location).accept("application/xml"). + contextPath(contextPath)). + andDo(document("v2-get-metadata-document")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 3. Accessing metadata record + //************************************************************************** + this.mockMvc.perform(get(location). + contextPath(contextPath). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(document("v2-get-metadata-record")). + andExpect(status().isOk()). + andReturn().getResponse(); + + // 4. Update metadata record & document + //************************************************************************** + result = this.mockMvc.perform(get(location). + contextPath(contextPath). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + body = result.getResponse().getContentAsString(); + + mapper = new ObjectMapper(); + DataResource record = mapper.readValue(body, DataResource.class); + SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV2); + recordFile = new MockMultipartFile("record", "metadata-record-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); + metadataFile = new MockMultipartFile("document", "metadata-v2.xml", "application/xml", DOCUMENT_V2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(newLocation). + file(recordFile). + file(metadataFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(print()). + andDo(document("v2-update-metadata-record-v2")). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + location = result.getResponse().getHeader("Location"); + // 5. Update metadata record + //************************************************************************** + // update once more to newest version of schema + // Get Etag + this.mockMvc.perform(get(location). + contextPath(contextPath). + accept(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(document("v2-get-metadata-record-v2")). + andExpect(status().isOk()). + andReturn().getResponse(); + SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV3); + recordFile = new MockMultipartFile("record", "metadata-record-v3.json", "application/json", mapper.writeValueAsString(record).getBytes()); + metadataFile = new MockMultipartFile("document", "metadata-v3.xml", "application/xml", DOCUMENT_V3.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(newLocation). + file(recordFile). + file(metadataFile). + contextPath(contextPath). + header("If-Match", etag).with(putMultipart())). + andDo(print()). + andDo(document("v2-update-metadata-record-v3")). + andExpect(status().isOk()). + andReturn(); + location = result.getResponse().getHeader("Location"); + this.mockMvc.perform(get(location). + contextPath(contextPath)). + andDo(document("v2-get-metadata-document-v3")). + andExpect(status().isOk()). + andReturn().getResponse(); + // 6. List all versions of a record + //************************************************************************** + String resourceId = record.getId(); + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("id", resourceId)). + andDo(print()). + andDo(document("v2-list-all-versions-of-metadata-document")). + andExpect(status().isOk()). + andReturn(); + + // 7. Find a metadata record. + //************************************************************************** + // find all metadata for a resource + Instant oneHourBefore = Instant.now().minusSeconds(3600); + Instant twoHoursBefore = Instant.now().minusSeconds(7200); + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("resoureId", RELATED_RESOURCE.getIdentifier())). + andDo(print()). + andDo(document("v2-find-metadata-record-resource")). + andExpect(status().isOk()). + andReturn(); + + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("from", twoHoursBefore.toString())). + andDo(print()). + andDo(document("v2-find-metadata-record-from")). + andExpect(status().isOk()). + andReturn(); + + this.mockMvc.perform(get(endpointMetadata). + contextPath(contextPath). + param("from", twoHoursBefore.toString()). + param("until", oneHourBefore.toString())). + andDo(print()). + andDo(document("v2-find-metadata-record-from-to")). + andExpect(status().isOk()). + andReturn(); + + } + + private static RequestPostProcessor putMultipart() { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setMethod("PUT"); + return request; + }; + } + +} diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 2ee794b6..f05f8074 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -13,7 +13,6 @@ import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.domain.SchemaRecord; @@ -51,7 +50,6 @@ import java.util.Locale; import java.util.Set; import java.util.stream.Stream; -import net.bytebuddy.agent.VirtualMachine; import org.hamcrest.Matchers; import org.junit.Assert; import static org.junit.Assert.assertEquals; @@ -2544,6 +2542,54 @@ public static void setDefinition(DataResource record, String definition) { } } + public static String getLabel(DataResource record) { + String returnValue = null; + for (Description item : record.getDescriptions()) { + if (item.getType() == Description.TYPE.ABSTRACT) { + returnValue = item.getDescription(); + break; + } + } + return returnValue; + } + + public static void setLabel(DataResource record, String label) { + boolean addDefinition = true; + for (Description item : record.getDescriptions()) { + if (item.getType() == Description.TYPE.ABSTRACT) { + item.setDescription(label); + addDefinition = false; + break; + } + } + if (addDefinition) { + record.getDescriptions().add(Description.factoryDescription(label, Description.TYPE.ABSTRACT)); + } + } + + public static String getRights(DataResource record) { + String returnValue = null; + for (Scheme item : record.getRights()) { + returnValue = item.getSchemeId(); + break; + } + return returnValue; + } + + public static void setRights(DataResource record, String schemeId) { + boolean addDefinition = true; + for (Scheme item : record.getRights()) { + if (item.getSchemeId().equals(schemeId)) { + addDefinition = false; + break; + } + } + if (addDefinition) { + Scheme scheme = Scheme.factoryScheme(schemeId, "https://spdx.org/licenses/" + schemeId + ".html") + record.getDescriptions().add(Scheme..factoryDescription(label, Description.TYPE.ABSTRACT)); + } + } + public static String getComment(DataResource record) { String returnValue = null; for (Description item : record.getDescriptions()) { From 8e206a4ddae288cd6efb165df73768a091eeedb2 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 22 Apr 2024 08:51:38 +0200 Subject: [PATCH 029/181] Fix test for documentation --- .../metastore2/test/SchemaRegistryControllerTestV2.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index f05f8074..7ff21ad3 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -2585,8 +2585,8 @@ public static void setRights(DataResource record, String schemeId) { } } if (addDefinition) { - Scheme scheme = Scheme.factoryScheme(schemeId, "https://spdx.org/licenses/" + schemeId + ".html") - record.getDescriptions().add(Scheme..factoryDescription(label, Description.TYPE.ABSTRACT)); + Scheme scheme = Scheme.factoryScheme(schemeId, "https://spdx.org/licenses/" + schemeId + ".html"); + record.getRights().add(scheme); } } From 618853e0621aa1e4e1a63c6952e4e556ee3f7ade Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 22 Apr 2024 15:53:50 +0200 Subject: [PATCH 030/181] Next step towards documentation. --- docs/documentationV2.adoc | 227 ++++++++++-------- ...tryControllerDocumentation4JsonTestV2.java | 6 +- ...RegistryControllerDocumentationTestV2.java | 10 +- .../test/SchemaRegistryControllerTestV2.java | 3 +- 4 files changed, 140 insertions(+), 106 deletions(-) diff --git a/docs/documentationV2.adoc b/docs/documentationV2.adoc index ed0d4740..5b3bce67 100644 --- a/docs/documentationV2.adoc +++ b/docs/documentationV2.adoc @@ -35,14 +35,14 @@ All fields mandatory at creation time are explained in the resource creation exa === Building the URL The URL for accessing the MetaStore REST endpoints is constructed as follows: -1. Protocol (e.g., http, https) -2. Host name (e.g. localhost, www.example.org) -3. Port (e.g. 8040) -4. Context path (e.g. /metastore) -5. Endpoint (e.g. /api/v1/metadata) +1. Protocol (e.g.: http, https) +2. Host name (e.g.: localhost, www.example.org) +3. Port (e.g.: 8040) +4. Context path (e.g.: /metastore) +5. Endpoint (e.g.: /api/v2/metadata) For example, to list all the schema records in your local MetaStore, you need to -run the following in your browser: http://localhost:8040/metastore/api/v1/schemas +run the following in your browser: http://localhost:8040/metastore/api/v2/schemas/ In former versions (< 1.3.0), no context path was provided by default. @@ -51,49 +51,75 @@ In former versions (< 1.3.0), no context path was provided by default. [[ChapterMetadataSchemaHandling4Xml]] === Schema Registration and Management -In this first section, the handling of schema resources is explained. It all starts with creating your first xml schema resource. The model of a metadata schema record looks +In this first section, the handling of schema resources is explained. It all starts with creating your first xml schema resource. The model of a datacite record looks like this: [source,options="nowrap"] ---- { - "schemaId" : "...", - "schemaVersion" : 1, - "mimeType" : "...", - "type" : "...", - "createdAt" : "...", + "id" : "...", + "identifier" : { + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "givenName" : "..." + } ], + "titles" : [ { + "value" : "..." + } ], + "publisher" : "...", + "publicationYear" : "...", + "resourceType" : { + "value" : "...", + "typeGeneral" : "..." + }, + "dates" : [ { + "value" : "...", + "type" : "..." + } ], + "relatedIdentifiers" : [ { + "value" : "...", + "identifierType" : "...", + "relationType" : "..." + }} ], + "alternateIdentifiers" : [ { + "value" : "...", + "identifierType" : "..." + } ], + "version" : "...", "lastUpdate" : "...", - "acl" : [ { - "id" : 1, + "state" : "...", + "acls" : [ { "sid" : "...", "permission" : "..." - } ], - "licenseUri" : "...", - "schemaDocumentUri" : "...", - "schemaHash" : "...", - "locked" : false + } ] } ---- At least the following elements are expected to be provided by the user: [point] -- schemaId: A unique label for the schema. -- type: XML or JSON. For XSD schemas this should be 'XML' +- id: A unique label for the schema. +- title: Any title for the schema. In addition, ACL may be useful to make schema readable/editable by others. This will be of interest while accessing/updating an existing schema.(if authorization is enabled) [NOTE] -License URI is optional. It's new since 1.5.0. +License URI is optional. It's new since 1.4.2. === Registering a Metadata Schema Document The following example shows the creation of the first xsd schema only providing mandatory fields mentioned above: [source,options="nowrap"] ---- -schema-record.json: +schema-record-v2.json: { - "schemaId" : "my_first_xsd", - "type" : "XML" + "id": "my_first_xsd", + "titles": [ + { + "value": "Title for my_first_xsd", + } + ] } ---- [source,options="nowrap"] @@ -116,7 +142,7 @@ schema.xsd: include::{snippets}/v2-register-schema/curl-request.adoc[] -You can see, that most of the sent metadata schema record is empty. Only schemaId, mimeType and type are provided by the user. HTTP-wise the call looks as follows: +You can see, that most of the sent datacite record is empty. Only (schema)Id and title are provided by the user. HTTP-wise the call looks as follows: include::{snippets}/v2-register-schema/http-request.adoc[] @@ -125,16 +151,18 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-register-schema/http-response.adoc[] -What you see is, that the metadata schema record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. Furthermore, you'll find an ETag header with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. +There are two possible values for DataResource: XML_Schema and JSON_Schema which depends on the format of the given schema document. (XML in our case.) + ==== Getting a Metadata Schema Record -For obtaining one metadata schema record you have to provide the value of the field 'schemaId'. +For obtaining one datacite record you have to provide the value of the field 'Id'. [NOTE] -As 'Accept' field you have to provide 'application/vnd.datamanager.schema-record+json' otherwise you will -get the metadata schema instead. +As 'Accept' field you have to provide 'application/vnd.datacite.org+json' otherwise you will +get the landing page of the digital object schema instead. include::{snippets}/v2-get-schema-record/curl-request.adoc[] @@ -143,7 +171,7 @@ Be aware that you also have to provide the 'Accept' field. include::{snippets}/v2-get-schema-record/http-request.adoc[] -As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. +As a result, you receive the datacite record send before and again the corresponding ETag in the HTTP response header. include::{snippets}/v2-get-schema-record/http-response.adoc[] @@ -170,8 +198,8 @@ As every update results in a new version 'old' metadata schema documents are sti available. For updating an existing metadata schema (record) a valid ETag is needed. The actual ETag -is available via the HTTP GET call of the metadata schema record. (see above) -Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. +is available via the HTTP GET call of the datacite record. (see above) +Just send an HTTP POST with the updated metadata schema document and/or datacite record. [source,options="nowrap"] ---- @@ -206,7 +234,7 @@ include::{snippets}/v2-update-schema-v2/http-response.adoc[] ==== Updating a Metadata Schema Document (add optional 'note' field) For updating existing metadata schema document we have to provide the new ETag. -Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. +Just send an HTTP POST with the updated metadata schema document and/or datacite record. [source,options="nowrap"] ---- @@ -284,7 +312,7 @@ Now there are two schemaIds registered in the metadata schema registry. ==== Getting a List of Metadata Schema Records -Obtaining all accessible metadata schema records. +Obtaining all accessible datacite records. include::{snippets}/v2-get-all-schemas/curl-request.adoc[] @@ -292,7 +320,7 @@ Same for HTTP request: include::{snippets}/v2-get-all-schemas/http-request.adoc[] -As a result, you receive a list of metadata schema records. +As a result, you receive a list of datacite records. include::{snippets}/v2-get-all-schemas/http-response.adoc[] @@ -321,7 +349,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-get-all-versions-of-a-schema/http-request.adoc[] -As a result, you receive a list of metadata schema records in descending order. +As a result, you receive a list of datacite records in descending order. (current version first) include::{snippets}/v2-get-all-versions-of-a-schema/http-response.adoc[] @@ -399,7 +427,7 @@ Everything should be fine now. As a result, you receive 204 as HTTP status and n include::{snippets}/v2-validate-document-v3/http-response.adoc[] ==== Update Metadata Schema Record -In case of authorization it may be neccessary to update metadata record to be accessible +In case of authorization it may be neccessary to update datacite record to be accessible by others. To do so an update has to be made. In this example we introduce a user called 'admin' and give him all rights. @@ -430,7 +458,7 @@ Same for the HTTP request. include::{snippets}/v2-update-schema-record/http-request.adoc[] -As a result, you receive 200 as HTTP status, the updated metadata schema record and the +As a result, you receive 200 as HTTP status, the updated datacite record and the updated ETag and location in the HTTP response header. include::{snippets}/v2-update-schema-record/http-response.adoc[] @@ -444,7 +472,7 @@ After the update the following fields has changed: === Metadata Management After registration of a schema metadata may be added to MetaStore. In this section, the handling of metadata resources is explained. -It all starts with creating your first metadata resource. The model of a metadata record looks +It all starts with creating your first metadata resource. The model of a datacite record looks like this: [source,options="nowrap"] ---- @@ -488,7 +516,7 @@ License URI is optional. It's new since 1.5.0. ==== Register/Ingest a Metadata Record with Metadata Document -The following example shows the creation of the first metadata record and its metadata only providing mandatory fields mentioned above: +The following example shows the creation of the first datacite record and its metadata only providing mandatory fields mentioned above: [source,options="nowrap"] ---- metadata-record.json: @@ -516,7 +544,7 @@ The schemaId used while registering metadata schema has to be used to link the m approbriate metadata schema. include::{snippets}/v2-ingest-metadata-document/curl-request.adoc[] -You can see, that most of the sent metadata schema record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: +You can see, that most of the sent datacite record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: include::{snippets}/v2-ingest-metadata-document/http-request.adoc[] @@ -525,7 +553,7 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-ingest-metadata-document/http-response.adoc[] -What you see is, that the metadata record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. In the header you'll find a location URL to access the ingested metadata and an ETag with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. @@ -545,8 +573,8 @@ include::{snippets}/v2-get-metadata-document/http-response.adoc[] What you see is, that the metadata is untouched. ==== Accessing Metadata Record -For accessing the metadata record the same URL as before has to be used. -The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". +For accessing the datacite record the same URL as before has to be used. +The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". Then the command line looks like this: include::{snippets}/v2-get-metadata-record/curl-request.adoc[] @@ -558,12 +586,12 @@ The linked metadata will be returned. The result is sent back to the user and wi include::{snippets}/v2-get-metadata-record/http-response.adoc[] -You also get the metadata record seen before. +You also get the datacite record seen before. ==== Updating a Metadata Record (edit ACL entries) & Metadata Document -The following example shows the update of the metadata record and metadata document +The following example shows the update of the datacite record and metadata document to a newer version of the schema. As mentioned before the ETag is needed: [source,options="nowrap"] @@ -601,10 +629,10 @@ You can see, that only the ACL entry for "guest" was added. All other properties include::{snippets}/v2-update-metadata-record-v2/http-request.adoc[] -You will get the new metadata record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. +You will get the new datacite record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. include::{snippets}/v2-update-metadata-record-v2/http-response.adoc[] -What you see is, that the metadata record looks different from the original document. +What you see is, that the datacite record looks different from the original document. ==== Updating Metadata Record & Document Repeat the last step and update to the current version. As mentioned before the @@ -616,7 +644,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-get-metadata-record-v2/http-request.adoc[] -You will get the new metadata record with the new ETag. +You will get the new datacite record with the new ETag. include::{snippets}/v2-get-metadata-record-v2/http-response.adoc[] Now you can update metadata due to new version of schema using the new Etag. @@ -652,7 +680,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-update-metadata-record-v3/http-request.adoc[] -You will get the new metadata record. +You will get the new datacite record. include::{snippets}/v2-update-metadata-record-v3/http-response.adoc[] @@ -668,9 +696,9 @@ include::{snippets}/v2-get-metadata-document-v3/http-response.adoc[] ==== Find a Metadata Record -Search will find all current metadata records. +Search will find all current datacite records. There are some filters available which may be combined. -All filters for the metadata records are set via query parameters. +All filters for the datacite records are set via query parameters. The following filters are allowed: - id @@ -680,7 +708,7 @@ The following filters are allowed: [NOTE] The header contains the field 'Content-Range" which displays delivered indices and the maximum number of available schema records. -If there are more than 20 metadata records registered you have to provide page and/or size as additional query parameters. +If there are more than 20 datacite records registered you have to provide page and/or size as additional query parameters. - page: Number of the page you want to get **(starting with page 0)** - size: Number of entries per page. @@ -696,7 +724,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-list-all-versions-of-metadata-document/http-request.adoc[] -As a result, you receive a list of metadata records in descending order. +As a result, you receive a list of datacite records in descending order. (current version first) include::{snippets}/v2-list-all-versions-of-metadata-document/http-response.adoc[] @@ -713,11 +741,11 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-find-metadata-record-resource/http-request.adoc[] -You will get the current version of the metadata record(s). +You will get the current version of the datacite record(s). include::{snippets}/v2-find-metadata-record-resource/http-response.adoc[] ===== Find after a specific date -If you want to find all metadata records updated after a specific date. +If you want to find all datacite records updated after a specific date. Command line: include::{snippets}/v2-find-metadata-record-from/curl-request.adoc[] @@ -726,11 +754,11 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-find-metadata-record-from/http-request.adoc[] -You will get the current version metadata records updated ln the last 2 hours. +You will get the current version datacite records updated ln the last 2 hours. include::{snippets}/v2-find-metadata-record-from/http-response.adoc[] ===== Find in a specific date range -If you want to find all metadata records updated in a specific date range. +If you want to find all datacite records updated in a specific date range. Command line: include::{snippets}/v2-find-metadata-record-from-to/curl-request.adoc[] @@ -739,7 +767,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-find-metadata-record-from-to/http-request.adoc[] -You will get an empty array as no metadata record exists in the given range: +You will get an empty array as no datacite record exists in the given range: include::{snippets}/v2-find-metadata-record-from-to/http-response.adoc[] @@ -748,7 +776,7 @@ include::{snippets}/v2-find-metadata-record-from-to/http-response.adoc[] [[ChapterMetadataSchemaHandling4Json]] === Schema Registration and Management -In this section, the handling of json schema resources is explained. It all starts with creating your first json schema resource. The model of a metadata schema record looks +In this section, the handling of json schema resources is explained. It all starts with creating your first json schema resource. The model of a datacite record looks like this: [source,options="nowrap"] ---- @@ -818,7 +846,7 @@ schema.json: include::{snippets}/v2-register-json-schema/curl-request.adoc[] -You can see, that most of the sent metadata schema record is empty. Only schemaId, mimeType and type are provided by the user. HTTP-wise the call looks as follows: +You can see, that most of the sent datacite record is empty. Only schemaId, mimeType and type are provided by the user. HTTP-wise the call looks as follows: include::{snippets}/v2-register-json-schema/http-request.adoc[] @@ -827,16 +855,19 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-register-json-schema/http-response.adoc[] -What you see is, that the metadata schema record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. Furthermore, you'll find an ETag header with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. -==== Getting a Metadata Schema Record +There are two possible values for DataResource: XML_Schema and JSON_Schema which depends on the format of the given schema document. (JSON in our case.) +As the metadata and the schema document has a defined structure "MODEL" is used as 'typeGeneral'. + +==== Getting a Datacite Schema Record -For obtaining one metadata schema record you have to provide the value of the field 'schemaId'. +For obtaining one datacite record you have to provide the value of the field 'schemaId'. [NOTE] -As 'Accept' field you have to provide 'application/vnd.datamanager.schema-record+json' otherwise you will -get the metadata schema instead. +As 'Accept' field you have to provide 'application/vnd.datacite.org+json' otherwise you will +get the landing page of the digital object instead. include::{snippets}/v2-get-json-schema-record/curl-request.adoc[] @@ -845,14 +876,14 @@ Be aware that you also have to provide the 'Accept' field. include::{snippets}/v2-get-json-schema-record/http-request.adoc[] -As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. +As a result, you receive the datacite record send before and again the corresponding ETag in the HTTP response header. include::{snippets}/v2-get-json-schema-record/http-response.adoc[] ==== Getting a Metadata Schema Document For obtaining accessible metadata schemas you also have to provide the 'schemaId'. -For accessing schema document you don't have to provide the 'Accept' header. +For accessing schema document you have to provide 'application/xml' as 'Accept' header. include::{snippets}/v2-get-json-schema-document/curl-request.adoc[] @@ -872,8 +903,8 @@ As every update results in a new version 'old' metadata schema documents are sti available. For updating an existing metadata schema (record) a valid ETag is needed. The actual ETag -is available via the HTTP GET call of the metadata schema record. (see above) -Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. +is available via the HTTP GET call of the datacite record. (see above) +Just send an HTTP POST with the updated metadata schema document and/or datacite record. [source,options="nowrap"] ---- @@ -919,7 +950,7 @@ include::{snippets}/v2-update-json-schema-v2/http-response.adoc[] ==== Updating a Metadata Schema Document (add optional 'note' field) For updating existing metadata schema document we have to provide the new ETag. -Just send an HTTP POST with the updated metadata schema document and/or metadata schema record. +Just send an HTTP POST with the updated metadata schema document and/or datacite record. [source,options="nowrap"] ---- @@ -1018,7 +1049,7 @@ Now there are two schemaIds registered in the metadata schema registry. ==== Getting a List of Metadata Schema Records -Obtaining all accessible metadata schema records. +Obtaining all accessible datacite records. include::{snippets}/v2-get-all-json-schemas/curl-request.adoc[] @@ -1026,7 +1057,7 @@ Same for HTTP request: include::{snippets}/v2-get-all-json-schemas/http-request.adoc[] -As a result, you receive a list of metadata schema records. +As a result, you receive a list of datacite records. include::{snippets}/v2-get-all-json-schemas/http-response.adoc[] @@ -1055,7 +1086,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-get-all-versions-of-a-json-schema/http-request.adoc[] -As a result, you receive a list of metadata schema records in descending order. +As a result, you receive a list of datacite records in descending order. (current version first) include::{snippets}/v2-get-all-versions-of-a-json-schema/http-response.adoc[] @@ -1132,7 +1163,7 @@ Everything should be fine now. As a result, you receive 204 as HTTP status and n include::{snippets}/v2-validate-json-document-v3/http-response.adoc[] ==== Update Metadata Schema Record -In case of authorization it may be neccessary to update metadata record to be accessible +In case of authorization it may be neccessary to update datacite record to be accessible by others. To do so an update has to be made. In this example we introduce a user called 'admin' and give him all rights. @@ -1160,7 +1191,7 @@ Same for the HTTP request. include::{snippets}/v2-update-json-schema-record/http-request.adoc[] -As a result, you receive 200 as HTTP status, the updated metadata schema record and the +As a result, you receive 200 as HTTP status, the updated datacite record and the updated ETag and location in the HTTP response header. include::{snippets}/v2-update-json-schema-record/http-response.adoc[] @@ -1174,7 +1205,7 @@ After the update the following fields has changed: === Metadata Management After registration of a schema metadata may be added to MetaStore. In this section, the handling of metadata resources is explained. -It all starts with creating your first metadata resource. The model of a metadata record looks +It all starts with creating your first metadata resource. The model of a datacite record looks like this: [source,options="nowrap"] ---- @@ -1215,7 +1246,7 @@ License URI is optional. It's new since 1.5.0. ==== Register/Ingest a Metadata Record with Metadata Document -The following example shows the creation of the first metadata record and its metadata only providing mandatory fields mentioned above: +The following example shows the creation of the first datacite record and its metadata only providing mandatory fields mentioned above: [source,options="nowrap"] ---- metadata-record4json.json: @@ -1242,7 +1273,7 @@ The schemaId used while registering metadata schema has to be used to link the m approbriate metadata schema. include::{snippets}/v2-ingest-json-metadata-document/curl-request.adoc[] -You can see, that most of the sent metadata schema record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: +You can see, that most of the sent datacite record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: include::{snippets}/v2-ingest-json-metadata-document/http-request.adoc[] @@ -1251,7 +1282,7 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-ingest-json-metadata-document/http-response.adoc[] -What you see is, that the metadata record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. In the header you'll find a location URL to access the ingested metadata and an ETag with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. @@ -1271,8 +1302,8 @@ include::{snippets}/v2-get-json-metadata-document/http-response.adoc[] What you see is, that the metadata is untouched. ==== Accessing Metadata Record -For accessing the metadata record the same URL as before has to be used. -The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". +For accessing the datacite record the same URL as before has to be used. +The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". Then the command line looks like this: include::{snippets}/v2-get-json-metadata-record/curl-request.adoc[] @@ -1284,12 +1315,12 @@ The linked metadata will be returned. The result is sent back to the user and wi include::{snippets}/v2-get-json-metadata-record/http-response.adoc[] -You also get the metadata record seen before. +You also get the datacite record seen before. ==== Updating a Metadata Record (edit ACL entries) -The following example shows the update of the metadata record. As mentioned before the +The following example shows the update of the datacite record. As mentioned before the ETag is needed: [source,options="nowrap"] ---- @@ -1325,10 +1356,10 @@ You can see, that only the ACL entry for "guest" was added. All other properties include::{snippets}/v2-update-json-metadata-record-v2/http-request.adoc[] -You will get the new metadata record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. +You will get the new datacite record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. include::{snippets}/v2-update-json-metadata-record-v2/http-response.adoc[] -What you see is, that the metadata record looks different from the original document. +What you see is, that the datacite record looks different from the original document. ==== Updating Metadata Record & Document Repeat the last step and update to the current version. As mentioned before the @@ -1340,7 +1371,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-get-json-metadata-record-v2/http-request.adoc[] -You will get the new metadata record with the new ETag. +You will get the new datacite record with the new ETag. include::{snippets}/v2-get-json-metadata-record-v2/http-response.adoc[] Now you can update metadata due to new version of schema using the new Etag. @@ -1375,7 +1406,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-update-json-metadata-record-v3/http-request.adoc[] -You will get the new metadata record. +You will get the new datacite record. include::{snippets}/v2-update-json-metadata-record-v3/http-response.adoc[] @@ -1391,9 +1422,9 @@ include::{snippets}/v2-get-json-metadata-document-v3/http-response.adoc[] ==== Find a Metadata Record -Search will find all current metadata records. +Search will find all current datacite records. There are some filters available which may be combined. -All filters for the metadata records are set via query parameters. +All filters for the datacite records are set via query parameters. The following filters are allowed: - id @@ -1403,7 +1434,7 @@ The following filters are allowed: [NOTE] The header contains the field 'Content-Range" which displays delivered indices and the maximum number of available schema records. -If there are more than 20 metadata records registered you have to provide page and/or size as additional query parameters. +If there are more than 20 datacite records registered you have to provide page and/or size as additional query parameters. - page: Number of the page you want to get **(starting with page 0)** - size: Number of entries per page. @@ -1419,7 +1450,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-list-all-versions-of-json-metadata-document/http-request.adoc[] -As a result, you receive a list of metadata records in descending order. +As a result, you receive a list of datacite records in descending order. (current version first) include::{snippets}/v2-list-all-versions-of-json-metadata-document/http-response.adoc[] @@ -1436,11 +1467,11 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-find-json-metadata-record-resource/http-request.adoc[] -You will get the current version metadata record. +You will get the current version datacite record. include::{snippets}/v2-find-json-metadata-record-resource/http-response.adoc[] ===== Find after a specific date -If you want to find all metadata records updated after a specific date. +If you want to find all datacite records updated after a specific date. Command line: include::{snippets}/v2-find-json-metadata-record-from/curl-request.adoc[] @@ -1449,11 +1480,11 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-find-json-metadata-record-from/http-request.adoc[] -You will get the current version metadata records updated ln the last 2 hours. +You will get the current version datacite records updated ln the last 2 hours. include::{snippets}/v2-find-json-metadata-record-from/http-response.adoc[] ===== Find in a specific date range -If you want to find all metadata records updated in a specific date range. +If you want to find all datacite records updated in a specific date range. Command line: include::{snippets}/v2-find-json-metadata-record-from-to/curl-request.adoc[] @@ -1462,7 +1493,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-find-json-metadata-record-from-to/http-request.adoc[] -You will get an empty array as no metadata record exists in the given range: +You will get an empty array as no datacite record exists in the given range: include::{snippets}/v2-find-json-metadata-record-from-to/http-response.adoc[] diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java index 88be93e7..6ac37fda 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java @@ -254,9 +254,9 @@ public void documentSchemaRegistry4Json() throws Exception { //************************************************************************** schemaRecord.setId(EXAMPLE_SCHEMA_ID); SchemaRegistryControllerTestV2.setTitle(schemaRecord, "Title for " + EXAMPLE_SCHEMA_ID); - SchemaRegistryControllerTestV2.setComment(schemaRecord, "Comment for " + EXAMPLE_SCHEMA_ID); - SchemaRegistryControllerTestV2.setDefinition(schemaRecord, "Definition for " + EXAMPLE_SCHEMA_ID); - SchemaRegistryControllerTestV2.setLabel(schemaRecord, "Labels for " + EXAMPLE_SCHEMA_ID); +// SchemaRegistryControllerTestV2.setComment(schemaRecord, "Comment for " + EXAMPLE_SCHEMA_ID); +// SchemaRegistryControllerTestV2.setDefinition(schemaRecord, "Definition for " + EXAMPLE_SCHEMA_ID); +// SchemaRegistryControllerTestV2.setLabel(schemaRecord, "Labels for " + EXAMPLE_SCHEMA_ID); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java index 7917d9fb..572e3487 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java @@ -43,6 +43,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; @@ -236,9 +237,9 @@ public void documentSchemaRegistry() throws Exception { //************************************************************************** schemaRecord.setId(EXAMPLE_SCHEMA_ID); SchemaRegistryControllerTestV2.setTitle(schemaRecord, "Title for " + EXAMPLE_SCHEMA_ID); - SchemaRegistryControllerTestV2.setComment(schemaRecord, "Comment for " + EXAMPLE_SCHEMA_ID); - SchemaRegistryControllerTestV2.setDefinition(schemaRecord, "Definition for " + EXAMPLE_SCHEMA_ID); - SchemaRegistryControllerTestV2.setLabel(schemaRecord, "Labels for " + EXAMPLE_SCHEMA_ID); +// SchemaRegistryControllerTestV2.setComment(schemaRecord, "Comment for " + EXAMPLE_SCHEMA_ID); +// SchemaRegistryControllerTestV2.setDefinition(schemaRecord, "Definition for " + EXAMPLE_SCHEMA_ID); +// SchemaRegistryControllerTestV2.setLabel(schemaRecord, "Labels for " + EXAMPLE_SCHEMA_ID); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); @@ -270,7 +271,8 @@ public void documentSchemaRegistry() throws Exception { //************************************************************************** // Get metadata schema this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). - contextPath(contextPath)). + contextPath(contextPath). + accept(MediaType.APPLICATION_XML)). andDo(document("v2-get-schema-document")). andExpect(status().isOk()). andReturn().getResponse(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 7ff21ad3..236d36bf 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1893,10 +1893,11 @@ public static DataResource createDataResource4XmlSchema(String id) { public static DataResource createDataResource4Schema(String id) { DataResource record = new DataResource(); record.setId(id); + // mandatory element title has to be set setTitle(record, id); + // the following fields are optional setComment(record, COMMENT); setDefinition(record, DEFINITION); - // mandatory element title has to be set record.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.SCHEMA_TYPE.XML + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); record.getFormats().add(MediaType.APPLICATION_XML.toString()); Set<AclEntry> aclEntries = new HashSet<>(); From 96a1590a48c5afe80efa72c5040c834a7d91f062 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 23 Apr 2024 13:01:04 +0200 Subject: [PATCH 031/181] Update API version for swagger-ui. --- .../metastore2/configuration/OpenApiDefinitions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java b/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java index a83d6779..301df5b1 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java +++ b/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java @@ -36,7 +36,7 @@ public OpenAPI customOpenAPI(){ .components(new Components()) .info(new Info().title("MetaStore Microservice - RESTful API"). description("This webpage describes the RESTful interface of the KIT Data Manager MetaStore Microservice."). - version("1.0.0"). + version("2.0.0"). contact( new Contact(). name("KIT Data Manager Support"). From bc5929926aea1755a8de772bd51c3b195c3dd7df Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 23 Apr 2024 13:02:49 +0200 Subject: [PATCH 032/181] Adapt documentation for API v2. --- docs/documentationV2.adoc | 275 ++++++++++++++++++++++++++------------ 1 file changed, 190 insertions(+), 85 deletions(-) diff --git a/docs/documentationV2.adoc b/docs/documentationV2.adoc index 5b3bce67..f2d9fe3d 100644 --- a/docs/documentationV2.adoc +++ b/docs/documentationV2.adoc @@ -87,6 +87,12 @@ like this: "identifierType" : "..." } ], "version" : "...", + "rights": [ + { + "schemeId": "", + "schemeUri": "" + } + ], "lastUpdate" : "...", "state" : "...", "acls" : [ { @@ -151,13 +157,14 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-register-schema/http-response.adoc[] -What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. Some of the elements received a value by the server. Furthermore, you'll find an ETag header with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. -There are two possible values for DataResource: XML_Schema and JSON_Schema which depends on the format of the given schema document. (XML in our case.) +There are two possible values for DataResource: 'XML_Schema' and 'JSON_Schema' which depends on the format of the given schema document. (XML in our case.) +As the schema document has a defined structure "MODEL" is used as 'typeGeneral'. -==== Getting a Metadata Schema Record +==== Getting a Datacite Schema Record For obtaining one datacite record you have to provide the value of the field 'Id'. [NOTE] @@ -166,7 +173,7 @@ get the landing page of the digital object schema instead. include::{snippets}/v2-get-schema-record/curl-request.adoc[] -In the actual HTTP request just access the path of the resource using the base path and the 'schemaid'. +In the actual HTTP request just access the path of the resource using the base path and the 'schemaId'. Be aware that you also have to provide the 'Accept' field. include::{snippets}/v2-get-schema-record/http-request.adoc[] @@ -178,7 +185,7 @@ include::{snippets}/v2-get-schema-record/http-response.adoc[] ==== Getting a Metadata Schema Document For obtaining accessible metadata schemas you also have to provide the 'schemaId'. -For accessing schema document you don't have to provide the 'Accept' header. +For accessing schema document you have to provide 'application/xml' as 'Accept' header. include::{snippets}/v2-get-schema-document/curl-request.adoc[] @@ -197,8 +204,8 @@ Updating a metadata schema document will not break old metadata documents. As every update results in a new version 'old' metadata schema documents are still available. -For updating an existing metadata schema (record) a valid ETag is needed. The actual ETag -is available via the HTTP GET call of the datacite record. (see above) +For updating an existing metadata schema and/or datacite record a valid ETag is needed. The actual ETag +is available via the HTTP GET call of the datacite record (see above). Just send an HTTP POST with the updated metadata schema document and/or datacite record. [source,options="nowrap"] @@ -226,7 +233,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-update-schema-v2/http-request.adoc[] -As a result, you receive the updated schema record and in the HTTP response header +As a result, you receive the updated datacite record of the schema document and in the HTTP response header the new location URL and the ETag. include::{snippets}/v2-update-schema-v2/http-response.adoc[] @@ -262,12 +269,12 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-update-schema-v3/http-request.adoc[] -As a result, you receive the updated schema record and in the HTTP response header +As a result, you receive the updated datacite record of the schema document and in the HTTP response header the new location URL and the ETag. include::{snippets}/v2-update-schema-v3/http-response.adoc[] -The updated schema record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. +The updated datacite record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. === Registering another Metadata Schema Document @@ -312,7 +319,7 @@ Now there are two schemaIds registered in the metadata schema registry. ==== Getting a List of Metadata Schema Records -Obtaining all accessible datacite records. +Obtaining all accessible datacite records of schema documents. include::{snippets}/v2-get-all-schemas/curl-request.adoc[] @@ -477,49 +484,119 @@ like this: [source,options="nowrap"] ---- { - "id": "...", - "relatedResource": { - "identifier": "...", - "identifierType": "..." - }, - "createdAt": "...", - "lastUpdate": "...", - "recordVersion": 1, - "schema": { - "identifier": "...", - "identifierType": "..." - }, - "schemaVersion": 1, - "acl": [{ - "id": 1, - "sid": "...", - "permission": "..." - }], - "licenseUri": "...", - "metadataDocumentUri": "...", - "documentHash": "..." + "id" : "...", + "identifier" : { + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "givenName" : "..." + } ], + "titles" : [ { + "value" : "..." + } ], + "publisher" : "...", + "publicationYear" : "...", + "resourceType" : { + "value" : "...", + "typeGeneral" : "..." + }, + "dates" : [ { + "value" : "...", + "type" : "..." + } ], + "relatedIdentifiers" : [ { + "value" : "...", + "identifierType" : "...", + "relationType" : "..." + }} ], + "alternateIdentifiers" : [ { + "value" : "...", + "identifierType" : "..." + } ], + "version" : "...", + "rights": [ + { + "schemeId": "", + "schemeUri": "" + } + ], + "lastUpdate" : "...", + "state" : "...", + "acls" : [ { + "sid" : "...", + "permission" : "..." + } ] } ---- -At least the following elements are expected to be provided by the user: +At least the following elements has to be provided by the user: [point] -- schema: Identifier of the linked schema. (INTERNAL and URL supported as type) -- relatedResource: The link to the resource. +- title: Any title for the metadata document +- resourceType: 'XML_Metadata' or 'JSON_Metadata' and type 'MODEL'. +- relatedIdentifier/schema: Link to the related schema. (identifierType: INTERNAL and URL are supported, relationType: IS_DERIVED_FROM) +- relatedIdentifier/data: Link to the (data) resource. (identifierType: any, relationType: IS_METADATA_FOR) In addition, ACL may be useful to make metadata editable by others. (This will be of interest while updating an existing metadata) [NOTE] -If linked schema is identified by its schemaId the INTERNAL type has to be used. +If linked schema is identified by its schemaId the INTERNAL type has to be used. It's than +linked to the current schema version at creation time. [NOTE] -License URI is optional. It's new since 1.5.0. +License URI is optional. It's new since 1.4.2. -==== Register/Ingest a Metadata Record with Metadata Document +==== Register/Ingest a Datacite Record with Metadata Document The following example shows the creation of the first datacite record and its metadata only providing mandatory fields mentioned above: [source,options="nowrap"] ---- metadata-record.json: +{ + "titles": [ + { + "value": "Title of first XML metadata document", + } + ], + "resourceType": { + "value": "XML_Metadata", + "typeGeneral": "MODEL" + }, + "subjects": [], + "contributors": [], + "dates": [], + "relatedIdentifiers": [ + { + "id": null, + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", + "relationType": "IS_DERIVED_FROM", + "scheme": null, + "relatedMetadataScheme": null + }, + { + "id": null, + "identifierType": "URL", + "value": "https://repo/anyResourceId", + "relationType": "IS_METADATA_FOR", + "scheme": null, + "relatedMetadataScheme": null + } + ], + "descriptions": [], + "geoLocations": [], + "language": null, + "alternateIdentifiers": [], + "sizes": [], + "formats": [], + "version": null, + "rights": [], + "fundingReferences": [], + "lastUpdate": null, + "state": null, + "embargoDate": null, + "acls": [] +} { "relatedResource": { "identifier": "anyResourceId", @@ -572,7 +649,7 @@ include::{snippets}/v2-get-metadata-document/http-response.adoc[] What you see is, that the metadata is untouched. -==== Accessing Metadata Record +==== Accessing Datacite Record of Metadata Document For accessing the datacite record the same URL as before has to be used. The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". Then the command line looks like this: @@ -589,7 +666,7 @@ include::{snippets}/v2-get-metadata-record/http-response.adoc[] You also get the datacite record seen before. -==== Updating a Metadata Record (edit ACL entries) & Metadata Document +==== Updating a Datacite Record (edit ACL entries) & Metadata Document The following example shows the update of the datacite record and metadata document to a newer version of the schema. As mentioned before the @@ -634,7 +711,7 @@ include::{snippets}/v2-update-metadata-record-v2/http-response.adoc[] What you see is, that the datacite record looks different from the original document. -==== Updating Metadata Record & Document +==== Updating Datacite Record & Document Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. @@ -695,7 +772,7 @@ You will get the updated metadata. include::{snippets}/v2-get-metadata-document-v3/http-response.adoc[] -==== Find a Metadata Record +==== Find a Datacite Record of Metadata Document Search will find all current datacite records. There are some filters available which may be combined. All filters for the datacite records are set via query parameters. @@ -713,7 +790,7 @@ If there are more than 20 datacite records registered you have to provide page a - page: Number of the page you want to get **(starting with page 0)** - size: Number of entries per page. -==== Getting a List of all Metadata Records for a Specific Metadata Document +==== Getting a List of all Datacite Records for a Specific Metadata Document If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: @@ -855,12 +932,12 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-register-json-schema/http-response.adoc[] -What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. Some of the elements received a value by the server. Furthermore, you'll find an ETag header with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. -There are two possible values for DataResource: XML_Schema and JSON_Schema which depends on the format of the given schema document. (JSON in our case.) -As the metadata and the schema document has a defined structure "MODEL" is used as 'typeGeneral'. +There are two possible values for DataResource: 'XML_Schema' and 'JSON_Schema' which depends on the format of the given schema document. (JSON in our case.) +As the schema document has a defined structure "MODEL" is used as 'typeGeneral'. ==== Getting a Datacite Schema Record @@ -871,7 +948,7 @@ get the landing page of the digital object instead. include::{snippets}/v2-get-json-schema-record/curl-request.adoc[] -In the actual HTTP request just access the path of the resource using the base path and the 'schemaid'. +In the actual HTTP request just access the path of the resource using the base path and the 'schemaId'. Be aware that you also have to provide the 'Accept' field. include::{snippets}/v2-get-json-schema-record/http-request.adoc[] @@ -903,7 +980,7 @@ As every update results in a new version 'old' metadata schema documents are sti available. For updating an existing metadata schema (record) a valid ETag is needed. The actual ETag -is available via the HTTP GET call of the datacite record. (see above) +is available via the HTTP GET call of the datacite record (see above). Just send an HTTP POST with the updated metadata schema document and/or datacite record. [source,options="nowrap"] @@ -942,7 +1019,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-update-json-schema-v2/http-request.adoc[] -As a result, you receive the updated schema record and in the HTTP response header +As a result, you receive the updated datacite record of the schema document and in the HTTP response header the new location URL and the ETag. include::{snippets}/v2-update-json-schema-v2/http-response.adoc[] @@ -993,12 +1070,12 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-update-json-schema-v3/http-request.adoc[] -As a result, you receive the updated schema record and in the HTTP response header +As a result, you receive the updated datacite record of the schema document and in the HTTP response header the new location URL and the ETag. include::{snippets}/v2-update-json-schema-v3/http-response.adoc[] -The updated schema record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. +The updated datacite record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. === Registering another Metadata Schema Document @@ -1049,7 +1126,7 @@ Now there are two schemaIds registered in the metadata schema registry. ==== Getting a List of Metadata Schema Records -Obtaining all accessible datacite records. +Obtaining all accessible datacite records of schema documents. include::{snippets}/v2-get-all-json-schemas/curl-request.adoc[] @@ -1205,46 +1282,74 @@ After the update the following fields has changed: === Metadata Management After registration of a schema metadata may be added to MetaStore. In this section, the handling of metadata resources is explained. -It all starts with creating your first metadata resource. The model of a datacite record looks -like this: +It all starts with creating your first metadata resource. The model of a datacite record is +similar to the record of the schema document: [source,options="nowrap"] ---- { - "id": "...", - "relatedResource": { - "identifier": "...", - "identifierType": "..." - }, - "createdAt": "...", - "lastUpdate": "...", - "schema": { - "identifier": "...", - "identifierType": "..." - }, - "schemaVersion": 1, - "recordVersion": 1, - "acl": [{ - "id": 3, - "sid": "...", - "permission": "..." - }], - "licenseUri": "...", - "metadataDocumentUri": "...", - "documentHash": "..." + "id" : "...", + "identifier" : { + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "givenName" : "..." + } ], + "titles" : [ { + "value" : "..." + } ], + "publisher" : "...", + "publicationYear" : "...", + "resourceType" : { + "value" : "...", + "typeGeneral" : "..." + }, + "dates" : [ { + "value" : "...", + "type" : "..." + } ], + "relatedIdentifiers" : [ { + "value" : "...", + "identifierType" : "...", + "relationType" : "..." + }} ], + "alternateIdentifiers" : [ { + "value" : "...", + "identifierType" : "..." + } ], + "version" : "...", + "rights": [ + { + "schemeId": "", + "schemeUri": "" + } + ], + "lastUpdate" : "...", + "state" : "...", + "acls" : [ { + "sid" : "...", + "permission" : "..." + } ] } ---- -At least the following elements are expected to be provided by the user: +At least the following elements has to be provided by the user: [point] -- schema: Identifier of the linked schema. (INTERNAL and URL supported as type) -- relatedResource: The link to the resource. +- title: Any title for the metadata document +- resourceType: 'XML_Metadata' or 'JSON_Metadata' and type 'MODEL'. +- relatedIdentifier/schema: Link to the related schema. (identifierType: INTERNAL and URL are supported, relationType: IS_DERIVED_FROM) +- relatedIdentifier/data: Link to the (data) resource. (identifierType: any, relationType: IS_METADATA_FOR) In addition, ACL may be useful to make metadata editable by others. (This will be of interest while updating an existing metadata) [NOTE] -License URI is optional. It's new since 1.5.0. +If linked schema is identified by its schemaId the INTERNAL type has to be used. It's than +linked to the current schema version at creation time. + +[NOTE] +License URI is optional. It's new since 1.4.2. -==== Register/Ingest a Metadata Record with Metadata Document +==== Register/Ingest a Datacite Record with Metadata Document The following example shows the creation of the first datacite record and its metadata only providing mandatory fields mentioned above: [source,options="nowrap"] @@ -1301,7 +1406,7 @@ include::{snippets}/v2-get-json-metadata-document/http-response.adoc[] What you see is, that the metadata is untouched. -==== Accessing Metadata Record +==== Accessing Datacite Record of Metadata Document For accessing the datacite record the same URL as before has to be used. The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". Then the command line looks like this: @@ -1318,7 +1423,7 @@ include::{snippets}/v2-get-json-metadata-record/http-response.adoc[] You also get the datacite record seen before. -==== Updating a Metadata Record (edit ACL entries) +==== Updating a Datacite Record of Metadata Document (edit ACL entries) The following example shows the update of the datacite record. As mentioned before the ETag is needed: @@ -1361,7 +1466,7 @@ include::{snippets}/v2-update-json-metadata-record-v2/http-response.adoc[] What you see is, that the datacite record looks different from the original document. -==== Updating Metadata Record & Document +==== Updating Datacite Record of Metadata Document & Document Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. @@ -1421,7 +1526,7 @@ You will get the updated metadata. include::{snippets}/v2-get-json-metadata-document-v3/http-response.adoc[] -==== Find a Metadata Record +==== Find a Datacite Record of Metadata Document Search will find all current datacite records. There are some filters available which may be combined. All filters for the datacite records are set via query parameters. @@ -1439,7 +1544,7 @@ If there are more than 20 datacite records registered you have to provide page a - page: Number of the page you want to get **(starting with page 0)** - size: Number of entries per page. -==== Getting a List of all Metadata Records for a Specific Metadata Document +==== Getting a List of all Datacite Records for a Specific Metadata Document If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: From ccf932c73726275bb6751a7bffcb693902511c05 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 23 Apr 2024 14:24:56 +0200 Subject: [PATCH 033/181] Fix documentation API v1. --- docs/documentation.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/documentation.adoc b/docs/documentation.adoc index d362aba5..986454c3 100644 --- a/docs/documentation.adoc +++ b/docs/documentation.adoc @@ -120,7 +120,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaI include::{snippets}/register-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/register-schema/http-response.adoc[] @@ -275,7 +275,7 @@ HTTP-wise the call looks as follows: include::{snippets}/register-another-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/register-another-schema/http-response.adoc[] @@ -520,7 +520,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaI include::{snippets}/ingest-metadata-document/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/ingest-metadata-document/http-response.adoc[] @@ -822,7 +822,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaI include::{snippets}/register-json-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/register-json-schema/http-response.adoc[] @@ -1009,7 +1009,7 @@ HTTP-wise the call looks as follows: include::{snippets}/register-another-json-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/register-another-json-schema/http-response.adoc[] @@ -1246,7 +1246,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaI include::{snippets}/ingest-json-metadata-document/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/ingest-json-metadata-document/http-response.adoc[] From 5c2641074ea0a7e0a9f35638dc51a9147336863a Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 23 Apr 2024 16:05:11 +0200 Subject: [PATCH 034/181] Continue documentation for API v2. --- docs/documentationV2.adoc | 87 ++++++++++++------- ...tryControllerDocumentation4JsonTestV2.java | 12 ++- ...RegistryControllerDocumentationTestV2.java | 5 +- 3 files changed, 66 insertions(+), 38 deletions(-) diff --git a/docs/documentationV2.adoc b/docs/documentationV2.adoc index f2d9fe3d..07d620c3 100644 --- a/docs/documentationV2.adoc +++ b/docs/documentationV2.adoc @@ -115,7 +115,7 @@ License URI is optional. It's new since 1.4.2. === Registering a Metadata Schema Document -The following example shows the creation of the first xsd schema only providing mandatory fields mentioned above: +The following example shows the creation of the first XSD schema only providing mandatory fields mentioned above: [source,options="nowrap"] ---- schema-record-v2.json: @@ -152,7 +152,7 @@ You can see, that most of the sent datacite record is empty. Only (schema)Id and include::{snippets}/v2-register-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/v2-register-schema/http-response.adoc[] @@ -278,13 +278,17 @@ The updated datacite record contains three modified fields: 'schemaVersion', 'la === Registering another Metadata Schema Document -The following example shows the creation of another xsd schema only providing mandatory fields mentioned above: +The following example shows the creation of another XSD schema only providing mandatory fields mentioned above: [source,options="nowrap"] ---- another-schema-record.json: { - "schemaId" : "another_xsd", - "type" : "XML" + "id": "another_xsd", + "titles": [ + { + "value": "Title for another_xsd", + } + ] } ---- [source,options="nowrap"] @@ -310,7 +314,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-register-another-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/v2-register-another-schema/http-response.adoc[] @@ -319,7 +323,7 @@ Now there are two schemaIds registered in the metadata schema registry. ==== Getting a List of Metadata Schema Records -Obtaining all accessible datacite records of schema documents. +For getting all accessible datacite records of schema documents type: include::{snippets}/v2-get-all-schemas/curl-request.adoc[] @@ -372,7 +376,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-get-schema-v3/http-request.adoc[] -As a result, you receive the XSD schema document sent before. +As a result, you receive the XSD schema document sent before: include::{snippets}/v2-get-schema-v3/http-response.adoc[] @@ -441,6 +445,22 @@ user called 'admin' and give him all rights. [source,options="nowrap"] ---- schema-record-v4.json +{ + "id": "my_first_xsd", + [...] + "acls": [ + { + "id": 1, + "sid": "SELF", + "permission": "ADMINISTRATE" + }, + { + "id": null, + "sid": "admin", + "permission": "ADMINISTRATE" + } + ] +} { "schemaId":"my_first_xsd", "mimeType":"application/xml", @@ -472,7 +492,7 @@ include::{snippets}/v2-update-schema-record/http-response.adoc[] After the update the following fields has changed: -- schemaVersion number increased by one. +- version number increased by one. - lastUpdate to the date of the last update (set by server) - acl additional ACL entry (set during update) @@ -529,7 +549,7 @@ like this: } ] } ---- -At least the following elements has to be provided by the user: +At least the following elements have to be provided by the user: [point] - title: Any title for the metadata document @@ -540,7 +560,7 @@ At least the following elements has to be provided by the user: In addition, ACL may be useful to make metadata editable by others. (This will be of interest while updating an existing metadata) [NOTE] -If linked schema is identified by its schemaId the INTERNAL type has to be used. It's than +If linked schema is identified by its schemaId the INTERNAL type has to be used. It's then linked to the current schema version at creation time. [NOTE] @@ -625,7 +645,7 @@ You can see, that most of the sent datacite record is empty. Only schemaId and r include::{snippets}/v2-ingest-metadata-document/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/v2-ingest-metadata-document/http-response.adoc[] @@ -927,7 +947,7 @@ You can see, that most of the sent datacite record is empty. Only schemaId, mime include::{snippets}/v2-register-json-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/v2-register-json-schema/http-response.adoc[] @@ -1117,7 +1137,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-register-another-json-schema/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/v2-register-another-json-schema/http-response.adoc[] @@ -1126,7 +1146,7 @@ Now there are two schemaIds registered in the metadata schema registry. ==== Getting a List of Metadata Schema Records -Obtaining all accessible datacite records of schema documents. +For getting all accessible datacite records of schema documents type: include::{snippets}/v2-get-all-json-schemas/curl-request.adoc[] @@ -1179,7 +1199,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-get-json-schema-v3/http-request.adoc[] -As a result, you receive the XSD schema document sent before. +As a result, you receive the XSD schema document sent before: include::{snippets}/v2-get-json-schema-v3/http-response.adoc[] @@ -1221,7 +1241,6 @@ Same for the HTTP request. The schemaVersion number is set by a query parameter. include::{snippets}/v2-validate-json-document-v1/http-request.adoc[] As a result, you receive 422 as HTTP status and an error message holding some information about the error. -(Unfortunately not documented here due to technical reasons.) include::{snippets}/v2-validate-json-document-v1/http-response.adoc[] @@ -1248,18 +1267,20 @@ user called 'admin' and give him all rights. ---- schema-record4json-v4.json { - "schemaId" : "my_first_json", - "mimeType" : "application/json", - "type" : "JSON", - "acl" : [ { - "id" : 1, - "sid" : "SELF", - "permission" : "ADMINISTRATE" - }, { - "id" : 3, - "sid" : "admin", - "permission" : "ADMINISTRATE" - } ] + "id": "my_first_json", + [...] + "acls": [ + { + "id": 1, + "sid": "SELF", + "permission": "ADMINISTRATE" + }, + { + "id": null, + "sid": "admin", + "permission": "ADMINISTRATE" + } + ] } ---- include::{snippets}/v2-update-json-schema-record/curl-request.adoc[] @@ -1275,7 +1296,7 @@ include::{snippets}/v2-update-json-schema-record/http-response.adoc[] After the update the following fields has changed: -- schemaVersion number increased by one. +- version number increased by one. - lastUpdate to the date of the last update (set by server) - acl additional ACL entry (set during update) @@ -1332,7 +1353,7 @@ similar to the record of the schema document: } ] } ---- -At least the following elements has to be provided by the user: +At least the following elements have to be provided by the user: [point] - title: Any title for the metadata document @@ -1343,7 +1364,7 @@ At least the following elements has to be provided by the user: In addition, ACL may be useful to make metadata editable by others. (This will be of interest while updating an existing metadata) [NOTE] -If linked schema is identified by its schemaId the INTERNAL type has to be used. It's than +If linked schema is identified by its schemaId the INTERNAL type has to be used. It's then linked to the current schema version at creation time. [NOTE] @@ -1382,7 +1403,7 @@ You can see, that most of the sent datacite record is empty. Only schemaId and r include::{snippets}/v2-ingest-json-metadata-document/http-request.adoc[] -As Content-Type only 'application/json' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: include::{snippets}/v2-ingest-json-metadata-document/http-response.adoc[] diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java index 6ac37fda..b42b52a9 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java @@ -19,8 +19,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import edu.kit.datamanager.entities.PERMISSION; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.test.SchemaRegistryControllerTestV2; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; @@ -45,6 +43,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; @@ -289,7 +288,8 @@ public void documentSchemaRegistry4Json() throws Exception { //************************************************************************** // Get metadata schema this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). - contextPath(contextPath)). + contextPath(contextPath). + accept(MediaType.APPLICATION_JSON)). andDo(document("v2-get-json-schema-document")). andExpect(status().isOk()). andReturn().getResponse(); @@ -322,6 +322,7 @@ public void documentSchemaRegistry4Json() throws Exception { // 6. Registering another metadata schema //************************************************************************** schemaRecord.setId(ANOTHER_SCHEMA_ID); + SchemaRegistryControllerTestV2.setTitle(schemaRecord, "Title for " + ANOTHER_SCHEMA_ID); schemaFile = new MockMultipartFile("schema", "another-schema.json", "application/xml", ANOTHER_SCHEMA.getBytes()); recordFile = new MockMultipartFile("record", "another-schema-record.json", "application/json", new ByteArrayInputStream(mapper.writeValueAsString(schemaRecord).getBytes())); @@ -363,7 +364,8 @@ public void documentSchemaRegistry4Json() throws Exception { // 9. Getting current schema //************************************************************************** this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). - contextPath(contextPath)). + contextPath(contextPath). + accept(MediaType.APPLICATION_JSON)). andDo(document("v2-get-json-schema-v3")). andExpect(status().isOk()). andReturn().getResponse(); @@ -371,6 +373,8 @@ public void documentSchemaRegistry4Json() throws Exception { // 10. Getting specific version of a schema //************************************************************************** this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). + contextPath(contextPath). + accept(MediaType.APPLICATION_JSON). param("version", "1"). contextPath(contextPath)). andDo(document("v2-get-json-schema-v1")). diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java index 572e3487..148f4f91 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java @@ -305,6 +305,7 @@ public void documentSchemaRegistry() throws Exception { // 6. Registering another metadata schema //************************************************************************** schemaRecord.setId(ANOTHER_SCHEMA_ID); + SchemaRegistryControllerTestV2.setTitle(schemaRecord, "Title for " + ANOTHER_SCHEMA_ID); schemaFile = new MockMultipartFile("schema", "another-schema.xsd", "application/xml", ANOTHER_SCHEMA.getBytes()); recordFile = new MockMultipartFile("record", "another-schema-record.json", "application/json", new ByteArrayInputStream(mapper.writeValueAsString(schemaRecord).getBytes())); @@ -346,7 +347,8 @@ public void documentSchemaRegistry() throws Exception { // 9. Getting current schema //************************************************************************** this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). - contextPath(contextPath)). + contextPath(contextPath). + accept(MediaType.APPLICATION_XML)). andDo(document("v2-get-schema-v3")). andExpect(status().isOk()). andReturn().getResponse(); @@ -355,6 +357,7 @@ public void documentSchemaRegistry() throws Exception { //************************************************************************** this.mockMvc.perform(get(endpointSchema + "/" + EXAMPLE_SCHEMA_ID). contextPath(contextPath). + accept(MediaType.APPLICATION_XML). param("version", "1")). andDo(document("v2-get-schema-v1")). andExpect(status().isOk()). From e18a9115545862663fbeea2c1833356e62388387 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 24 Apr 2024 12:25:38 +0200 Subject: [PATCH 035/181] Fix documentation for API v1. --- .../SchemaRegistryControllerDocumentation4JsonTest.java | 1 + .../documentation/SchemaRegistryControllerDocumentationTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java index b1bdb179..a786ceb6 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java @@ -464,6 +464,7 @@ public void documentSchemaRegistry4Json() throws Exception { mapper = new ObjectMapper(); MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + record.getAcl().add(new AclEntry("guest", PERMISSION.READ)); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); record.setSchemaVersion(2l); recordFile = new MockMultipartFile("record", "metadata-record4json-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java index 39d1e74f..2ecdb8e8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java @@ -448,6 +448,7 @@ public void documentSchemaRegistry() throws Exception { mapper = new ObjectMapper(); MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + record.getAcl().add(new AclEntry("guest", PERMISSION.READ)); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); record.setSchemaVersion(2l); recordFile = new MockMultipartFile("record", "metadata-record-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); From a349b42ab86f7c4d088b852aeb01e3f22aa7a14f Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 24 Apr 2024 12:26:15 +0200 Subject: [PATCH 036/181] Improve documentation API v2. --- docs/documentationV2.adoc | 239 +++++++++--------- ...tryControllerDocumentation4JsonTestV2.java | 3 +- ...RegistryControllerDocumentationTestV2.java | 3 +- 3 files changed, 126 insertions(+), 119 deletions(-) diff --git a/docs/documentationV2.adoc b/docs/documentationV2.adoc index 07d620c3..6a829e34 100644 --- a/docs/documentationV2.adoc +++ b/docs/documentationV2.adoc @@ -461,23 +461,6 @@ schema-record-v4.json } ] } -{ - "schemaId":"my_first_xsd", - "mimeType":"application/xml", - "type":"XML", - "acl":[ - { - "id":33, - "sid":"SELF", - "permission":"ADMINISTRATE" - }, - { - "id":null, - "sid":"admin", - "admin":"ADMINISTRATE" - } - ] -} ---- include::{snippets}/v2-update-schema-record/curl-request.adoc[] @@ -494,7 +477,7 @@ After the update the following fields has changed: - version number increased by one. - lastUpdate to the date of the last update (set by server) -- acl additional ACL entry (set during update) +- acls additional ACL entry (set during update) === Metadata Management After registration of a schema metadata may be added to MetaStore. @@ -582,51 +565,18 @@ metadata-record.json: "value": "XML_Metadata", "typeGeneral": "MODEL" }, - "subjects": [], - "contributors": [], - "dates": [], "relatedIdentifiers": [ { - "id": null, "identifierType": "URL", "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", - "relationType": "IS_DERIVED_FROM", - "scheme": null, - "relatedMetadataScheme": null + "relationType": "IS_DERIVED_FROM" }, { - "id": null, "identifierType": "URL", "value": "https://repo/anyResourceId", - "relationType": "IS_METADATA_FOR", - "scheme": null, - "relatedMetadataScheme": null + "relationType": "IS_METADATA_FOR" } - ], - "descriptions": [], - "geoLocations": [], - "language": null, - "alternateIdentifiers": [], - "sizes": [], - "formats": [], - "version": null, - "rights": [], - "fundingReferences": [], - "lastUpdate": null, - "state": null, - "embargoDate": null, - "acls": [] -} -{ - "relatedResource": { - "identifier": "anyResourceId", - "identifierType": "INTERNAL" - }, - "schema": { - "identifier": "my_first_xsd", - "identifierType": "INTERNAL" - }, - "schemaVersion": 1 + ] } ---- [source,options="nowrap"] @@ -650,7 +600,7 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-ingest-metadata-document/http-response.adoc[] -What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. Some of the elements received a value by the server. In the header you'll find a location URL to access the ingested metadata and an ETag with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. @@ -695,20 +645,25 @@ ETag is needed: ---- metadata-record-v2.json: { - "relatedResource": { - "identifier": "anyResourceId", - "identifierType": "INTERNAL" - }, - "schema": { - "identifier": "my_first_xsd", - "identifierType": "INTERNAL" - }, - "schemaVersion": "2", - "acl": [ { - "id":null, - "sid":"guest", - "permission":"READ" - } ] + "id": "3efdd6fe-429d-40a6-acff-c7c40631d508", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] + "acls": [ + [...] + { + "sid": "guest", + "permission": "READ" + } + ] } ---- [source,options="nowrap"] @@ -722,14 +677,21 @@ metadata-v2.xml: ---- include::{snippets}/v2-update-metadata-record-v2/curl-request.adoc[] -You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: +You can see, that the schema was set to version 2 (allowing additional field +for date) and the ACL entry for "guest" was added. All other properties are still +the same. HTTP-wise the call looks as follows: include::{snippets}/v2-update-metadata-record-v2/http-request.adoc[] -You will get the new datacite record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. +The response provides the updated datacite record: + include::{snippets}/v2-update-metadata-record-v2/http-response.adoc[] -What you see is, that the datacite record looks different from the original document. +You will get the updated datacite record with the following changes: +- new schema (version) +- an additional ACL entry +- 'version' of record was incremented by one +- 'lastUpdate' was also modified by the server. ==== Updating Datacite Record & Document Repeat the last step and update to the current version. As mentioned before the @@ -749,15 +711,18 @@ Now you can update metadata due to new version of schema using the new Etag. ---- metadata-record-v3.json: { - "relatedResource": { - "identifier": "anyResourceId", - "identifierType": "INTERNAL" - }, - "schema": { - "identifier": "my_first_xsd", - "identifierType": "INTERNAL" - }, - "schemaVersion": "3" + "id": "3efdd6fe-429d-40a6-acff-c7c40631d508", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "INTERNAL", + "value": "my_first_xsd", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] } ---- [source,options="nowrap"] @@ -1298,7 +1263,7 @@ After the update the following fields has changed: - version number increased by one. - lastUpdate to the date of the last update (set by server) -- acl additional ACL entry (set during update) +- acls additional ACL entry (set during update) === Metadata Management After registration of a schema metadata may be added to MetaStore. @@ -1377,15 +1342,33 @@ The following example shows the creation of the first datacite record and its me ---- metadata-record4json.json: { - "relatedResource": { - "identifier": "anyResourceId", - "identifierType": "INTERNAL" - }, - "schema": { - "identifier": "my_first_json", - "identifierType": "INTERNAL" + "titles": [ + { + "id": null, + "value": "Title of first metadata document", + "titleType": null, + "lang": null + } + ], + "publisher": null, + "publicationYear": null, + "resourceType": { + "id": null, + "value": "JSON_Metadata", + "typeGeneral": "MODEL" + }, + "relatedIdentifiers": [ + { + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", + "relationType": "IS_DERIVED_FROM" }, - "schemaVersion": 1 + { + "identifierType": "URL", + "value": "https://repo/anyResourceId", + "relationType": "IS_METADATA_FOR" + } + ] } ---- [source,options="nowrap"] @@ -1408,7 +1391,7 @@ provided document, adding missing information where possible and persisting the include::{snippets}/v2-ingest-json-metadata-document/http-response.adoc[] -What you see is, that the datacite record looks different from the original document. All remaining elements received a value by the server. +What you see is, that the datacite record looks different from the original document. Some of the elements received a value by the server. In the header you'll find a location URL to access the ingested metadata and an ETag with the current ETag of the resource. This value is returned by POST, GET and PUT calls and must be provided for all calls modifying the resource, e.g. POST, PUT and DELETE, in order to avoid conflicts. @@ -1452,20 +1435,25 @@ ETag is needed: ---- metadata-record4json-v2.json: { - "relatedResource": { - "identifier": "anyResourceId", - "identifierType": "INTERNAL" - }, - "schema": { - "identifier": "my_first_json", - "identifierType": "INTERNAL" - }, - "schemaVersion": "2", - "acl": [ { - "id":null, - "sid":"guest", - "permission":"READ" - } ] + "id": "d5439ccf-af4c-4727-b45b-1aa8b949d60e", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] + "acls": [ + [...] + { + "sid": "guest", + "permission": "READ" + } + ] } ---- [source,options="nowrap"] @@ -1478,14 +1466,21 @@ metadata-v2.json: ---- include::{snippets}/v2-update-json-metadata-record-v2/curl-request.adoc[] -You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: +You can see, that the schema was set to version 2 (allowing additional field +for date) and the ACL entry for "guest" was added. All other properties are still +the same. HTTP-wise the call looks as follows: include::{snippets}/v2-update-json-metadata-record-v2/http-request.adoc[] -You will get the new datacite record with the additional ACL entry. Version number of record was incremented by one and 'lastUpdate' was also modified by the server. +The response provides the updated datacite record: + include::{snippets}/v2-update-json-metadata-record-v2/http-response.adoc[] -What you see is, that the datacite record looks different from the original document. +You will get the updated datacite record with the following changes: +- new schema (version) +- an additional ACL entry +- 'version' of record was incremented by one +- 'lastUpdate' was also modified by the server. ==== Updating Datacite Record of Metadata Document & Document Repeat the last step and update to the current version. As mentioned before the @@ -1505,15 +1500,25 @@ Now you can update metadata due to new version of schema using the new Etag. ---- metadata-record4json-v3.json: { - "relatedResource": { - "identifier": "anyResourceId", - "identifierType": "INTERNAL" - }, - "schema": { - "identifier": "my_first_json", - "identifierType": "INTERNAL" - }, - "schemaVersion": "3" + "id": "d5439ccf-af4c-4727-b45b-1aa8b949d60e", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "INTERNAL", + "value": "my_first_json", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] + "acls": [ + [...] + { + "sid": "guest", + "permission": "READ" + } + ] } ---- [source,options="nowrap"] diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java index b42b52a9..9599855f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java @@ -434,7 +434,7 @@ public void documentSchemaRegistry4Json() throws Exception { //************************************************************************** // Create a metadata record. DataResource metadataRecord = new DataResource(); - SchemaRegistryControllerTestV2.setTitle(metadataRecord, "Title of first metadata document"); + SchemaRegistryControllerTestV2.setTitle(metadataRecord, "Title of first JSON metadata document"); metadataRecord.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.JSON_METADATA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); // record.setId("my_id"); @@ -484,6 +484,7 @@ public void documentSchemaRegistry4Json() throws Exception { mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); + record.getAcls().add(new AclEntry("guest", PERMISSION.READ)); SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV2); recordFile = new MockMultipartFile("record", "metadata-record4json-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v2.json", "application/xml", DOCUMENT_V2.getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java index 148f4f91..6548ae06 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTestV2.java @@ -466,6 +466,7 @@ public void documentSchemaRegistry() throws Exception { mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); + record.getAcls().add(new AclEntry("guest", PERMISSION.READ)); SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV2); recordFile = new MockMultipartFile("record", "metadata-record-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v2.xml", "application/xml", DOCUMENT_V2.getBytes()); @@ -491,7 +492,7 @@ public void documentSchemaRegistry() throws Exception { andDo(document("v2-get-metadata-record-v2")). andExpect(status().isOk()). andReturn().getResponse(); - SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV3); + SchemaRegistryControllerTestV2.setRelatedSchema(record, EXAMPLE_SCHEMA_ID); recordFile = new MockMultipartFile("record", "metadata-record-v3.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v3.xml", "application/xml", DOCUMENT_V3.getBytes()); From 35df58bf5dac4bba735d8e27b52118e583139431 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 3 May 2024 12:15:22 +0200 Subject: [PATCH 037/181] Finalize documentation API v2. --- docs/documentationV2.adoc | 45 ++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/docs/documentationV2.adoc b/docs/documentationV2.adoc index 6a829e34..9a52441b 100644 --- a/docs/documentationV2.adoc +++ b/docs/documentationV2.adoc @@ -380,6 +380,10 @@ As a result, you receive the XSD schema document sent before: include::{snippets}/v2-get-schema-v3/http-response.adoc[] +[NOTE] +For accessing schema document you have to provide +'application/xml' as 'Accept' header. + ==== Getting a specific Version of Metadata Schema Document To get a specific version of the metadata schema document just send an HTTP GET with the linked @@ -395,6 +399,9 @@ As a result, you receive the initial XSD schema document (version 1). include::{snippets}/v2-get-schema-v1/http-response.adoc[] +[NOTE] +As before you have to provide 'application/XML' as 'Accept' header. + ==== Validating Metadata Document Before an ingest of metadata is made the metadata should be successfully validated. Otherwise @@ -419,7 +426,6 @@ Same for the HTTP request. The schemaVersion number is set by a query parameter. include::{snippets}/v2-validate-document-v1/http-request.adoc[] As a result, you receive 422 as HTTP status and an error message holding some information about the error. -(Unfortunately not documented here due to technical reasons.) include::{snippets}/v2-validate-document-v1/http-response.adoc[] @@ -455,7 +461,6 @@ schema-record-v4.json "permission": "ADMINISTRATE" }, { - "id": null, "sid": "admin", "permission": "ADMINISTRATE" } @@ -551,7 +556,7 @@ License URI is optional. It's new since 1.4.2. ==== Register/Ingest a Datacite Record with Metadata Document -The following example shows the creation of the first datacite record and its metadata only providing mandatory fields mentioned above: +The following example shows the creation of the first metadata document and its datacite record only providing mandatory fields mentioned above: [source,options="nowrap"] ---- metadata-record.json: @@ -619,6 +624,10 @@ include::{snippets}/v2-get-metadata-document/http-response.adoc[] What you see is, that the metadata is untouched. +[NOTE] +For accessing metadata document you have to provide +'application/xml' as 'Accept' header. + ==== Accessing Datacite Record of Metadata Document For accessing the datacite record the same URL as before has to be used. The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". @@ -693,7 +702,7 @@ You will get the updated datacite record with the following changes: - 'version' of record was incremented by one - 'lastUpdate' was also modified by the server. -==== Updating Datacite Record & Document +==== Updating Datacite Record of Metadata Document & Document Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. @@ -725,6 +734,11 @@ metadata-record-v3.json: [...] } ---- + +[NOTE] +In contrast to the previous update, the INTERNAL identifier is used. +This always refers to the latest version of the schema (in our case version 3). + [source,options="nowrap"] ---- metadata-v3.xml: @@ -1168,6 +1182,10 @@ As a result, you receive the XSD schema document sent before: include::{snippets}/v2-get-json-schema-v3/http-response.adoc[] +[NOTE] +For accessing schema document you have to provide +'application/json' as 'Accept' header. + ==== Getting a specific Version of Metadata Schema Document To get a specific version of the metadata schema document just send an HTTP GET with the linked @@ -1183,6 +1201,9 @@ As a result, you receive the initial XSD schema document (version 1). include::{snippets}/v2-get-json-schema-v1/http-response.adoc[] +[NOTE] +As before you have to provide 'application/json' as 'Accept' header. + ==== Validating Metadata Document Before an ingest of metadata is made the metadata should be successfully validated. Otherwise @@ -1241,7 +1262,6 @@ schema-record4json-v4.json "permission": "ADMINISTRATE" }, { - "id": null, "sid": "admin", "permission": "ADMINISTRATE" } @@ -1337,23 +1357,19 @@ License URI is optional. It's new since 1.4.2. ==== Register/Ingest a Datacite Record with Metadata Document -The following example shows the creation of the first datacite record and its metadata only providing mandatory fields mentioned above: +The following example shows the creation of the first metadata document and its datacite record only providing mandatory fields mentioned above: [source,options="nowrap"] ---- metadata-record4json.json: { "titles": [ { - "id": null, "value": "Title of first metadata document", - "titleType": null, - "lang": null } ], "publisher": null, "publicationYear": null, "resourceType": { - "id": null, "value": "JSON_Metadata", "typeGeneral": "MODEL" }, @@ -1410,6 +1426,10 @@ include::{snippets}/v2-get-json-metadata-document/http-response.adoc[] What you see is, that the metadata is untouched. +[NOTE] +For accessing metadata document you have to provide +'application/json' as 'Accept' header. + ==== Accessing Datacite Record of Metadata Document For accessing the datacite record the same URL as before has to be used. The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". @@ -1521,6 +1541,11 @@ metadata-record4json-v3.json: ] } ---- + +[NOTE] +In contrast to the previous update, the INTERNAL identifier is used. +This always refers to the latest version of the schema (in our case version 3). + [source,options="nowrap"] ---- metadata-v3.json: From 814310c46ddd0bf28ae21d5af2178dd6afaae210 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 3 May 2024 13:29:20 +0200 Subject: [PATCH 038/181] Fix SchemaUrls for metadata documents. --- .../web/impl/MetadataControllerImplV2.java | 7 +++---- .../test/MetadataControllerTestV2.java | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 41624785..2fc3fd43 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -30,18 +30,14 @@ import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.AclRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.domain.SchemaRecord; import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; -import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.IMetadataControllerV2; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; import edu.kit.datamanager.repo.domain.ContentInformation; @@ -440,6 +436,9 @@ public ResponseEntity<List<DataResource>> getRecords( LOG.trace("Transforming Dataresource to DataResource"); List<DataResource> recordList = records.getContent(); + for (DataResource item : recordList) { + DataResourceRecordUtil.fixSchemaUrl(item); + } String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index c55313d8..2f18a5f0 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -936,6 +936,27 @@ public void testGetRecordById() throws Exception { Assert.assertNotEquals("file:///tmp/dc.xml", locationUri); } + @Test + public void testGetRecords() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + Assert.assertNotNull(result); + String locationUri = res.getResponse().getHeader("Location"); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); + //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema + Assert.assertNotEquals("file:///tmp/dc.xml", locationUri); + } + } + @Test public void testGetRecordByIdWithVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); From fae0cdd7885394017c8f202f46a3671b582e6194 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 3 May 2024 14:07:02 +0200 Subject: [PATCH 039/181] Add more tests regarding schemaURL while filtering metadata documents. --- .../test/MetadataControllerTestV2.java | 138 +++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 2f18a5f0..c5629ce2 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -1008,7 +1008,6 @@ public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { Assert.assertEquals(1, result.length); } -// ToDo @Test public void testFindRecordsBySchemaId() throws Exception { @@ -1018,6 +1017,14 @@ public void testFindRecordsBySchemaId() throws Exception { DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); + } } @Test @@ -1059,6 +1066,13 @@ public void testFindRecordsOfMultipleVersionsBySchemaId() throws Exception { result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(3, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + } } @Test @@ -1076,6 +1090,16 @@ public void testFindRecordsByResourceId() throws Exception { result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertEquals(RELATED_RESOURCE.getIdentifier(), resourceIdentifier.getValue()); + } } @Test @@ -1098,8 +1122,18 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); ObjectMapper map = new ObjectMapper(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); - // Looking for second schema Assert.assertEquals(2, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); + } + // Looking for second schema res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", secondSchemaId)). andDo(print()). @@ -1107,6 +1141,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); + } // Looking for first AND second schema res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", firstSchemaId). @@ -1116,6 +1160,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(4, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); + } // Looking for first, second AND invalid schema res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", firstSchemaId). @@ -1126,6 +1180,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(4, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); + } // Looking for first, second AND invalid schema AND resource1 and resource2 res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", firstSchemaId). @@ -1138,6 +1202,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(4, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); + } res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", firstSchemaId). @@ -1148,6 +1222,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); + } res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", firstSchemaId). @@ -1157,6 +1241,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertEquals(relatedResource, resourceIdentifier.getValue()); + } res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", firstSchemaId). @@ -1166,6 +1260,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertEquals(relatedResource2, resourceIdentifier.getValue()); + } res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", secondSchemaId). @@ -1176,6 +1280,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); + } res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", secondSchemaId). @@ -1185,6 +1299,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertEquals(relatedResource, resourceIdentifier.getValue()); + } res = this.mockMvc.perform(get(API_METADATA_PATH). param("schemaId", secondSchemaId). @@ -1194,6 +1318,16 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); + for (DataResource dataResource : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + Assert.assertEquals(IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = schemaIdentifier.getValue(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); + Assert.assertEquals(relatedResource2, resourceIdentifier.getValue()); + } } @Test From 170f211ad9b1903c32a9687baf4527c7cd4e7bca Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 15 May 2024 08:49:10 +0200 Subject: [PATCH 040/181] Fix response message --- .../datamanager/metastore2/util/DataResourceRecordUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 82e45a5b..abffe1eb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -380,7 +380,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur try { metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); } catch (IOException ex) { - String message = "Can't map record document to MetadataRecord"; + String message = "Can't map record document to DataResource"; if (ex instanceof JsonParseException) { message = message + " Reason: " + ex.getMessage(); } @@ -1524,7 +1524,7 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu try { metadataRecord = Json.mapper().readValue(dataResourceRecord.getInputStream(), DataResource.class); } catch (IOException ex) { - message = "Can't map record document to MetadataRecord"; + message = "Can't map record document to DataResource"; if (ex instanceof JsonParseException) { message = message + " Reason: " + ex.getMessage(); } From bd68daea1d56d47945f546ecd8a3b4f131fd0fc1 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 15 May 2024 15:12:15 +0200 Subject: [PATCH 041/181] Fix test with URL reference to schema. --- .../datamanager/metastore2/test/MetadataControllerTestV2.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index c55313d8..b326b492 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -124,6 +124,7 @@ public class MetadataControllerTestV2 { private static final String SCHEMA_ID = "my_dc"; private static final String JSON_SCHEMA_ID = "my_json"; private static final String JSON_HTTP_SCHEMA_ID = "my_json_with_http"; + private static final String JSON_HTTP_SCHEMA_ID_URL = "http://localhost:41421/api/v2/schemas/my_json_with_http?version=1"; private static final String JSON_HTTP_SCHEMA_ID_WITH_HASH = "my_json_with_hash"; private static final String INVALID_SCHEMA = "invalid_dc"; private static final String UNKNOWN_RELATED_RESOURCE = "unknownHResourceId"; @@ -292,7 +293,7 @@ public void testCreateRecord() throws Exception { public void testCreateRecordWithHttpSchema() throws Exception { ingestHttpJsonSchemaRecord(); String id = "testCreateRecordWithHttpSchema"; - String schemaId = JSON_HTTP_SCHEMA_ID; + String schemaId = JSON_HTTP_SCHEMA_ID_URL; DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(id, schemaId); Set<AclEntry> aclEntries = new HashSet<>(); // aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); From 12088e1a4814ff944f7933a59a7754af893fd2ad Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 19 Jun 2024 16:34:23 +0200 Subject: [PATCH 042/181] Fix tests --- .../util/DataResourceRecordUtil.java | 7 +- .../web/impl/MetadataControllerImplV2.java | 6 +- .../impl/SchemaRegistryControllerImplV2.java | 101 +++++++++++------- .../test/MetadataControllerTestV2.java | 7 +- .../test/SchemaRegistryControllerTestV2.java | 30 +++++- 5 files changed, 104 insertions(+), 47 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 82e45a5b..6f0652a6 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -220,6 +220,8 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio throw new UnprocessableEntityException(message); } } + // reload data resource + metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(applicationProperties, metadataRecord.getId(), Long.valueOf(metadataRecord.getVersion())); return metadataRecord; } @@ -261,7 +263,8 @@ public static DataResource createDataResourceRecord4Metadata(MetastoreConfigurat DataResource dataResource = metadataRecord; DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); // store document - ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); + ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); + metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(applicationProperties, metadataRecord.getId(), Long.valueOf(metadataRecord.getVersion())); return metadataRecord; } @@ -391,6 +394,8 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); + LOG.trace("Get ETag of DataResource."); + LOG.trace("Get dataresource: '{}'", dataResource.toString()); LOG.trace("ETag: '{}'", dataResource.getEtag()); LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, dataResource); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 2fc3fd43..07d5fdfe 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -231,7 +231,9 @@ public ResponseEntity createRecord( // return ResponseEntity.status(HttpStatus.CONFLICT).body(message); // } DataResource result = DataResourceRecordUtil.createDataResourceRecord4Metadata(metadataConfig, recordDocument, document); + LOG.trace("Get dataresource: '{}'", result.toString()); String eTag = result.getEtag(); + LOG.trace("Get ETag: ' {}'", eTag); // Successfully created metadata record. // long nano4 = System.nanoTime() / 1000000; LOG.trace("Metadata record successfully persisted. Returning result."); @@ -266,7 +268,9 @@ public ResponseEntity<DataResource> getRecordById( LOG.trace("Metadata record found. Prepare response."); //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN LOG.trace("Get ETag of DataResource."); + LOG.trace("Get dataresource: '{}'", metadataRecord.toString()); String etag = metadataRecord.getEtag(); + LOG.trace("Get ETag: ' {}'", etag); DataResourceRecordUtil.fixSchemaUrl(metadataRecord); URI locationUri; locationUri = DataResourceRecordUtil.getMetadataDocumentUri(metadataRecord.getId(), metadataRecord.getVersion()); @@ -288,7 +292,7 @@ public ResponseEntity<ContentInformation> getContentInformationById( LOG.trace("ContentInformation record found. Prepare response..."); DataResource minimalDataResource = DataResource.factoryNewDataResource(contentInformation.getParentResource().getId()); URI locationUri; - locationUri = DataResourceRecordUtil.getMetadataDocumentUri(id, contentInformation.getFileVersion()); + locationUri = DataResourceRecordUtil.getMetadataDocumentUri(id, contentInformation.getVersion().toString()); contentInformation.setParentResource(minimalDataResource); contentInformation.setContentUri(locationUri.toString()); contentInformation.setRelativePath(null); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 6c500ceb..d5a80b04 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -15,12 +15,15 @@ */ package edu.kit.datamanager.metastore2.web.impl; +import static co.elastic.clients.elasticsearch._types.mapping.PropertyBuilders.object; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.exceptions.ResourceNotFoundException; import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; @@ -29,7 +32,6 @@ import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.TitleSpec; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.util.AuthenticationHelper; @@ -73,6 +75,7 @@ import edu.kit.datamanager.metastore2.web.ISchemaRegistryControllerV2; import edu.kit.datamanager.repo.domain.ContentInformation; import java.nio.file.Path; +import java.util.logging.Level; /** * Controller for schema documents. @@ -82,13 +85,13 @@ @Tag(name = "Schema Registry") @Schema(description = "Schema Registry") public class SchemaRegistryControllerImplV2 implements ISchemaRegistryControllerV2 { - + private static final Logger LOG = LoggerFactory.getLogger(SchemaRegistryControllerImplV2.class); - + private final ApplicationProperties applicationProperties; - + private final MetastoreConfiguration schemaConfig; - + private final IDataResourceDao dataResourceDao; /** @@ -109,7 +112,7 @@ public SchemaRegistryControllerImplV2(ApplicationProperties applicationPropertie LOG.info("------{}", schemaConfig); LOG.info("------------------------------------------------------"); } - + @Override public ResponseEntity<DataResource> createRecord( @RequestPart(name = "record") final MultipartFile recordDocument, @@ -120,18 +123,28 @@ public ResponseEntity<DataResource> createRecord( LOG.trace("Performing createRecord({},....", recordDocument); BiFunction<String, Long, String> getSchemaDocumentById; getSchemaDocumentById = (schema, version) -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getSchemaDocumentById(schema, version, null, null)).toString(); - + DataResource dataResourceRecord = DataResourceRecordUtil.createDataResourceRecord4Schema(schemaConfig, recordDocument, document); LOG.trace("Schema record successfully persisted. Returning result."); String etag = dataResourceRecord.getEtag(); - + if (LOG.isTraceEnabled()) { + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + String json; + try { + json = ow.writeValueAsString(dataResourceRecord); + LOG.trace(json); + } catch (JsonProcessingException ex) { + java.util.logging.Logger.getLogger(SchemaRegistryControllerImplV2.class.getName()).log(Level.SEVERE, null, ex); + } + } + LOG.trace("Schema record successfully persisted."); URI locationUri; locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(dataResourceRecord); LOG.trace("Set locationUri to '{}'", locationUri.toString()); return ResponseEntity.created(locationUri).eTag("\"" + etag + "\"").body(dataResourceRecord); } - + @Override public ResponseEntity<DataResource> getRecordById( @PathVariable(value = "schemaId") String schemaId, @@ -139,15 +152,15 @@ public ResponseEntity<DataResource> getRecordById( WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing getRecordById({}, {}).", schemaId, version); - + LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); String etag = schemaRecord.getEtag(); - + LOG.trace("Returning result."); return ResponseEntity.ok().eTag("\"" + etag + "\"").body(schemaRecord); } - + @Override public ResponseEntity<ContentInformation> getContentInformationById( @PathVariable(value = "schemaId") String schemaId, @@ -155,14 +168,19 @@ public ResponseEntity<ContentInformation> getContentInformationById( WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing getContentInformationById({}, {}).", schemaId, version); - + LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); ContentInformation contentInformation = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaId, version); - - LOG.trace("Returning result."); + DataResource minimalDataResource = DataResource.factoryNewDataResource(contentInformation.getParentResource().getId()); + URI locationUri; + locationUri = DataResourceRecordUtil.getMetadataDocumentUri(schemaId, contentInformation.getVersion().toString()); + contentInformation.setParentResource(minimalDataResource); + contentInformation.setContentUri(locationUri.toString()); + contentInformation.setRelativePath(null); + contentInformation.setVersioningService(null); return ResponseEntity.ok().body(contentInformation); } - + @Override public ModelAndView getLandingPageById(@PathVariable(value = "schemaId") String id, @RequestParam(value = "version", required = false) Long version, @@ -176,12 +194,12 @@ public ModelAndView getLandingPageById(@PathVariable(value = "schemaId") String versionString = version.toString(); } redirectUrl = "redirect:" + redirectUrl.replace(MetadataControllerImpl.PLACEHOLDER_VERSION, versionString); - + LOG.trace("Redirect to '{}'", redirectUrl); - + return new ModelAndView(redirectUrl); } - + @Override public ResponseEntity getSchemaDocumentById( @PathVariable(value = "schemaId") String schemaId, @@ -189,7 +207,7 @@ public ResponseEntity getSchemaDocumentById( WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing getSchemaDocumentById({}, {}).", schemaId, version); - + LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); ContentInformation contentInfo = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaRecord.getId(), Long.valueOf(schemaRecord.getVersion())); @@ -200,14 +218,14 @@ public ResponseEntity getSchemaDocumentById( LOG.trace("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Schema document on server either does not exist or is no file or is not readable."); } - + return ResponseEntity. ok(). contentType(contentType). header(HttpHeaders.CONTENT_LENGTH, String.valueOf(schemaDocumentPath.toFile().length())). body(new FileSystemResource(schemaDocumentPath.toFile())); } - + public ResponseEntity<List<DataResource>> getAllVersions( String id, Pageable pgbl @@ -229,12 +247,12 @@ public ResponseEntity<List<DataResource>> getAllVersions( } catch (ResourceNotFoundException rnfe) { LOG.info("Schema ID '{}' is unkown. Return empty list...", id); } - + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); - + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); } - + @Override public ResponseEntity validate(@PathVariable(value = "schemaId") String schemaId, @RequestParam(value = "version", required = false) Long version, @@ -246,7 +264,7 @@ public ResponseEntity validate(@PathVariable(value = "schemaId") String schemaId LOG.trace("Metadata document validation succeeded. Returning HTTP NOT_CONTENT."); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } - + @Override public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "schemaId", required = false) String schemaId, @RequestParam(value = "mimeType", required = false) List<String> mimeTypes, @@ -278,12 +296,13 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche } if (searchForJson && !searchForXml) { resourceType = ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); - } + } if (!searchForJson && searchForXml) { resourceType = ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); } - if (!searchForJson && !searchForXml) + if (!searchForJson && !searchForXml) { resourceType = ResourceType.createResourceType("unknown"); + } } Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); // Add authentication if enabled @@ -295,7 +314,7 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; List<DataResource.State> stateList = Arrays.asList(states); spec = spec.and(StateSpecification.toSpecification(stateList)); - + LOG.debug("Performing query for records."); Page<DataResource> records = null; try { @@ -311,12 +330,12 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche LOG.trace("---> " + item.toString()); } } - + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); - + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); } - + @Override public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final String schemaId, @RequestPart(name = "record", required = false) MultipartFile schemaRecord, @@ -327,7 +346,7 @@ public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); String eTag = ControllerUtils.getEtagFromHeader(request); DataResource updatedSchemaRecord = DataResourceRecordUtil.updateMetadataSchemaRecord(schemaConfig, schemaId, eTag, schemaRecord, document, getById); - + LOG.trace("DataResource record successfully persisted. Updating document URI and returning result."); String etag = updatedSchemaRecord.getEtag(); // Fix Url for OAI PMH entry @@ -338,7 +357,7 @@ public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final LOG.trace("Set locationUri to '{}'", locationUri.toString()); return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updatedSchemaRecord); } - + @Override public ResponseEntity deleteRecord(@PathVariable("schemaId") final String schemaId, WebRequest request, @@ -347,25 +366,25 @@ public ResponseEntity deleteRecord(@PathVariable("schemaId") final String schema UnaryOperator<String> getById; getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, hsr)).toString(); String eTag = ControllerUtils.getEtagFromHeader(request); - + MetadataSchemaRecordUtil.deleteMetadataSchemaRecord(schemaConfig, schemaId, eTag, getById); - + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } - + @Override public void contribute(Info.Builder builder) { LOG.trace("Check for SchemaRepo actuator information..."); - + URL basePath = schemaConfig.getBasepath(); Map<String, String> details = ActuatorUtil.testDirectory(basePath); - + if (!details.isEmpty()) { details.put("No of schema documents", Long.toString(MetadataSchemaRecordUtil.getNoOfSchemas())); builder.withDetail("schemaRepo", details); } } - + private Specification<DataResource> addAuthenticationSpecification(Specification<DataResource> spec) { if (schemaConfig.isAuthEnabled()) { boolean isAdmin; diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index c5629ce2..a36b7bfd 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -1445,7 +1445,8 @@ public void testUpdateRecord() throws Exception { Assert.assertEquals(contentInformation1.getVersion() + 1, contentInformation2.getVersion().longValue()); Assert.assertNotEquals(contentInformation1, contentInformation2); Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); - Assert.assertNotEquals(contentInformation1.getFileVersion(), contentInformation2.getFileVersion()); + Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); + Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); @@ -1637,10 +1638,10 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); - Assert.assertEquals(contentInformation1.getVersion() + 1, contentInformation2.getVersion().longValue()); Assert.assertNotEquals(contentInformation1, contentInformation2); Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); - Assert.assertNotEquals(contentInformation1.getFileVersion(), contentInformation2.getFileVersion()); + Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); + Assert.assertEquals((long)(contentInformation1.getVersion() + 1), contentInformation2.getVersion().longValue()); Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); Assert.assertEquals(DC_DOCUMENT.length(), contentInformation1.getSize()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 2ee794b6..d5050784 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1241,6 +1241,17 @@ public void testUpdateOnlyDocument() throws Exception { ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); + + // Get ContentInformation of first version + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH + schemaId). @@ -1249,7 +1260,7 @@ public void testUpdateOnlyDocument() throws Exception { with(putMultipart())). andDo(print()). andExpect(status().isOk()). - andExpect(redirectedUrlPattern("http://*:*/**/" + record.getId() + "?version=*")). + andExpect(redirectedUrlPattern("http://*:*/**/" + schemaId + "?version=*")). andReturn(); body = result.getResponse().getContentAsString(); @@ -1270,6 +1281,23 @@ public void testUpdateOnlyDocument() throws Exception { String content = result.getResponse().getContentAsString(); Assert.assertEquals(KIT_SCHEMA_V2, content); + // Test also contentInformation after update + result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); + Assert.assertEquals(contentInformation1.getVersion() + 1, contentInformation2.getVersion().longValue()); + Assert.assertNotEquals(contentInformation1, contentInformation2); + Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); + Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); + Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); + Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); + Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); } @Test From 15ce58a5bcd6889631527bf64f4aa8887833ff5f Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 24 Jun 2024 12:29:58 +0200 Subject: [PATCH 043/181] Fix test for documentation. --- .../SchemaRegistryControllerDocumentation4JsonTestV2.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java index 28fc1255..9908c355 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTestV2.java @@ -314,8 +314,7 @@ public void documentSchemaRegistry4Json() throws Exception { andDo(document("v2-update-json-schema-v3")). andExpect(status().isOk()). andReturn(); - etag = result.getResponse().getHeader("ETag"); - String exampleSchemaV3 = result.getResponse().getHeader("ETag"); + String exampleSchemaV3 = result.getResponse().getHeader("Location"); // 6. Registering another metadata schema //************************************************************************** schemaRecord.setId(ANOTHER_SCHEMA_ID); @@ -504,7 +503,7 @@ public void documentSchemaRegistry4Json() throws Exception { andReturn().getResponse(); SchemaRegistryControllerTestV2.setRelatedSchema(record, exampleSchemaV3); recordFile = new MockMultipartFile("record", "metadata-record4json-v3.json", "application/json", mapper.writeValueAsString(record).getBytes()); - metadataFile = new MockMultipartFile("document", "metadata-v3.json", "application/xml", DOCUMENT_V3.getBytes()); + metadataFile = new MockMultipartFile("document", "metadata-v3.json", "application/json", DOCUMENT_V3.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(newLocation). file(recordFile). From 6e9db0c663d8648adfe0ae7f8b79cd4f02658a0b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 25 Jun 2024 14:30:27 +0200 Subject: [PATCH 044/181] Refactoring update metadata documents. --- .../util/DataResourceRecordUtil.java | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 6f0652a6..2c593ca5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -371,35 +371,19 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur MultipartFile document, UnaryOperator<String> supplier) { DataResource metadataRecord = null; + metadataRecord = checkParameters(recordDocument, document, false); DataResource updatedDataResource; - // Do some checks first. - if ((recordDocument == null || recordDocument.isEmpty()) && (document == null || document.isEmpty())) { - String message = "Neither metadata record nor metadata document provided."; - LOG.error(message); - throw new BadArgumentException(message); - } - if (!(recordDocument == null || recordDocument.isEmpty())) { - try { - metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), DataResource.class); - } catch (IOException ex) { - String message = "Can't map record document to MetadataRecord"; - if (ex instanceof JsonParseException) { - message = message + " Reason: " + ex.getMessage(); - } - LOG.error(ERROR_PARSING_JSON, ex); - throw new BadArgumentException(message); - } - } LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); - LOG.trace("Get ETag of DataResource."); - LOG.trace("Get dataresource: '{}'", dataResource.toString()); - LOG.trace("ETag: '{}'", dataResource.getEtag()); LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, dataResource); + LOG.trace("ETag: '{}'", dataResource.getEtag()); if (metadataRecord != null) { + LOG.trace("metadataRecord: '{}'", metadataRecord.toString()); + metadataRecord.setVersion(dataResource.getVersion()); + metadataRecord.setId(dataResource.getId()); updatedDataResource = metadataRecord; if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { updatedDataResource.setAcls(dataResource.getAcls()); @@ -411,8 +395,6 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur updatedDataResource = DataResourceUtils.copyDataResource(dataResource); } - LOG.trace("ETag: '{}'", dataResource.getEtag()); - boolean noChanges = false; if (document != null) { SchemaRecord schemaRecord = getSchemaRecordFromDataResource(updatedDataResource); @@ -1710,6 +1692,8 @@ private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataRes SchemaRecord schemaRecord = null; RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataResource); String schemaId = schemaIdentifier.getValue(); + LOG.trace("getSchemaRecordFromDataResource: related identifier: '{}'", schemaIdentifier.toString()); + LOG.trace("getSchemaRecordFromDataResource: '{}'", schemaId); switch (schemaIdentifier.getIdentifierType()) { case URL: schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); From cc55d6f516c807f9a5400315bf909dc2c4346331 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 19 Jul 2024 10:57:40 +0200 Subject: [PATCH 045/181] Refine error message for wrong schema URL. --- .../metastore2/web/impl/MetadataControllerImplV2.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 07d5fdfe..b6150552 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -202,7 +202,8 @@ public ResponseEntity createRecord( SchemaRecord schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); if (schemaRecord == null) { String message = "External URLs are not supported yet!\n" - + "But '" + schemaIdentifier.getValue() + "' seems not to be an internal one!"; + + "But '" + schemaIdentifier.getValue() + "' seems not to be an internal one!\n" + + "Hint: Maybe version number is missing (e.g.: [...]?version=1"; LOG.error(message); throw new ResourceNotFoundException(message); } From 105e70cc6af4b06a89f76e51f9ab187acefccf1d Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 19 Jul 2024 12:27:10 +0200 Subject: [PATCH 046/181] Adapting tests to the new API. --- ...rollerTestWithAuthenticationEnabledV2.java | 1852 +++++++++++++++++ 1 file changed, 1852 insertions(+) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java new file mode 100644 index 00000000..764c9a91 --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -0,0 +1,1852 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.kit.datamanager.entities.Identifier; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.IDataRecordDao; +import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import org.javers.core.Javers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + * + * @author Torridity + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41428"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_md_aai_v2;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/md/aai/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/md/aai/metadata"}) +@TestPropertySource(properties = {"repo.auth.enabled=true"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class MetadataControllerTestWithAuthenticationEnabledV2 { + + private static final String API_BASE_PATH = "/api/v2"; + private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; + private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; + private static final String API_METADATA_PATH = API_BASE_PATH + "/metadata/"; + + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/md/aai/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private static final String METADATA_RECORD_ID = "test_id"; + private static final String SCHEMA_ID = "my_dc"; + private static final String INVALID_SCHEMA = "invalid_dc"; + private static final String RELATED_RESOURCE_STRING = "anyResourceId"; + private static final ResourceIdentifier RELATED_RESOURCE = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING); + private static final ResourceIdentifier RELATED_RESOURCE_2 = ResourceIdentifier.factoryUrlResourceIdentifier("anyOtherResourceId"); + private final static String KIT_SCHEMA = CreateSchemaUtil.KIT_SCHEMA; + + private final static String KIT_DOCUMENT = CreateSchemaUtil.KIT_DOCUMENT; + private final static String KIT_DOCUMENT_VERSION_2 = CreateSchemaUtil.KIT_DOCUMENT_VERSION_2; + private final static String KIT_DOCUMENT_WRONG_NAMESPACE = CreateSchemaUtil.KIT_DOCUMENT_WRONG_NAMESPACE; + private final static String KIT_DOCUMENT_INVALID = CreateSchemaUtil.KIT_DOCUMENT_INVALID_1; + + private String adminToken; + private String curatorToken; + private String userToken; + private String otherUserToken; + private String guestToken; + + private final String adminPrincipal = "admin"; + private final String curatorPrincipal = "curator"; + private final String userPrincipal = "user"; + private final String otherUserPrincipal = "other_user"; + private final String guestPrincipal = "guest"; + + private DataResource sampleResource; + private static Boolean alreadyInitialized = Boolean.FALSE; + + private MockMvc mockMvc; + @Autowired + private ApplicationProperties applicationProperties; + @Autowired + private WebApplicationContext context; + @Autowired + Javers javers = null; + @Autowired + private ILinkedMetadataRecordDao metadataRecordDao; + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private IDataRecordDao dataRecordDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + @Autowired + private IUrl2PathDao url2PathDao; + @Autowired + private MetastoreConfiguration metadataConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + @Before + public void setUp() throws Exception { + System.out.println("------MetadataControllerTest--------------------------"); + System.out.println("------" + this.metadataConfig); + System.out.println("------------------------------------------------------"); + + // setup mockMvc + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation).uris() + .withPort(41428)) + .build(); + adminToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(adminPrincipal, RepoUserRole.ADMINISTRATOR). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("groupid", "USERS"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + curatorToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(curatorPrincipal, RepoUserRole.ADMINISTRATOR). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(userPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + otherUserToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(otherUserPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + + guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(guestPrincipal, RepoUserRole.GUEST). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + metadataRecordDao.deleteAll(); + schemaRecordDao.deleteAll(); + dataRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + url2PathDao.deleteAll(); + + try { + // Create schema only once. + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + Paths.get(TEMP_DIR_4_SCHEMAS + INVALID_SCHEMA).toFile().createNewFile(); + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + ingestSchemaRecord(); + } + + @Test + public void testCreateRecordWithoutAuthentication() throws Exception { + String id = "testCreateRecordWithoutAuthentication"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)). + // no authorization + //header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isUnauthorized()); + } + + @Test + public void testCreateRecord() throws Exception { + String id = "testCreateRecord"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource result = map.readValue(mvcResult.getResponse().getContentAsString(), DataResource.class); + Assert.assertNotNull(result); + } + + @Test + public void testCreateRecordWithValidUrlSchema() throws Exception { + String id = "testCreateRecordWithValidUrlSchema"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + // Get URL of schema + String schemaUrl = getSchemaUrl(SCHEMA_ID); +// record.setId("my_id"); + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + schemaIdentifier.setValue(schemaUrl); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn(); + } + + @Test + public void testCreateRecordWithUrlSchemaNull() throws Exception { + String id = "testCreateRecordWithUrlSchemaNull"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + schemaIdentifier.setValue(null); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + } + + @Test + public void testCreateRecordWithInvalidUrl() throws Exception { + String id = "testCreateRecordWithInvalidUrl"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + // Get URL of schema and remove first character + String invalidSchemaUrl = getSchemaUrl(SCHEMA_ID).substring(1); + + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + schemaIdentifier.setValue(invalidSchemaUrl); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + } + + @Test + public void testCreateRecordWithInvalidUrlSchema() throws Exception { + String id = "testCreateRecordWithInvalidUrlSchema"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + // Get URL of schema and replace schema by an invalid one + String urlWithInvalidSchema = getSchemaUrl(SCHEMA_ID).replace(SCHEMA_ID, INVALID_SCHEMA); + + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + schemaIdentifier.setValue(urlWithInvalidSchema); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + } + + @Test + public void testCreateRecordWithAnyValidUrl() throws Exception { + String id = "testCreateRecordWithAnyValidUrl"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + // Set URL of schema to a broken url + String schemaUrl = "http://anyurl.example.org/shouldNotExist"; + + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + schemaIdentifier.setValue(schemaUrl); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + } + + @Test + public void testCreateRecordWithId() throws Exception { + String id = "testCreateRecordWithId"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + + this.mockMvc.perform(get(API_METADATA_PATH + id). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + } + + @Test + public void testCreateRecordWithIdTwice() throws Exception { + String id = "testCreateRecordWithIdTwice"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setValue(RELATED_RESOURCE_2.getIdentifier()); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isConflict()). + andReturn(); + } + + @Test + public void testCreateRecordWithLocationUri() throws Exception { + String id = "testCreateRecordWithLocationUri"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn(); + String locationUri = result.getResponse().getHeader("Location"); + String content = result.getResponse().getContentAsString(); + + ObjectMapper map = new ObjectMapper(); + MvcResult result2 = this.mockMvc.perform(get(locationUri). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content2 = result2.getResponse().getContentAsString(); + System.out.println(content); + System.out.println(content2); + Assert.assertEquals(content, content2); + } + + @Test + public void testCreateInvalidRecord() throws Exception { + String id = "testCreateInvalidRecord"; + String schemaId = INVALID_SCHEMA; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateInvalidMetadataRecord() throws Exception { + String wrongTypeJson = "{\"id\":\"dc\",\"relatedResource\":\"anyResource\",\"createdAt\":\"right now!\"}"; + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", wrongTypeJson.getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + String wrongFormatJson = "<metadata><schemaId>dc</schemaId><type>XML</type></metadata>"; + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", wrongFormatJson.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + + } + + @Test + public void testCreateEmptyMetadataSchemaRecord() throws Exception { + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", (byte[]) null); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", " ".getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + } + + // @Test + public void testCreateRecordFromExternal() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + RequestPostProcessor rpp = new RequestPostProcessor() { + @Override + public MockHttpServletRequest postProcessRequest(MockHttpServletRequest mhsr) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + }; + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + with(remoteAddr("any.external.domain"))). + andDo(print()).andExpect(status().isCreated()). + andReturn(); + } + + //@Test @ToDo Set external remote address. + public void testCreateRecordUpdateFromExternal() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier("my_dcExt")); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + with(remoteAddr("any.domain.com"))). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + with(remoteAddr("www.google.com"))). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + } + + @Test + public void testCreateMetadataUnknownSchemaId() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier("unknown_dc")); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateRecordWithBadMetadata() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", "<>".getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_WRONG_NAMESPACE.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateRecordWithInvalidMetadata() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_INVALID.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isUnprocessableEntity()). + andReturn(); + } + + @Test + public void testCreateRecordWithoutRecord() throws Exception { + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + } + + @Test + public void testCreateRecordWithoutSchema() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + } + + @Test + public void testCreateRecordWithBadRecord() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + MetadataRecord record = new MetadataRecord(); + //schemaId is missing + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(null)); + record.setRelatedResource(RELATED_RESOURCE); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + } + + @Test + public void testCreateRecordWithBadRecord2() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + //related resource is missing + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isBadRequest()). + andReturn(); + } + + @Test + public void testCreateTwoVersionsOfSameRecord() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + + MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + + res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isConflict()). + andReturn(); + + Assert.assertTrue(res.getResponse().getContentAsString().contains("Conflict")); + Assert.assertTrue(res.getResponse().getContentAsString().contains(SCHEMA_ID)); + Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); + } + + @Test + public void testCreateTwoVersions() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + + MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + + record.setRelatedResource(RELATED_RESOURCE_2); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + + result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + } + + @Test + public void testGetRecord() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> acl = new HashSet<>(); + acl.add(new AclEntry("test1", PERMISSION.ADMINISTRATE)); + acl.add(new AclEntry("test2", PERMISSION.WRITE)); + acl.add(new AclEntry("test3", PERMISSION.READ)); + acl.add(new AclEntry("test4", PERMISSION.NONE)); + record.setAcl(acl); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + String etag1 = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + MetadataRecord mr = mapper.readValue(body, MetadataRecord.class); + String locationUri = result.getResponse().getHeader("Location"); + String recordId = mr.getId(); + + result = this.mockMvc.perform(get(locationUri). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + String etag2 = result.getResponse().getHeader("ETag"); + MetadataRecord mr2 = mapper.readValue(content, MetadataRecord.class); + + Assert.assertEquals(etag1, etag2); + Assert.assertEquals(mr, mr2); + result = this.mockMvc.perform(get(API_METADATA_PATH + recordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + content = result.getResponse().getContentAsString(); + etag2 = result.getResponse().getHeader("ETag"); + mr2 = mapper.readValue(content, MetadataRecord.class); + + Assert.assertEquals(etag1, etag2); + Assert.assertEquals(mr, mr2); + } + + @Test + public void testGetRecordByIdWithVersion() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1"). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); + Assert.assertNotNull(result); + Assert.assertEquals(ResourceIdentifier.IdentifierType.URL, result.getSchema().getIdentifierType()); + String schemaUrl = result.getSchema().getIdentifier(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); + Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); + } + + @Test + public void testGetRecordByIdWithInvalidId() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + this.mockMvc.perform(get(API_METADATA_PATH + "cd"). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + } + + @Test + public void testGetRecordByIdWithInvalidVersion() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "13"). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().is4xxClientError()). + andReturn(); + } + + @Test + public void testFindRecordsBySchemaId() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", SCHEMA_ID). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(1, result.length); + } + + @Test + public void testFindRecordsByResourceId() throws Exception { + Instant oneHourBefore = Instant.now().minusSeconds(3600); + Instant twoHoursBefore = Instant.now().minusSeconds(7200); + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("resoureId", RELATED_RESOURCE.getIdentifier()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(1, result.length); + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("resourceId", RELATED_RESOURCE.getIdentifier()). + param("from", twoHoursBefore.toString()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + map = new ObjectMapper(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(1, result.length); + } + + @Test + public void testFindRecordsByInvalidResourceId() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH). + param("resourceId", "invalid"). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindRecordsByInvalidUploadDate() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + Instant oneHourBefore = Instant.now().minusSeconds(3600); + Instant twoHoursBefore = Instant.now().minusSeconds(7200); + + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH). + param("resourceId", RELATED_RESOURCE.getIdentifier()). + param("until", oneHourBefore.toString()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + ObjectMapper map = new ObjectMapper(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + + res = this.mockMvc.perform(get(API_METADATA_PATH). + param("resourceId", RELATED_RESOURCE.getIdentifier()). + param("from", twoHoursBefore.toString()). + param("until", oneHourBefore.toString()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + map = new ObjectMapper(); + result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindRecordsByUnknownParameter() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + this.mockMvc.perform(get(API_METADATA_PATH). + param("schemaId", "cd"). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(0))); + } + + @Test + public void testGetSchemaDocument() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = KIT_DOCUMENT; + + Assert.assertEquals(dcMetadata, content); + } + + @Test + public void testGetMetadataDocumentWithUnknownSchema() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + this.mockMvc.perform(get(API_METADATA_PATH + "unknown_dc"). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isNotFound()). + andReturn(); + } + + @Test + public void testUpdateRecord() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = KIT_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + } + + @Test + public void testUpdateAclOnly() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); + + // add one more user + record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion()); + if (record.getAcl() != null) { + Assert.assertTrue(record2.getAcl().containsAll(oldRecord.getAcl())); + // There should be an additional entry + Assert.assertTrue(oldRecord.getAcl().size() + 1 == record2.getAcl().size()); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testUpdateWrongAclOnly() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + // remove old users + record.getAcl().clear(); + // add new user with administration rights + record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isBadRequest()); + } + + @Test + public void testUpdateRecordWithWrongACL() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + // Set all ACL to WRITE + for (AclEntry entry : record.getAcl()) { + entry.setPermission(PERMISSION.WRITE); + } + record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isForbidden()); + + } + + @Test + public void testUpdateRecordWithWrongACLButAdminRole() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + // Set all ACL to WRITE + for (AclEntry entry : record.getAcl()) { + entry.setPermission(PERMISSION.WRITE); + } + record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()); + + } + + @Test + public void testUpdateRecordWithEmptyACL() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + Set<AclEntry> currentAcl = oldRecord.getAcl(); + // Set ACL to null + record.setAcl(null); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + record = mapper.readValue(body, MetadataRecord.class); + Assert.assertTrue(record.getAcl().containsAll(currentAcl)); + Assert.assertTrue(currentAcl.containsAll(record.getAcl())); + } + + @Test + public void testUpdateRecordWithoutExplizitGet() throws Exception { + MetadataRecord record = new MetadataRecord(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> acl = new HashSet<>(); + acl.add(new AclEntry("test", PERMISSION.READ)); + acl.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(acl); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + String locationUri = result.getResponse().getHeader("Location"); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); + MockMultipartFile metadataFile2 = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(locationUri). + file(recordFile2). + file(metadataFile2). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record2.getDocumentHash(), record3.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); + Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); + Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); + Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1l);// version should be 1 higher + if (record2.getAcl() != null) { + Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); + } + Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); + } + + @Test + public void testUpdateRecordWithoutETag() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + with(putMultipart())). + andDo(print()). + andExpect(status().isPreconditionRequired()). + andReturn(); + } + + @Test + public void testUpdateRecordWithWrongETag() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag") + "unknown"; + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isPreconditionFailed()). + andReturn(); + } + + @Test + public void testUpdateRecordWithoutRecord() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testUpdateRecordWithoutDocument() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + file(recordFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash()); + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getMetadataDocumentUri(), record2.getMetadataDocumentUri()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion());// version should be the same + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + } + + @Test + public void testDeleteRecordWithoutAuthentication() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag)). + andDo(print()). + andExpect(status().isUnauthorized()). + andReturn(); + } + + @Test + public void testDeleteRecordWithoutAuthenticationButAuthorization() throws Exception { + // anonymousUser is not allowed to delete an digital object even the + // access rights allow this. + String metadataRecordId = createDCMetadataRecordWithAdminForAnonymous(); + + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag)). + andDo(print()). + andExpect(status().isUnauthorized()). + andReturn(); + } + + @Test + public void testDeleteRecord() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String metadataRecordId = createDCMetadataRecord(); + + // Get a list of all records + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + + // Get ETag + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + // Delete record + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isNoContent()). + andReturn(); + // Delete second time + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isPreconditionRequired()). + andReturn(); +// Recreation should be no problem. +// //try to create after deletion (Should return HTTP GONE) +// MetadataRecord record = new MetadataRecord(); +// record.setSchemaId("dc"); +// record.setRelatedResource(RELATED_RESOURCE); +// ObjectMapper mapper = new ObjectMapper(); +// +// MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); +// MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); +// +// this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + METADATA_RECORD_ID). +// file(recordFile). +// file(metadataFile)).andDo(print()).andExpect(status().isGone()).andReturn(); + + // List of records should be smaller afterwards + result = this.mockMvc.perform(get(API_METADATA_PATH). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); + } + + @Test + public void testDeleteRecordWithAdminRole() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String metadataRecordId = createDCMetadataRecord(); + + // Get a list of all records + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + + // Get ETag + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + // Delete record without appropriate access rights. + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isForbidden()). + andReturn(); + // Delete record + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag). + header(HttpHeaders.AUTHORIZATION, "Bearer " + curatorToken)). + andDo(print()). + andExpect(status().isNoContent()). + andReturn(); + // Delete second time + // Get ETag + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + etag = result.getResponse().getHeader("ETag"); + this.mockMvc.perform(delete(API_METADATA_PATH + metadataRecordId). + header("If-Match", etag). + header(HttpHeaders.AUTHORIZATION, "Bearer " + curatorToken)). + andDo(print()). + andExpect(status().isNoContent()). + andReturn(); +// Recreation should be no problem. +// //try to create after deletion (Should return HTTP GONE) +// MetadataRecord record = new MetadataRecord(); +// record.setSchemaId("dc"); +// record.setRelatedResource(RELATED_RESOURCE); +// ObjectMapper mapper = new ObjectMapper(); +// +// MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); +// MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); +// +// this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + METADATA_RECORD_ID). +// file(recordFile). +// file(metadataFile)).andDo(print()).andExpect(status().isGone()).andReturn(); + + // List of records should be smaller afterwards + result = this.mockMvc.perform(get(API_METADATA_PATH). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); + } + + @Test + public void testGetAllVersionsOfRecord() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + // Get version of record as array + // Read all versions (only 1 version available) + this.mockMvc.perform(get(API_METADATA_PATH). + param("id", metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header(HttpHeaders.ACCEPT, "application/json")). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))); + + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update + Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = KIT_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + // Get version of record as array + // Read all versions (only 1 version available) + this.mockMvc.perform(get(API_METADATA_PATH). + param("id", metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header(HttpHeaders.ACCEPT, "application/json")). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))); + } + + private String createDCMetadataRecordWithAdminForAnonymous() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); + aclEntries.add(new AclEntry("anonymousUser", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn(); + MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + + return result.getId(); + } + + private String createDCMetadataRecord() throws Exception { + MetadataRecord record = new MetadataRecord(); +// record.setId("my_id"); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); + record.setRelatedResource(RELATED_RESOURCE); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); + aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult andReturn = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). + andReturn(); + MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + + return result.getId(); + } + + private static RequestPostProcessor remoteAddr(final String remoteAddr) { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setRemoteAddr(remoteAddr); + return request; + }; + } + + private static RequestPostProcessor putMultipart() { // it's nice to extract into a helper + return (MockHttpServletRequest request) -> { + request.setMethod("PUT"); + return request; + }; + } + + private void ingestSchemaRecord() throws Exception { + DataResource record = SchemaRegistryControllerTestV2.createDataResource4XmlSchema(SCHEMA_ID); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("anonymousUser", PERMISSION.READ)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + } + + public static synchronized boolean isInitialized() { + boolean returnValue = alreadyInitialized; + alreadyInitialized = Boolean.TRUE; + + return returnValue; + } + + private String getSchemaUrl(String schemaId) throws Exception { + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + param("version", "1")). + andDo(print()).andExpect(status().isOk()). + andReturn(); + String result = res.getRequest().getRequestURL().toString() + "?version=1"; + System.out.println("result " + result); + return result.replaceFirst("8080", "41428"); + } +} From a5c12e4982f5249ab9b721f9fc9674bc2ace1b3b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 22 Jul 2024 16:02:45 +0200 Subject: [PATCH 047/181] Fix tests for API v2. --- ...rollerTestWithAuthenticationEnabledV2.java | 309 ++++++++++-------- 1 file changed, 168 insertions(+), 141 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java index 764c9a91..acc6b1aa 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -16,7 +16,6 @@ import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; @@ -50,7 +49,6 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; @@ -116,8 +114,9 @@ public class MetadataControllerTestWithAuthenticationEnabledV2 { private static final String SCHEMA_ID = "my_dc"; private static final String INVALID_SCHEMA = "invalid_dc"; private static final String RELATED_RESOURCE_STRING = "anyResourceId"; + private static final String RELATED_RESOURCE_STRING_2 = "anyResourceId"; private static final ResourceIdentifier RELATED_RESOURCE = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING); - private static final ResourceIdentifier RELATED_RESOURCE_2 = ResourceIdentifier.factoryUrlResourceIdentifier("anyOtherResourceId"); + private static final ResourceIdentifier RELATED_RESOURCE_2 = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING_2); private final static String KIT_SCHEMA = CreateSchemaUtil.KIT_SCHEMA; private final static String KIT_DOCUMENT = CreateSchemaUtil.KIT_DOCUMENT; @@ -637,9 +636,10 @@ public void testCreateRecordUpdateFromExternal() throws Exception { @Test public void testCreateMetadataUnknownSchemaId() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier("unknown_dc")); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateMetadataUnknownSchemaId"; + String schemaId = "unknown_schema"; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -655,10 +655,11 @@ public void testCreateMetadataUnknownSchemaId() throws Exception { } @Test - public void testCreateRecordWithBadMetadata() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + public void testCreateRecordWithBadSchema() throws Exception { + String id = "testCreateRecordWithBadSchema"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -675,9 +676,10 @@ public void testCreateRecordWithBadMetadata() throws Exception { @Test public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateRecordWithInvalidMetadataNamespace"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -694,9 +696,10 @@ public void testCreateRecordWithInvalidMetadataNamespace() throws Exception { @Test public void testCreateRecordWithInvalidMetadata() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateRecordWithInvalidMetadata"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -724,9 +727,10 @@ public void testCreateRecordWithoutRecord() throws Exception { @Test public void testCreateRecordWithoutSchema() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateRecordWithoutSchema"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -740,12 +744,14 @@ public void testCreateRecordWithoutSchema() throws Exception { @Test public void testCreateRecordWithBadRecord() throws Exception { - ObjectMapper mapper = new ObjectMapper(); + String id = "testCreateRecordWithBadRecord"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); - MetadataRecord record = new MetadataRecord(); - //schemaId is missing - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(null)); - record.setRelatedResource(RELATED_RESOURCE); + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + schemaIdentifier.setValue(null); + + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); @@ -755,17 +761,26 @@ public void testCreateRecordWithBadRecord() throws Exception { file(metadataFile). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). andDo(print()). - andExpect(status().isBadRequest()). + andExpect(status().isUnprocessableEntity()). andReturn(); } @Test public void testCreateRecordWithBadRecord2() throws Exception { - ObjectMapper mapper = new ObjectMapper(); + String id = "testCreateRecordWithBadRecord2"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - //related resource is missing + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + Set<RelatedIdentifier> relatedIdentifiers = record.getRelatedIdentifiers(); + for (RelatedIdentifier item : relatedIdentifiers) { + if (item != schemaIdentifier) { + //remove related resource + relatedIdentifiers.remove(item); + } + } + + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); @@ -781,11 +796,11 @@ public void testCreateRecordWithBadRecord2() throws Exception { @Test public void testCreateTwoVersionsOfSameRecord() throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); + // Two records with same schema and same related resource are now allowed. + String id = "testCreateRecordWithBadRecord2"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -799,29 +814,26 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { andExpect(status().isCreated()). andReturn(); - MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(Long.valueOf(1l).toString(), result.getVersion()); + record = SchemaRegistryControllerTestV2.createDataResource4Document(id + "_2", schemaId); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). file(metadataFile). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). andDo(print()). - andExpect(status().isConflict()). + andExpect(status().isCreated()). andReturn(); - - Assert.assertTrue(res.getResponse().getContentAsString().contains("Conflict")); - Assert.assertTrue(res.getResponse().getContentAsString().contains(SCHEMA_ID)); - Assert.assertTrue(res.getResponse().getContentAsString().contains(RELATED_RESOURCE_STRING)); } @Test public void testCreateTwoVersions() throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); + String id = "testCreateTwoVersions"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -835,10 +847,23 @@ public void testCreateTwoVersions() throws Exception { andExpect(status().isCreated()). andReturn(); - MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(Long.valueOf(1l).toString(), result.getVersion()); + + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); + Set<RelatedIdentifier> relatedIdentifiers = record.getRelatedIdentifiers(); + for (RelatedIdentifier item : relatedIdentifiers) { + if (item != schemaIdentifier) { + //remove related resource + relatedIdentifiers.remove(item); + } + } + RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE_STRING_2, null, null); + relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + relatedIdentifiers.add(relatedResource); + record.getAlternateIdentifiers().clear(); + record.setId(null); - record.setRelatedResource(RELATED_RESOURCE_2); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). @@ -848,21 +873,23 @@ public void testCreateTwoVersions() throws Exception { andExpect(status().isCreated()). andReturn(); - result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertEquals(Long.valueOf(1l).toString(), result.getVersion()); } @Test public void testGetRecord() throws Exception { - MetadataRecord record = new MetadataRecord(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "testCreateTwoVersions"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + Set<AclEntry> acl = new HashSet<>(); acl.add(new AclEntry("test1", PERMISSION.ADMINISTRATE)); acl.add(new AclEntry("test2", PERMISSION.WRITE)); acl.add(new AclEntry("test3", PERMISSION.READ)); acl.add(new AclEntry("test4", PERMISSION.NONE)); - record.setAcl(acl); + record.setAcls(acl); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -877,31 +904,31 @@ public void testGetRecord() throws Exception { andReturn(); String etag1 = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); - MetadataRecord mr = mapper.readValue(body, MetadataRecord.class); + DataResource mr = mapper.readValue(body, DataResource.class); String locationUri = result.getResponse().getHeader("Location"); String recordId = mr.getId(); result = this.mockMvc.perform(get(locationUri). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); String content = result.getResponse().getContentAsString(); String etag2 = result.getResponse().getHeader("ETag"); - MetadataRecord mr2 = mapper.readValue(content, MetadataRecord.class); + DataResource mr2 = mapper.readValue(content, DataResource.class); Assert.assertEquals(etag1, etag2); Assert.assertEquals(mr, mr2); result = this.mockMvc.perform(get(API_METADATA_PATH + recordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); content = result.getResponse().getContentAsString(); etag2 = result.getResponse().getHeader("ETag"); - mr2 = mapper.readValue(content, MetadataRecord.class); + mr2 = mapper.readValue(content, DataResource.class); Assert.assertEquals(etag1, etag2); Assert.assertEquals(mr, mr2); @@ -913,19 +940,20 @@ public void testGetRecordByIdWithVersion() throws Exception { MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "1"). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertNotNull(result); - Assert.assertEquals(ResourceIdentifier.IdentifierType.URL, result.getSchema().getIdentifierType()); - String schemaUrl = result.getSchema().getIdentifier(); + + DataResource record = map.readValue(res.getResponse().getContentAsString(), DataResource.class); + Assert.assertNotNull(record); + Assert.assertEquals(Identifier.IDENTIFIER_TYPE.URL, DataResourceRecordUtil.getSchemaIdentifier(record).getIdentifierType()); + String schemaUrl = DataResourceRecordUtil.getSchemaIdentifier(record).getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); - Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); + Assert.assertNotEquals("file:///tmp/dc.xml", DataResourceRecordUtil.getSchemaIdentifier(record).getValue()); } @Test @@ -933,7 +961,7 @@ public void testGetRecordByIdWithInvalidId() throws Exception { String metadataRecordId = createDCMetadataRecord(); this.mockMvc.perform(get(API_METADATA_PATH + "cd"). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isNotFound()). andReturn(); @@ -944,7 +972,7 @@ public void testGetRecordByIdWithInvalidVersion() throws Exception { String metadataRecordId = createDCMetadataRecord(); this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "13"). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().is4xxClientError()). andReturn(); @@ -959,7 +987,7 @@ public void testFindRecordsBySchemaId() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); } @@ -975,7 +1003,7 @@ public void testFindRecordsByResourceId() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); res = this.mockMvc.perform(get(API_METADATA_PATH). @@ -986,7 +1014,7 @@ public void testFindRecordsByResourceId() throws Exception { andExpect(status().isOk()). andReturn(); map = new ObjectMapper(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); } @@ -1001,7 +1029,7 @@ public void testFindRecordsByInvalidResourceId() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -1020,7 +1048,7 @@ public void testFindRecordsByInvalidUploadDate() throws Exception { andExpect(status().isOk()). andReturn(); ObjectMapper map = new ObjectMapper(); - MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); @@ -1033,7 +1061,7 @@ public void testFindRecordsByInvalidUploadDate() throws Exception { andExpect(status().isOk()). andReturn(); map = new ObjectMapper(); - result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(0, result.length); } @@ -1080,7 +1108,7 @@ public void testUpdateRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1088,7 +1116,7 @@ public void testUpdateRecord() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1102,12 +1130,12 @@ public void testUpdateRecord() throws Exception { andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + DataResource record2 = mapper.readValue(body, DataResource.class); Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); + Assert.assertEquals(DataResourceRecordUtil.getrecord.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher if (record.getAcl() != null) { @@ -1134,7 +1162,7 @@ public void testUpdateAclOnly() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1142,8 +1170,8 @@ public void testUpdateAclOnly() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); + DataResource oldRecord = mapper.readValue(body, DataResource.class); // add one more user record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); @@ -1158,10 +1186,10 @@ public void testUpdateAclOnly() throws Exception { andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + DataResource record2 = mapper.readValue(body, DataResource.class); Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); @@ -1179,7 +1207,7 @@ public void testUpdateWrongAclOnly() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1187,7 +1215,7 @@ public void testUpdateWrongAclOnly() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); // remove old users record.getAcl().clear(); // add new user with administration rights @@ -1208,7 +1236,7 @@ public void testUpdateRecordWithWrongACL() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1216,8 +1244,8 @@ public void testUpdateRecordWithWrongACL() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource oldRecord = mapper.readValue(body, DataResource.class); + DataResource record = mapper.readValue(body, DataResource.class); // Set all ACL to WRITE for (AclEntry entry : record.getAcl()) { entry.setPermission(PERMISSION.WRITE); @@ -1242,7 +1270,7 @@ public void testUpdateRecordWithWrongACLButAdminRole() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1250,8 +1278,8 @@ public void testUpdateRecordWithWrongACLButAdminRole() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource oldRecord = mapper.readValue(body, DataResource.class); + DataResource record = mapper.readValue(body, DataResource.class); // Set all ACL to WRITE for (AclEntry entry : record.getAcl()) { entry.setPermission(PERMISSION.WRITE); @@ -1276,7 +1304,7 @@ public void testUpdateRecordWithEmptyACL() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1284,8 +1312,8 @@ public void testUpdateRecordWithEmptyACL() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord oldRecord = mapper.readValue(body, MetadataRecord.class); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource oldRecord = mapper.readValue(body, DataResource.class); + DataResource record = mapper.readValue(body, DataResource.class); Set<AclEntry> currentAcl = oldRecord.getAcl(); // Set ACL to null record.setAcl(null); @@ -1303,14 +1331,14 @@ public void testUpdateRecordWithEmptyACL() throws Exception { andExpect(status().isOk()). andReturn(); body = result.getResponse().getContentAsString(); - record = mapper.readValue(body, MetadataRecord.class); + record = mapper.readValue(body, DataResource.class); Assert.assertTrue(record.getAcl().containsAll(currentAcl)); Assert.assertTrue(currentAcl.containsAll(record.getAcl())); } @Test public void testUpdateRecordWithoutExplizitGet() throws Exception { - MetadataRecord record = new MetadataRecord(); + DataResource record = new DataResource(); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); record.setRelatedResource(RELATED_RESOURCE); Set<AclEntry> acl = new HashSet<>(); @@ -1333,7 +1361,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + DataResource record2 = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); MockMultipartFile metadataFile2 = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1347,10 +1375,10 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record3 = mapper.readValue(body, MetadataRecord.class); + DataResource record3 = mapper.readValue(body, DataResource.class); Assert.assertNotEquals(record2.getDocumentHash(), record3.getDocumentHash());//mime type was changed by update Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); @@ -1367,7 +1395,7 @@ public void testUpdateRecordWithoutETag() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1375,7 +1403,7 @@ public void testUpdateRecordWithoutETag() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1393,7 +1421,7 @@ public void testUpdateRecordWithoutETag() throws Exception { public void testUpdateRecordWithWrongETag() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). andDo(print()). andExpect(status().isOk()). @@ -1402,7 +1430,7 @@ public void testUpdateRecordWithWrongETag() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1422,7 +1450,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1430,7 +1458,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1444,7 +1472,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + DataResource record2 = mapper.readValue(body, DataResource.class); Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); @@ -1461,7 +1489,7 @@ public void testUpdateRecordWithoutDocument() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1469,7 +1497,7 @@ public void testUpdateRecordWithoutDocument() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). @@ -1480,10 +1508,10 @@ public void testUpdateRecordWithoutDocument() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); -// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); +// this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + DataResource record2 = mapper.readValue(body, DataResource.class); Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash()); Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getMetadataDocumentUri(), record2.getMetadataDocumentUri()); @@ -1501,7 +1529,7 @@ public void testDeleteRecordWithoutAuthentication() throws Exception { MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1522,7 +1550,7 @@ public void testDeleteRecordWithoutAuthenticationButAuthorization() throws Excep MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1543,16 +1571,16 @@ public void testDeleteRecord() throws Exception { // Get a list of all records MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); - int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; // Get ETag result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1572,7 +1600,7 @@ public void testDeleteRecord() throws Exception { andReturn(); // Recreation should be no problem. // //try to create after deletion (Should return HTTP GONE) -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchemaId("dc"); // record.setRelatedResource(RELATED_RESOURCE); // ObjectMapper mapper = new ObjectMapper(); @@ -1587,11 +1615,11 @@ public void testDeleteRecord() throws Exception { // List of records should be smaller afterwards result = this.mockMvc.perform(get(API_METADATA_PATH). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); - int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); } @@ -1603,16 +1631,16 @@ public void testDeleteRecordWithAdminRole() throws Exception { // Get a list of all records MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); - int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + int noOfRecords = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; // Get ETag result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1635,7 +1663,7 @@ public void testDeleteRecordWithAdminRole() throws Exception { // Get ETag result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1648,7 +1676,7 @@ public void testDeleteRecordWithAdminRole() throws Exception { andReturn(); // Recreation should be no problem. // //try to create after deletion (Should return HTTP GONE) -// MetadataRecord record = new MetadataRecord(); +// DataResource record = new DataResource(); // record.setSchemaId("dc"); // record.setRelatedResource(RELATED_RESOURCE); // ObjectMapper mapper = new ObjectMapper(); @@ -1663,11 +1691,11 @@ public void testDeleteRecordWithAdminRole() throws Exception { // List of records should be smaller afterwards result = this.mockMvc.perform(get(API_METADATA_PATH). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); - int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), MetadataRecord[].class).length; + int noOfRecordsAfter = mapper.readValue(result.getResponse().getContentAsString(), DataResource[].class).length; Assert.assertEquals("No of records should be decremented!", noOfRecords - 1, noOfRecordsAfter); } @@ -1686,7 +1714,7 @@ public void testGetAllVersionsOfRecord() throws Exception { MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). - header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1694,7 +1722,7 @@ public void testGetAllVersionsOfRecord() throws Exception { String body = result.getResponse().getContentAsString(); ObjectMapper mapper = new ObjectMapper(); - MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1708,10 +1736,10 @@ public void testGetAllVersionsOfRecord() throws Exception { andExpect(status().isOk()). andReturn(); -// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + DataResource record2 = mapper.readValue(body, DataResource.class); Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); @@ -1745,7 +1773,7 @@ public void testGetAllVersionsOfRecord() throws Exception { } private String createDCMetadataRecordWithAdminForAnonymous() throws Exception { - MetadataRecord record = new MetadataRecord(); + DataResource record = new DataResource(); // record.setId("my_id"); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); record.setRelatedResource(RELATED_RESOURCE); @@ -1766,20 +1794,19 @@ private String createDCMetadataRecordWithAdminForAnonymous() throws Exception { andExpect(status().isCreated()). andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). andReturn(); - MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + DataResource result = mapper.readValue(andReturn.getResponse().getContentAsString(), DataResource.class); return result.getId(); } private String createDCMetadataRecord() throws Exception { - MetadataRecord record = new MetadataRecord(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); - aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + String id = "createDCMetadataRecord"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + record.setId(null); + record.getAlternateIdentifiers().clear(); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1793,7 +1820,7 @@ private String createDCMetadataRecord() throws Exception { andExpect(status().isCreated()). andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). andReturn(); - MetadataRecord result = mapper.readValue(andReturn.getResponse().getContentAsString(), MetadataRecord.class); + DataResource result = mapper.readValue(andReturn.getResponse().getContentAsString(), DataResource.class); return result.getId(); } From d3538b507dd76c3357bab9ca6ff1b573372e21a4 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 23 Jul 2024 14:24:56 +0200 Subject: [PATCH 048/181] Fix more tests for API v2. --- ...rollerTestWithAuthenticationEnabledV2.java | 305 +++++++++++++----- 1 file changed, 216 insertions(+), 89 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java index acc6b1aa..6b94ef39 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -21,6 +21,7 @@ import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.acl.AclEntry; @@ -49,6 +50,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; @@ -1081,7 +1083,8 @@ public void testFindRecordsByUnknownParameter() throws Exception { public void testGetSchemaDocument() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + accept(MediaType.APPLICATION_XML)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1106,16 +1109,27 @@ public void testGetMetadataDocumentWithUnknownSchema() throws Exception { @Test public void testUpdateRecord() throws Exception { String metadataRecordId = createDCMetadataRecord(); + ObjectMapper mapper = new ObjectMapper(); + // Get ContentInformation of first version MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); + String locationUri = result.getResponse().getHeader("Location"); String etag = result.getResponse().getHeader("ETag"); - String body = result.getResponse().getContentAsString(); + body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1131,20 +1145,38 @@ public void testUpdateRecord() throws Exception { andReturn(); // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(DataResourceRecordUtil.getrecord.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); + Assert.assertNotEquals(contentInformation1, contentInformation2); + Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); + Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); + Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); + Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); + Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); + // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + accept(MediaType.APPLICATION_XML)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1154,29 +1186,42 @@ public void testUpdateRecord() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); } @Test public void testUpdateAclOnly() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String metadataRecordId = createDCMetadataRecord(); + // Get ContentInformation of first version MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + +// Get datacite record of first version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); String etag = result.getResponse().getHeader("ETag"); - String body = result.getResponse().getContentAsString(); + body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); + // make a copy of the record due to changes in the original one. DataResource oldRecord = mapper.readValue(body, DataResource.class); // add one more user - record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + record.getAcls().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); - + // update record only result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). file(recordFile). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). @@ -1190,16 +1235,26 @@ public void testUpdateAclOnly() throws Exception { body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); - Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion()); - if (record.getAcl() != null) { - Assert.assertTrue(record2.getAcl().containsAll(oldRecord.getAcl())); - // There should be an additional entry - Assert.assertTrue(oldRecord.getAcl().size() + 1 == record2.getAcl().size()); - } + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));// version should be the same + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); + Assert.assertEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); + Assert.assertEquals(contentInformation1, contentInformation2); + Assert.assertEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); + Assert.assertEquals(contentInformation1.getHash(), contentInformation2.getHash()); + Assert.assertEquals(contentInformation1.getSize(), contentInformation2.getSize()); } @Test @@ -1217,9 +1272,9 @@ public void testUpdateWrongAclOnly() throws Exception { ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); // remove old users - record.getAcl().clear(); + record.getAcls().clear(); // add new user with administration rights - record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + record.getAcls().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). @@ -1247,10 +1302,10 @@ public void testUpdateRecordWithWrongACL() throws Exception { DataResource oldRecord = mapper.readValue(body, DataResource.class); DataResource record = mapper.readValue(body, DataResource.class); // Set all ACL to WRITE - for (AclEntry entry : record.getAcl()) { + for (AclEntry entry : record.getAcls()) { entry.setPermission(PERMISSION.WRITE); } - record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + record.getAcls().add(new AclEntry(otherUserPrincipal, PERMISSION.ADMINISTRATE)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1262,7 +1317,6 @@ public void testUpdateRecordWithWrongACL() throws Exception { with(putMultipart())). andDo(print()). andExpect(status().isForbidden()); - } @Test @@ -1281,10 +1335,10 @@ public void testUpdateRecordWithWrongACLButAdminRole() throws Exception { DataResource oldRecord = mapper.readValue(body, DataResource.class); DataResource record = mapper.readValue(body, DataResource.class); // Set all ACL to WRITE - for (AclEntry entry : record.getAcl()) { + for (AclEntry entry : record.getAcls()) { entry.setPermission(PERMISSION.WRITE); } - record.getAcl().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + record.getAcls().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1314,9 +1368,9 @@ public void testUpdateRecordWithEmptyACL() throws Exception { ObjectMapper mapper = new ObjectMapper(); DataResource oldRecord = mapper.readValue(body, DataResource.class); DataResource record = mapper.readValue(body, DataResource.class); - Set<AclEntry> currentAcl = oldRecord.getAcl(); + Set<AclEntry> currentAcl = oldRecord.getAcls(); // Set ACL to null - record.setAcl(null); + record.setAcls(null); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1332,19 +1386,16 @@ public void testUpdateRecordWithEmptyACL() throws Exception { andReturn(); body = result.getResponse().getContentAsString(); record = mapper.readValue(body, DataResource.class); - Assert.assertTrue(record.getAcl().containsAll(currentAcl)); - Assert.assertTrue(currentAcl.containsAll(record.getAcl())); + Assert.assertTrue(record.getAcls().containsAll(currentAcl)); + Assert.assertTrue(currentAcl.containsAll(record.getAcls())); } @Test public void testUpdateRecordWithoutExplizitGet() throws Exception { - DataResource record = new DataResource(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); - Set<AclEntry> acl = new HashSet<>(); - acl.add(new AclEntry("test", PERMISSION.READ)); - acl.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(acl); + String id = "testUpdateRecordWithoutExplizitGet"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1379,14 +1430,10 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { body = result.getResponse().getContentAsString(); DataResource record3 = mapper.readValue(body, DataResource.class); - Assert.assertNotEquals(record2.getDocumentHash(), record3.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); - Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); - Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1l);// version should be 1 higher - if (record2.getAcl() != null) { - Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); - } + SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); + SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record2.getRelatedIdentifiers(), record2.getRelatedIdentifiers()); + Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); } @@ -1434,7 +1481,7 @@ public void testUpdateRecordWithWrongETag() throws Exception { MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); - result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). file(recordFile). file(metadataFile). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). @@ -1447,19 +1494,29 @@ public void testUpdateRecordWithWrongETag() throws Exception { @Test public void testUpdateRecordWithoutRecord() throws Exception { + ObjectMapper mapper = new ObjectMapper(); String metadataRecordId = createDCMetadataRecord(); + // Get ContentInformation of first version MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); + String locationUri = result.getResponse().getHeader("Location"); String etag = result.getResponse().getHeader("ETag"); - String body = result.getResponse().getContentAsString(); + body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); - MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); +// MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + metadataRecordId). @@ -1470,33 +1527,71 @@ public void testUpdateRecordWithoutRecord() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); + Assert.assertNotEquals(contentInformation1, contentInformation2); + Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); + Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); + Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); + Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); + Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); + + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_JSON)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = CreateSchemaUtil.KIT_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); } @Test public void testUpdateRecordWithoutDocument() throws Exception { + ObjectMapper mapper = new ObjectMapper(); String metadataRecordId = createDCMetadataRecord(); + // Get ContentInformation of first version MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation1 = mapper.readValue(body, ContentInformation.class); + + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). andExpect(status().isOk()). andReturn(); + String locationUri = result.getResponse().getHeader("Location"); String etag = result.getResponse().getHeader("ETag"); - String body = result.getResponse().getContentAsString(); + body = result.getResponse().getContentAsString(); - ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1509,18 +1604,45 @@ public void testUpdateRecordWithoutDocument() throws Exception { andExpect(status().isOk()). andReturn(); // this.mockMvc.perform(put(API_METADATA_PATH + "dc").contentType("application/json").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + String locationUri2 = result.getResponse().getHeader("Location"); body = result.getResponse().getContentAsString(); - DataResource record2 = mapper.readValue(body, DataResource.class); - Assert.assertEquals(record.getDocumentHash(), record2.getDocumentHash()); - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getMetadataDocumentUri(), record2.getMetadataDocumentUri()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals(record.getRecordVersion(), record2.getRecordVersion());// version should be the same - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + DataResource record2 = mapper.readValue(body, DataResource.class); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));// version should be the same + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check ContentInformation of second version + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(ContentInformation.CONTENT_INFORMATION_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + body = result.getResponse().getContentAsString(); + + ContentInformation contentInformation2 = mapper.readValue(body, ContentInformation.class); + Assert.assertEquals(contentInformation1.getFilename(), contentInformation2.getFilename()); + Assert.assertEquals(contentInformation1, contentInformation2); + Assert.assertEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); + Assert.assertEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); + Assert.assertEquals((long)(contentInformation1.getVersion()), (long)contentInformation2.getVersion()); + Assert.assertEquals(contentInformation1.getHash(), contentInformation2.getHash()); + Assert.assertEquals(contentInformation1.getSize(), contentInformation2.getSize()); + + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_JSON)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = CreateSchemaUtil.KIT_DOCUMENT; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(locationUri, locationUri2); } @Test @@ -1718,6 +1840,7 @@ public void testGetAllVersionsOfRecord() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); + String locationUri = result.getResponse().getHeader("Location"); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1738,19 +1861,19 @@ public void testGetAllVersionsOfRecord() throws Exception { // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); + String locationUri2 = result.getResponse().getHeader("Location"); DataResource record2 = mapper.readValue(body, DataResource.class); - Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update - Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); - Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher - if (record.getAcl() != null) { - Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); - } + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). - header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + accept(MediaType.APPLICATION_XML)). andDo(print()). andExpect(status().isOk()). andReturn(); @@ -1760,9 +1883,9 @@ public void testGetAllVersionsOfRecord() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); // Get version of record as array - // Read all versions (only 1 version available) + // Read all versions (only 2 versions available) this.mockMvc.perform(get(API_METADATA_PATH). param("id", metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). @@ -1773,14 +1896,18 @@ public void testGetAllVersionsOfRecord() throws Exception { } private String createDCMetadataRecordWithAdminForAnonymous() throws Exception { - DataResource record = new DataResource(); -// record.setId("my_id"); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setRelatedResource(RELATED_RESOURCE); + String id = "createDCMetadataRecordWithAdminForAnonymous"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + + record.setId(null); + record.getAlternateIdentifiers().clear(); + Set<AclEntry> aclEntries = new HashSet<>(); aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); aclEntries.add(new AclEntry("anonymousUser", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); From d1f30468ea7c21e3cc01f1ff3890804c1d86ca67 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 24 Jul 2024 08:20:16 +0200 Subject: [PATCH 049/181] All tests running (MetadataControllerV2). --- ...rollerTestWithAuthenticationEnabledV2.java | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java index 6b94ef39..9c45e1ce 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -1274,16 +1274,16 @@ public void testUpdateWrongAclOnly() throws Exception { // remove old users record.getAcls().clear(); // add new user with administration rights - record.getAcls().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + record.getAcls().add(new AclEntry(otherUserPrincipal, PERMISSION.ADMINISTRATE)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). file(recordFile). - header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserPrincipal). header("If-Match", etag). with(putMultipart())). andDo(print()). - andExpect(status().isBadRequest()); + andExpect(status().isUnauthorized()); } @Test @@ -1338,7 +1338,7 @@ public void testUpdateRecordWithWrongACLButAdminRole() throws Exception { for (AclEntry entry : record.getAcls()) { entry.setPermission(PERMISSION.WRITE); } - record.getAcls().add(new AclEntry("testacl", PERMISSION.ADMINISTRATE)); + record.getAcls().add(new AclEntry(otherUserPrincipal, PERMISSION.ADMINISTRATE)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); @@ -1354,7 +1354,40 @@ public void testUpdateRecordWithWrongACLButAdminRole() throws Exception { } @Test - public void testUpdateRecordWithEmptyACL() throws Exception { + public void testUpdateRecordWithEmptyACLAsUser() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + DataResource oldRecord = mapper.readValue(body, DataResource.class); + DataResource record = mapper.readValue(body, DataResource.class); + Set<AclEntry> currentAcl = oldRecord.getAcls(); + // Set ACL to null + record.setAcls(null); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT_VERSION_2.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isForbidden()). + andReturn(); + } + + @Test + public void testUpdateRecordWithEmptyACLAsAdmin() throws Exception { String metadataRecordId = createDCMetadataRecord(); MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). From 515f45dfcbfe64111b182455a1317c8cd82b2291 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 24 Jul 2024 09:05:49 +0200 Subject: [PATCH 050/181] Add access tests (API v2). --- ...ccessWithAuthenticationEnabled4JsonV2.java | 422 ++++++++++++++++ ...TestAccessWithAuthenticationEnabledV2.java | 451 ++++++++++++++++++ 2 files changed, 873 insertions(+) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4JsonV2.java create mode 100644 src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabledV2.java diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4JsonV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4JsonV2.java new file mode 100644 index 00000000..49e28aee --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4JsonV2.java @@ -0,0 +1,422 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.IDataRecordDao; +import edu.kit.datamanager.metastore2.dao.ILinkedDataResourceDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import edu.kit.datamanager.util.AuthenticationHelper; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import org.javers.core.Javers; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41435"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_md_accesswithaai4json_v2;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/md/aai/access/json/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/md/aai/access/json/metadata"}) +@TestPropertySource(properties = {"repo.auth.enabled=true"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class MetadataControllerTestAccessWithAuthenticationEnabled4JsonV2 { + + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/md/aai/access/json/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private static final String SCHEMA_ID = "my_dc_access_aai"; + private static final String INVALID_SCHEMA = "invalid_dc"; + private final static String JSON_SCHEMA = "{\n" + + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\",\n" + + " \"date\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " },\n" + + " \"date\": {\n" + + " \"type\": \"string\",\n" + + " \"format\": \"date\",\n" + + " \"title\": \"Date\",\n" + + " \"description\": \"Date of object\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + private final static String JSON_DOCUMENT = "{\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"date\": \"2022-07-29\"\n" + + "}"; + + private String adminToken; + private String userToken; + private String otherUserToken; + private String guestToken; + + private final String adminPrincipal = "admin"; + private final String userPrincipal = "user1"; + private final String otherUserPrincipal = "test_user"; + private final String guestPrincipal = "guest"; + + private final String ANONYMOUS_ID = "id_for_public_available_do4json"; + + private static Boolean alreadyInitialized = Boolean.FALSE; + + private MockMvc mockMvc; + @Autowired + private ApplicationProperties applicationProperties; + @Autowired + private WebApplicationContext context; + @Autowired + Javers javers = null; + @Autowired + private ILinkedDataResourceDao metadataRecordDao; + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private IDataRecordDao dataRecordDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + @Autowired + private IUrl2PathDao url2PathDao; + @Autowired + private MetastoreConfiguration metadataConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + @Before + public void setUp() throws Exception { + // setup mockMvc + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation).uris() + .withPort(41415)) + .build(); + adminToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(adminPrincipal, RepoUserRole.ADMINISTRATOR). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("groupid", "USERS"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(userPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + otherUserToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(otherUserPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + + guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(guestPrincipal, RepoUserRole.GUEST). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + if (!isInitialized()) { + System.out.println("------MetadataControllerAccessTestWithAAI4Json--------"); + System.out.println("------" + this.metadataConfig); + System.out.println("------------------------------------------------------"); + + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + metadataRecordDao.deleteAll(); + schemaRecordDao.deleteAll(); + dataRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + url2PathDao.deleteAll(); + + try { + // Create schema only once. + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + Paths.get(TEMP_DIR_4_SCHEMAS + INVALID_SCHEMA).toFile().createNewFile(); + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + + CreateSchemaUtil.ingestKitSchemaRecordV2(mockMvc, SCHEMA_ID, applicationProperties.getJwtSecret()); + int schemaNo = 1; + for (PERMISSION user1 : PERMISSION.values()) { + for (PERMISSION guest : PERMISSION.values()) { + ingestDataResource(SCHEMA_ID + "_" + schemaNo, user1, guest); + schemaNo++; + } + } + ingestDataResource4UnregisteredUsers(SCHEMA_ID + "_" + schemaNo); + } + } + + @Test + public void testAccessRecordListAdmin() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(17))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListUser() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(13))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListGuest() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()).andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(13))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListOtherUser() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(17))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListWithoutAuthentication() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200))). + andDo(print()).andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId())). + andDo(print()). + andExpect(status().isOk()); + } + } + + /** + * Ingest metadata with 'otheruser' set permissions for admin, user and guest. + * + * @param schemaId + * @param user + * @param guest + * @throws Exception + */ + private void ingestDataResource(String schemaId, PERMISSION user, PERMISSION guest) throws Exception { + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(schemaId, SCHEMA_ID); + Set<AclEntry> aclEntries = new HashSet<>(); + if (user != PERMISSION.NONE) { + aclEntries.add(new AclEntry(userPrincipal, user)); + } + if (guest != PERMISSION.NONE) { + aclEntries.add(new AclEntry(guestPrincipal, guest)); + } + if (!aclEntries.isEmpty()) { + record.setAcls(aclEntries); + } + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", CreateSchemaUtil.KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v2/metadata/"). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()).andExpect(status().isCreated()). + andReturn(); + } + + /** + * Ingest metadata with 'otheruser' set permissions for admin, user and guest. + * + * @param schemaId + * @throws Exception + */ + private void ingestDataResource4UnregisteredUsers(String schemaId) throws Exception { + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(ANONYMOUS_ID, SCHEMA_ID); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", CreateSchemaUtil.KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v2/metadata/"). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + } + + public static synchronized boolean isInitialized() { + boolean returnValue = alreadyInitialized; + alreadyInitialized = Boolean.TRUE; + + return returnValue; + } +} diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabledV2.java new file mode 100644 index 00000000..461eaaae --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabledV2.java @@ -0,0 +1,451 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.RepoServiceRole; +import edu.kit.datamanager.entities.RepoUserRole; +import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.IDataRecordDao; +import edu.kit.datamanager.metastore2.dao.ILinkedDataResourceDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; +import edu.kit.datamanager.metastore2.domain.AclRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import edu.kit.datamanager.util.AuthenticationHelper; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import static org.hamcrest.Matchers.greaterThan; +import org.javers.core.Javers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.postgresql.core.Oid.UUID; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41432"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_md_accesswithaai_v2;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/md/aai/access/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/md/aai/access/metadata"}) +@TestPropertySource(properties = {"repo.auth.enabled=true"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class MetadataControllerTestAccessWithAuthenticationEnabledV2 { + + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/v2/md/aai/access/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private static final String SCHEMA_ID = "my_dc_access_aai"; + private static final String INVALID_SCHEMA = "invalid_dc"; + + private String adminToken; + private String userToken; + private String otherUserToken; + private String guestToken; + private String serviceToken; + + private final String adminPrincipal = "admin"; + private final String userPrincipal = "user1"; + private final String otherUserPrincipal = "test_user"; + private final String guestPrincipal = "guest"; + private final String servicePrincipal = "any_service"; + + private final String ANONYMOUS_ID = "id_for_public_available_do"; + + private static Boolean alreadyInitialized = Boolean.FALSE; + + private MockMvc mockMvc; + @Autowired + private ApplicationProperties applicationProperties; + @Autowired + private WebApplicationContext context; + @Autowired + Javers javers = null; + @Autowired + private ILinkedDataResourceDao metadataRecordDao; + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private IDataRecordDao dataRecordDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + @Autowired + private IUrl2PathDao url2PathDao; + @Autowired + private MetastoreConfiguration metadataConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + @Before + public void setUp() throws Exception { + // setup mockMvc + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation).uris() + .withPort(41432)) + .build(); + adminToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(adminPrincipal, RepoUserRole.ADMINISTRATOR). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("groupid", "USERS"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + userToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(userPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false). + getCompactToken(applicationProperties.getJwtSecret()); + + otherUserToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(otherUserPrincipal, RepoUserRole.USER). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + + guestToken = edu.kit.datamanager.util.JwtBuilder.createUserToken(guestPrincipal, RepoUserRole.GUEST). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + + serviceToken = edu.kit.datamanager.util.JwtBuilder.createServiceToken(servicePrincipal, RepoServiceRole.SERVICE_READ). + addSimpleClaim("email", "thomas.jejkal@kit.edu"). + addSimpleClaim("orcid", "0000-0003-2804-688X"). + addSimpleClaim("loginFailures", 0). + addSimpleClaim("active", true). + addSimpleClaim("locked", false).getCompactToken(applicationProperties.getJwtSecret()); + + if (!isInitialized()) { + System.out.println("------MetadataControllerAccessTestWithAAIV2-------------"); + System.out.println("------" + this.metadataConfig); + System.out.println("------------------------------------------------------"); + + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + metadataRecordDao.deleteAll(); + schemaRecordDao.deleteAll(); + dataRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + url2PathDao.deleteAll(); + + try { + // Create schema only once. + try ( Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + Paths.get(TEMP_DIR_4_SCHEMAS + INVALID_SCHEMA).toFile().createNewFile(); + try ( Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + + CreateSchemaUtil.ingestKitSchemaRecordV2(mockMvc, SCHEMA_ID, applicationProperties.getJwtSecret()); + int schemaNo = 1; + for (PERMISSION user1 : PERMISSION.values()) { + for (PERMISSION guest : PERMISSION.values()) { + ingestDataResource(SCHEMA_ID + "_" + schemaNo, user1, guest); + schemaNo++; + } + } + ingestDataResource4UnregisteredUsers(SCHEMA_ID + "_" + schemaNo); + } + } + + @Test + public void testAccessRecordListAdmin() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(17))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListUser() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(13))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListGuest() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()).andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(13))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + guestToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListOtherUser() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200)). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(17))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId()). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessRecordListWithoutAuthentication() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + CollectionType mapCollectionType = mapper.getTypeFactory() + .constructCollectionType(List.class, DataResource.class); + + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/"). + param("size", Integer.toString(200))). + andDo(print()).andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))). + andReturn(); + List<DataResource> resultList = mapper.readValue(mvcResult.getResponse().getContentAsString(), mapCollectionType); + for (DataResource item : resultList) { + this.mockMvc.perform(get("/api/v2/metadata/" + item.getId())). + andDo(print()). + andExpect(status().isOk()); + } + } + + @Test + public void testAccessAclForServiceWithoutAuthentication() throws Exception { + this.mockMvc.perform(get("/api/v2/metadata/" + ANONYMOUS_ID). + header("Accept", AclRecord.ACL_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isForbidden()); + } + + @Test + public void testAccessAclForServiceWithAuthentication() throws Exception { + this.mockMvc.perform(get("/api/v2/metadata/" + ANONYMOUS_ID). + header("Accept", AclRecord.ACL_RECORD_MEDIA_TYPE). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isForbidden()); + } + + @Test + public void testAccessAclForServiceWithAdminAuthentication() throws Exception { + this.mockMvc.perform(get("/api/v2/metadata/" + ANONYMOUS_ID). + header("Accept", AclRecord.ACL_RECORD_MEDIA_TYPE). + header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken)). + andDo(print()). + andExpect(status().isForbidden()); + } + + @Test + public void testAccessAclForServiceWithServiceToken() throws Exception { + MvcResult mvcResult = this.mockMvc.perform(get("/api/v2/metadata/" + ANONYMOUS_ID). + header("Accept", AclRecord.ACL_RECORD_MEDIA_TYPE). + header(HttpHeaders.AUTHORIZATION, "Bearer " + serviceToken)). + andDo(print()). + andExpect(status().isOk()). + andExpect(MockMvcResultMatchers.jsonPath("$.read.length()", greaterThan(1))). + andReturn(); + ObjectMapper map = new ObjectMapper(); + AclRecord result = map.readValue(mvcResult.getResponse().getContentAsString(), AclRecord.class); + Assert.assertNotNull(result); + Assert.assertTrue(result.getRead().contains(otherUserPrincipal)); + Assert.assertTrue(result.getRead().contains(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL)); + } + + /** + * Ingest metadata with 'otheruser' set permissions for admin, user and guest. + * + * @param schemaId + * @param user + * @param guest + * @throws Exception + */ + private void ingestDataResource(String schemaId, PERMISSION user, PERMISSION guest) throws Exception { + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(schemaId, SCHEMA_ID); + Set<AclEntry> aclEntries = new HashSet<>(); + if (user != PERMISSION.NONE) { + aclEntries.add(new AclEntry(userPrincipal, user)); + } + if (guest != PERMISSION.NONE) { + aclEntries.add(new AclEntry(guestPrincipal, guest)); + } + if (!aclEntries.isEmpty()) { + record.setAcls(aclEntries); + } + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", CreateSchemaUtil.KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v2/metadata/"). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()).andExpect(status().isCreated()). + andReturn(); + } + + /** + * Ingest metadata with 'otheruser' set permissions for admin, user and guest. + * + * @param schemaId + * @throws Exception + */ + private void ingestDataResource4UnregisteredUsers(String schemaId) throws Exception { + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(ANONYMOUS_ID, SCHEMA_ID); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); + record.setAcls(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("document", "metadata.xml", "application/xml", CreateSchemaUtil.KIT_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v2/metadata/"). + file(recordFile). + file(schemaFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + } + + public static synchronized boolean isInitialized() { + boolean returnValue = alreadyInitialized; + alreadyInitialized = Boolean.TRUE; + + return returnValue; + } +} From 8485eb89f972dd1027ef4c3eeada8cabdf44ace0 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 26 Jul 2024 08:31:01 +0200 Subject: [PATCH 051/181] Fix setting state of data resource to null if no state is provided during update. --- .../util/DataResourceRecordUtil.java | 39 ++++++++++++++++++- .../web/impl/MetadataControllerImplV2.java | 17 ++------ .../impl/SchemaRegistryControllerImplV2.java | 8 +--- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 865069a8..ee3a5241 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -374,7 +374,6 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur metadataRecord = checkParameters(recordDocument, document, false); DataResource updatedDataResource; - LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); LOG.trace("Checking provided ETag."); @@ -391,6 +390,9 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur if (updatedDataResource.getRights() == null) { updatedDataResource.setRights(new HashSet<>()); } + if (updatedDataResource.getState() == null) { + updatedDataResource.setState(dataResource.getState()); + } } else { updatedDataResource = DataResourceUtils.copyDataResource(dataResource); } @@ -1976,4 +1978,39 @@ public static final String getSchemaDocumentUri(String schemaId, Long version) { public static final URI getMetadataDocumentUri(String id, String version) { return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, Long.parseLong(version), null, null)).toUri(); } + + /** + * Query for data resources with provided specification or all if no + * specification is provided. + * + * @param spec Specification of the data resources. + * @param pgbl The pageable object containing pagination information. + * @return Pageable Object holding all data resources fulfilling the + * specification. + */ + public static Page<DataResource> queryDataResources(Specification spec, Pageable pgbl) { + Page<DataResource> records = null; + try { + records = spec != null ? dataResourceDao.findAll(spec, pgbl) : dataResourceDao.findAll(pgbl); + if (LOG.isTraceEnabled()) { + if (spec != null) { + LOG.trace("Query data resources with spec '{}'", spec.toString()); + } else { + LOG.trace("Query all data resources..."); + } + LOG.trace("-----------------------------------------------"); + LOG.trace("List '{}' of '{}' data resources in total!", records.getContent().size(), records.getTotalElements()); + LOG.trace("-----------------------------------------------"); + int itemNo = 1; + for (DataResource item : records.getContent()) { + LOG.trace("#{} - '{}'", itemNo++, item); + } + LOG.trace("-----------------------------------------------"); + } + } catch (Exception ex) { + LOG.error("Error finding data resource records by specification!", ex); + throw ex; + } + return records; + } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index b6150552..9d1904c0 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -417,6 +417,7 @@ public ResponseEntity<List<DataResource>> getRecords( } spec = DataResourceRecordUtil.findBySchemaId(spec, schemaIds); spec = DataResourceRecordUtil.findByRelatedId(spec, relatedIds); + if ((updateFrom != null) || (updateUntil != null)) { spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); } @@ -426,18 +427,8 @@ public ResponseEntity<List<DataResource>> getRecords( List<DataResource.State> stateList = Arrays.asList(states); spec = spec.and(StateSpecification.toSpecification(stateList)); - if (LOG.isTraceEnabled()) { - Page<DataResource> records = dataResourceDao.findAll(pgbl); - LOG.trace("List all data resources..."); - LOG.trace("-----------------------------------------------"); - for (DataResource item : records.getContent()) { - LOG.trace("- '{}'", item); - } - LOG.trace("-----------------------------------------------"); - LOG.trace("Specification: '{}'", spec); - } - LOG.debug("Performing query for records."); - Page<DataResource> records = dataResourceDao.findAll(spec, pgbl); + Page<DataResource> records = DataResourceRecordUtil.queryDataResources(spec, pgbl); + LOG.trace("Transforming Dataresource to DataResource"); List<DataResource> recordList = records.getContent(); @@ -524,7 +515,7 @@ private RelatedIdentifier getRelatedIdentifier(DataResource result, RelatedIdent } return relatedIdentifier; } - + @Bean public RestTemplate restTemplate() { return new RestTemplate(); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index d5a80b04..7f7dc17e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -316,13 +316,7 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche spec = spec.and(StateSpecification.toSpecification(stateList)); LOG.debug("Performing query for records."); - Page<DataResource> records = null; - try { - records = dataResourceDao.findAll(spec, pgbl); - } catch (Exception ex) { - LOG.error("Error find metadata records by specification!", ex); - throw ex; - } + Page<DataResource> records = DataResourceRecordUtil.queryDataResources(spec, pgbl); List<DataResource> recordList = records.getContent(); if (LOG.isTraceEnabled()) { LOG.trace("Cleaning up schemaDocumentUri of query result."); From 3cbc5b2e77b7b17ef74af004db85f8a8c478606c Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 26 Jul 2024 09:15:03 +0200 Subject: [PATCH 052/181] Fix actuator info for API v2. --- .../util/DataResourceRecordUtil.java | 23 +++++++++++++++---- .../web/impl/MetadataControllerImplV2.java | 2 +- .../impl/SchemaRegistryControllerImplV2.java | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index ee3a5241..433ba500 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -49,13 +49,12 @@ import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; -import edu.kit.datamanager.repo.domain.Date; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.repo.domain.Scheme; -import edu.kit.datamanager.repo.domain.Title; import edu.kit.datamanager.repo.domain.acl.AclEntry; import edu.kit.datamanager.repo.service.IContentInformationService; import edu.kit.datamanager.repo.util.ContentDataUtils; @@ -1038,12 +1037,28 @@ public static <T> T mergeEntry(String description, T managed, T provided, boolea /** * Return the number of ingested documents. If there are two versions of the - * same document this will be counted as two. + * same document this will be counted as one. * * @return Number of registered documents. */ public static long getNoOfDocuments() { - return dataRecordDao.count(); + // Search for resource type of MetadataSchemaRecord + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + Pageable pgbl = PageRequest.of(0, 1); + return queryDataResources(spec, pgbl).getTotalElements(); + } + + /** + * Return the number of ingested schema documents. If there are two versions of the + * same document this will be counted as one. + * + * @return Number of registered documents. + */ + public static long getNoOfSchemaDocuments() { + // Search for resource type of MetadataSchemaRecord + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + Pageable pgbl = PageRequest.of(0, 1); + return queryDataResources(spec, pgbl).getTotalElements(); } public static void setToken(String bearerToken) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 9d1904c0..dbb5e40c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -494,7 +494,7 @@ public void contribute(Info.Builder builder) { Map<String, String> details = ActuatorUtil.testDirectory(basePath); if (!details.isEmpty()) { - details.put("No of metadata documents", Long.toString(MetadataRecordUtil.getNoOfDocuments())); + details.put("No of metadata documents", Long.toString(DataResourceRecordUtil.getNoOfDocuments())); builder.withDetail("metadataRepo", details); } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 7f7dc17e..7bdf3f50 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -374,7 +374,7 @@ public void contribute(Info.Builder builder) { Map<String, String> details = ActuatorUtil.testDirectory(basePath); if (!details.isEmpty()) { - details.put("No of schema documents", Long.toString(MetadataSchemaRecordUtil.getNoOfSchemas())); + details.put("No of schema documents", Long.toString(DataResourceRecordUtil.getNoOfSchemaDocuments())); builder.withDetail("schemaRepo", details); } } From c6fe9e4cc26eda2cf01899e310a1dbd8e9ae2a4d Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Sat, 3 Aug 2024 07:03:28 +0200 Subject: [PATCH 053/181] Fix wrong parsing of ResourceIdentifierType. --- .../kit/datamanager/metastore2/util/DataResourceRecordUtil.java | 2 +- .../edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 433ba500..56cd5543 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -569,7 +569,7 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app for (Identifier identifier : dataResource.getAlternateIdentifiers()) { if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().getValue())); + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().name())); LOG.trace("Set PID to '{}' of type '{}'", resourceIdentifier.getIdentifier(), resourceIdentifier.getIdentifierType()); metadataRecord.setPid(resourceIdentifier); break; diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 1e1d04bd..8231124a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -485,7 +485,7 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli for (Identifier identifier : dataResource.getAlternateIdentifiers()) { if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().getValue())); + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().name())); LOG.trace("Set PID to '{}' of type '{}'", resourceIdentifier.getIdentifier(), resourceIdentifier.getIdentifierType()); metadataRecord.setPid(resourceIdentifier); break; From 77c79e541fcf0d0120de36c64e9dbad0c3cc563a Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 23 Aug 2024 11:25:14 +0200 Subject: [PATCH 054/181] Fix wrong resource type for metadata documents. Fix #558 Fix #560 --- .../util/DataResourceRecordUtil.java | 63 ++++++++++--------- .../web/impl/MetadataControllerImplV2.java | 4 +- .../test/MetadataControllerTestV2.java | 42 +++++++++++++ 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 56cd5543..9961a099 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -33,12 +33,12 @@ import edu.kit.datamanager.metastore2.domain.DataRecord; import edu.kit.datamanager.metastore2.domain.MetadataRecord; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; + import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; + import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; -import static edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType.INTERNAL; -import static edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType.URL; import edu.kit.datamanager.metastore2.domain.SchemaRecord; import edu.kit.datamanager.metastore2.domain.Url2Path; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; @@ -62,6 +62,7 @@ import edu.kit.datamanager.util.AuthenticationHelper; import edu.kit.datamanager.util.ControllerUtils; import io.swagger.v3.core.util.Json; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; @@ -87,6 +88,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; + import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -176,9 +178,9 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio LOG.error(message); throw new BadArgumentException(message); } - // Check if id is lower case and URL encodable. - // and save as alternate identifier. (In case of - // upper letters in both versions (with and without + // Check if id is lower case and URL encodable. + // and save as alternate identifier. (In case of + // upper letters in both versions (with and without // upper letters) DataResourceRecordUtil.check4validSchemaId(metadataRecord); // End of parameter checks @@ -219,7 +221,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio throw new UnprocessableEntityException(message); } } - // reload data resource + // reload data resource metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(applicationProperties, metadataRecord.getId(), Long.valueOf(metadataRecord.getVersion())); return metadataRecord; @@ -247,16 +249,9 @@ public static DataResource createDataResourceRecord4Metadata(MetastoreConfigurat check4validId(metadataRecord, true); } // End of parameter checks - // validate schema document / determine type if not given + // validate schema document / determine or correct resource type validateMetadataDocument(applicationProperties, metadataRecord, document); - // set internal parameters - if (metadataRecord.getResourceType() == null) { - LOG.trace("No mimetype set! Try to determine..."); - if (document.getContentType() != null) { - LOG.trace("Set mimetype determined from document: '{}'", document.getContentType()); - metadataRecord.getFormats().add(document.getContentType()); - } - } + metadataRecord.setVersion(Long.toString(1)); // create record. DataResource dataResource = metadataRecord; @@ -989,10 +984,10 @@ public static Set<AclEntry> mergeAcl(Set<AclEntry> managed, Set<AclEntry> provid provided = (provided == null) ? new HashSet<>() : provided; if (!provided.isEmpty()) { if (!provided.equals(managed)) { - // check for special access rights + // check for special access rights // - only administrators are allowed to change ACL checkAccessRights(managed, true); - // - at least principal has to remain as ADMIN + // - at least principal has to remain as ADMIN checkAccessRights(provided, false); LOG.trace("Updating record acl from {} to {}.", managed, provided); managed = provided; @@ -1041,7 +1036,7 @@ public static <T> T mergeEntry(String description, T managed, T provided, boolea * * @return Number of registered documents. */ - public static long getNoOfDocuments() { + public static long getNoOfMetadataDocuments() { // Search for resource type of MetadataSchemaRecord Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); Pageable pgbl = PageRequest.of(0, 1); @@ -1049,8 +1044,8 @@ public static long getNoOfDocuments() { } /** - * Return the number of ingested schema documents. If there are two versions of the - * same document this will be counted as one. + * Return the number of ingested schema documents. If there are two versions + * of the same document this will be counted as one. * * @return Number of registered documents. */ @@ -1152,7 +1147,6 @@ private static void saveNewDataRecord(DataRecord dataRecord) { * * @param aclEntries AclEntries of resource. * @param currentAcl Check current ACL (true) or new one (false). - * * @return Allowed (true) or not. */ public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean currentAcl) { @@ -1276,6 +1270,13 @@ public static void check4RelatedResource(DataResource dataResource, RelatedIdent } } + /** + * Test if exactly one schema and one related resource exists. This method + * does NOT check the correctness of the references. + * + * @param dataResource Data resource of a metadata document. + * @throws BadArgumentException Related resources are not defined as expected. + */ public static void validateRelatedResources4MetadataDocuments(DataResource dataResource) throws BadArgumentException { int noOfRelatedData = 0; int noOfRelatedSchemas = 0; @@ -1662,12 +1663,11 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp /** * Gets SchemaRecord from identifier. Afterwards there should be a clean up. * - * @see #cleanUp(edu.kit.datamanager.metastore2.domain.ResourceIdentifier, - * edu.kit.datamanager.metastore2.domain.SchemaRecord) - * * @param identifier ResourceIdentifier of type INTERNAL or URL. * @param version Version (may be null) * @return schema record. + * @see #cleanUp(edu.kit.datamanager.metastore2.domain.ResourceIdentifier, + * edu.kit.datamanager.metastore2.domain.SchemaRecord) */ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long version) { LOG.trace("getSchemaRecord {},{}", identifier, version); @@ -1889,7 +1889,8 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app } /** - * Validate metadata document with given schema. + * Validate metadata document with given schema. Determine type if not already + * given or check type. * * @param metastoreProperties Configuration for accessing services * @param metadataRecord metadata of the document. @@ -1925,6 +1926,10 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro try { validateMetadataDocument(metastoreProperties, document, findByAlternateId); validationSuccess = true; + // After successful validation set type for metadata document resource. + MetadataSchemaRecord.SCHEMA_TYPE type = findByAlternateId.getType(); + metadataRecord.setResourceType(ResourceType.createResourceType(type + METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + // } catch (Exception ex) { String message = "Error validating document!"; LOG.error(message, ex); @@ -2013,10 +2018,10 @@ public static Page<DataResource> queryDataResources(Specification spec, Pageable } else { LOG.trace("Query all data resources..."); } - LOG.trace("-----------------------------------------------"); - LOG.trace("List '{}' of '{}' data resources in total!", records.getContent().size(), records.getTotalElements()); - LOG.trace("-----------------------------------------------"); - int itemNo = 1; + LOG.trace("-----------------------------------------------"); + LOG.trace("List '{}' of '{}' data resources in total!", records.getContent().size(), records.getTotalElements()); + LOG.trace("-----------------------------------------------"); + int itemNo = 1; for (DataResource item : records.getContent()) { LOG.trace("#{} - '{}'", itemNo++, item); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index dbb5e40c..ca920e6c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -17,7 +17,7 @@ import com.fasterxml.jackson.core.JsonParseException; import static edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE.INTERNAL; -import static edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE.URL; + import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; @@ -494,7 +494,7 @@ public void contribute(Info.Builder builder) { Map<String, String> details = ActuatorUtil.testDirectory(basePath); if (!details.isEmpty()) { - details.put("No of metadata documents", Long.toString(DataResourceRecordUtil.getNoOfDocuments())); + details.put("No of metadata documents", Long.toString(DataResourceRecordUtil.getNoOfMetadataDocuments())); builder.withDetail("metadataRepo", details); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index b7a85c0a..4410916a 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -78,6 +78,7 @@ import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.repo.domain.Scheme; import java.util.Locale; import java.util.UUID; @@ -772,6 +773,47 @@ public void testCreateRecordWithInvalidMetadata() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isUnprocessableEntity()).andReturn(); } + @Test + public void testCreateRecordWithInvalidorEmptyResource() throws Exception { + String id = "testCreateRecordWithInvalidorEmptyResource"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + ObjectMapper mapper = new ObjectMapper(); + // empty resource type + record.setResourceType(null); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + + // wrong resource type + id = "testCreateRecordWithWrongType"; + record.setId(id); + record.getAlternateIdentifiers().clear(); + record.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.XML_METADATA_TYPE)); + + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + + // wrong resource value + id = "testCreateRecordWithWrongValue"; + record.setId(id); + record.getAlternateIdentifiers().clear(); + record.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.XML_METADATA_TYPE + "invalid", ResourceType.TYPE_GENERAL.MODEL)); + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + @Test public void testCreateRecordWithoutRecord() throws Exception { MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT.getBytes()); From f1de4c70b5d782f70bfcde4e47b3407e6bcc9afe Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 23 Aug 2024 11:53:01 +0200 Subject: [PATCH 055/181] Optimize imports. --- .../messaging/MetadataResourceMessage.java | 6 +- .../datamanager/metastore2/Application.java | 11 +-- .../configuration/ApplicationProperties.java | 5 +- .../configuration/MetastoreConfiguration.java | 1 + .../configuration/OpenApiDefinitions.java | 2 +- .../configuration/WebSecurityConfig.java | 5 +- .../metastore2/dao/IDataRecordDao.java | 7 +- .../metastore2/dao/IMetadataFormatDao.java | 3 +- .../metastore2/dao/ISchemaRecordDao.java | 3 +- .../dao/ISchemaSynchronizationEventDao.java | 3 +- .../metastore2/dao/IUrl2PathDao.java | 5 +- .../metastore2/domain/AclRecord.java | 5 +- .../metastore2/domain/DataRecord.java | 10 +-- .../domain/LinkedMetadataRecord.java | 9 +-- .../metastore2/domain/MetadataRecord.java | 5 +- .../domain/MetadataSchemaRecord.java | 14 ++-- .../metastore2/domain/ResourceIdentifier.java | 7 +- .../metastore2/domain/SchemaRecord.java | 13 +--- .../domain/SchemaSynchronizationEvent.java | 3 +- .../metastore2/domain/Url2Path.java | 3 +- .../domain/oaipmh/MetadataFormat.java | 3 +- .../metastore2/dto/EditorRequestMetadata.java | 3 +- .../metastore2/dto/EditorRequestSchema.java | 3 +- .../dto/TabulatorLocalPagination.java | 3 +- .../dto/TabulatorRemotePagination.java | 3 +- .../filter/AccessLoggingFilter.java | 9 +-- .../health/ElasticsearchHealthCheck.java | 3 +- .../health/MetadataRepoHealthCheck.java | 5 +- .../health/SchemaRepoHealthCheck.java | 5 +- .../service/AbstractOAIPMHRepository.java | 15 ++-- .../service/MetastoreOAIPMHRepository.java | 54 ++++++--------- .../metastore2/oaipmh/util/OAIPMHBuilder.java | 29 ++------ .../oaipmh/web/OaiPmhController.java | 9 +-- .../runner/ElasticIndexerRunner.java | 13 +--- .../service/SchemaSynchronizationService.java | 20 +++--- .../metastore2/util/ActuatorUtil.java | 5 +- .../util/DataResourceRecordUtil.java | 68 ++++++------------- .../metastore2/util/DownloadUtil.java | 11 +-- .../metastore2/util/JsonUtils.java | 18 ++--- .../metastore2/util/MetadataRecordUtil.java | 55 ++++++--------- .../util/MetadataSchemaRecordUtil.java | 52 +++++--------- .../metastore2/util/SchemaUtils.java | 19 +++--- .../metastore2/validation/IValidator.java | 1 + .../validation/impl/JsonValidator.java | 7 +- .../validation/impl/XmlValidator.java | 29 +++----- .../web/ILandingPageController.java | 1 - .../web/ILandingPageControllerV2.java | 1 - .../metastore2/web/IMetadataController.java | 15 ++-- .../metastore2/web/IMetadataControllerV2.java | 14 ++-- .../web/ISchemaRegistryController.java | 12 ++-- .../web/ISchemaRegistryControllerV2.java | 12 ++-- .../web/impl/FrontendControllerImpl.java | 5 +- .../web/impl/LandingPageControllerImpl.java | 7 +- .../web/impl/LandingPageControllerImplV2.java | 9 +-- .../web/impl/MetadataControllerImpl.java | 33 +++------ .../web/impl/MetadataControllerImplV2.java | 25 +++---- .../web/impl/MetadataEditorController.java | 15 ++-- .../web/impl/MetadataSearchController.java | 6 +- .../web/impl/MetadataSearchControllerV2.java | 6 +- .../impl/SchemaRegistryControllerImpl.java | 31 ++++----- .../impl/SchemaRegistryControllerImplV2.java | 28 ++++---- .../org/openarchives/oai/_2/AboutType.java | 4 -- .../org/openarchives/oai/_2/HeaderType.java | 8 +-- .../org/openarchives/oai/_2/IdentifyType.java | 7 +- .../oai/_2/ListIdentifiersType.java | 1 + .../oai/_2/ListMetadataFormatsType.java | 1 + .../openarchives/oai/_2/ListRecordsType.java | 1 + .../org/openarchives/oai/_2/ListSetsType.java | 1 + .../oai/_2/MetadataFormatType.java | 6 +- .../openarchives/oai/_2/OAIPMHerrorType.java | 11 +-- .../org/openarchives/oai/_2/OAIPMHtype.java | 10 +-- .../openarchives/oai/_2/ObjectFactory.java | 1 + .../org/openarchives/oai/_2/RecordType.java | 1 + .../org/openarchives/oai/_2/RequestType.java | 7 +- .../oai/_2/ResumptionTokenType.java | 10 +-- .../java/org/openarchives/oai/_2/SetType.java | 5 +- .../org/openarchives/oai/_2/StatusType.java | 3 - .../fontawesome/webfonts/fa-brands-400.svg | 2 +- .../fontawesome/webfonts/fa-regular-400.svg | 2 +- .../MetadataResourceMessageTest.java | 7 +- .../metastore2/dao/IDataRecordDaoTest.java | 24 +++---- .../metastore2/dao/ISchemaRecordDaoTest.java | 12 ++-- .../metastore2/dao/IUrl2PathDaoTest.java | 28 ++++---- .../dto/EditorRequestMetadataTest.java | 10 ++- .../dto/EditorRequestSchemaTest.java | 10 ++- .../dto/TabulatorFormatterParamTest.java | 9 +-- .../metastore2/dto/TabulatorItemsTest.java | 9 +-- .../metastore2/test/ActuatorTest.java | 34 +++++----- .../metastore2/test/CreateSchemaUtil.java | 8 ++- .../test/FrontendControllerTest.java | 34 +++++----- .../JsonSchemaRegistryControllerTest.java | 46 +++++-------- .../test/MetadataControllerFilterTest.java | 30 ++++---- .../test/MetadataControllerTest.java | 38 +++++------ .../test/MetadataControllerTestV2.java | 47 ++++++------- ...ntrollerTestWithAuthenticationEnabled.java | 38 ++++++----- ...trollerTestWithInternalSchemaRegistry.java | 45 ++++++------ ...tadataControllerWithWrongRegistryTest.java | 1 + ...MetadataControllerWithoutRegistryTest.java | 30 ++++---- .../test/MetadataEditorControllerTest.java | 7 +- .../test/SchemaRegistryControllerTest.java | 50 ++++++-------- .../test/SchemaRegistryControllerTestV2.java | 53 ++++++--------- 101 files changed, 588 insertions(+), 845 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java index aded6563..8502c53a 100644 --- a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java +++ b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java @@ -16,13 +16,13 @@ package edu.kit.datamanager.entities.messaging; import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.domain.DataResource; -import java.util.HashMap; -import java.util.Map; import lombok.Data; import lombok.EqualsAndHashCode; +import java.util.HashMap; +import java.util.Map; + /** * Handler for creating messages for metadata. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/Application.java b/src/main/java/edu/kit/datamanager/metastore2/Application.java index ae24f1f1..c61b4586 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/Application.java +++ b/src/main/java/edu/kit/datamanager/metastore2/Application.java @@ -52,11 +52,6 @@ import edu.kit.datamanager.service.IAuditService; import edu.kit.datamanager.service.IMessagingService; import edu.kit.datamanager.service.impl.RabbitMQMessagingService; -import java.net.MalformedURLException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import org.javers.core.Javers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,6 +71,12 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.scheduling.annotation.EnableScheduling; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + /** * Main class starting spring boot service of MetaStore. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/configuration/ApplicationProperties.java b/src/main/java/edu/kit/datamanager/metastore2/configuration/ApplicationProperties.java index a37ceae5..31332a73 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/configuration/ApplicationProperties.java +++ b/src/main/java/edu/kit/datamanager/metastore2/configuration/ApplicationProperties.java @@ -17,8 +17,6 @@ import edu.kit.datamanager.annotations.LocalFolderURL; import edu.kit.datamanager.configuration.GenericApplicationProperties; -import java.net.URL; -import java.util.List; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.beans.factory.annotation.Value; @@ -27,6 +25,9 @@ import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; +import java.net.URL; +import java.util.List; + /** * Properties for configuration of MetaStore. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/configuration/MetastoreConfiguration.java b/src/main/java/edu/kit/datamanager/metastore2/configuration/MetastoreConfiguration.java index e182023d..c4032827 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/configuration/MetastoreConfiguration.java +++ b/src/main/java/edu/kit/datamanager/metastore2/configuration/MetastoreConfiguration.java @@ -17,6 +17,7 @@ import edu.kit.datamanager.metastore2.validation.IValidator; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; + import java.util.List; /** diff --git a/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java b/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java index 301df5b1..d19792bf 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java +++ b/src/main/java/edu/kit/datamanager/metastore2/configuration/OpenApiDefinitions.java @@ -15,10 +15,10 @@ */ package edu.kit.datamanager.metastore2.configuration; -import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/edu/kit/datamanager/metastore2/configuration/WebSecurityConfig.java b/src/main/java/edu/kit/datamanager/metastore2/configuration/WebSecurityConfig.java index 9ca308e9..7cbdb0a4 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/configuration/WebSecurityConfig.java +++ b/src/main/java/edu/kit/datamanager/metastore2/configuration/WebSecurityConfig.java @@ -18,8 +18,6 @@ import edu.kit.datamanager.security.filter.KeycloakTokenFilter; import edu.kit.datamanager.security.filter.NoAuthenticationFilter; import edu.kit.datamanager.security.filter.PublicAuthenticationFilter; -import java.util.Optional; -import jakarta.servlet.Filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,7 +25,6 @@ import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; -import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -45,6 +42,8 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; +import java.util.Optional; + /** * Configuration for web security. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/IDataRecordDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/IDataRecordDao.java index c0f179d2..20791172 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/IDataRecordDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/IDataRecordDao.java @@ -16,13 +16,14 @@ package edu.kit.datamanager.metastore2.dao; import edu.kit.datamanager.metastore2.domain.DataRecord; -import java.time.Instant; -import java.util.List; -import java.util.Optional; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.time.Instant; +import java.util.List; +import java.util.Optional; + /** * DAO for the record of a metadata document. * @author Torridity diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/IMetadataFormatDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/IMetadataFormatDao.java index f24f96bf..04c8543d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/IMetadataFormatDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/IMetadataFormatDao.java @@ -16,11 +16,12 @@ package edu.kit.datamanager.metastore2.dao; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; -import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; +import java.util.List; + /** * DAO for record for OAI-PMH. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java index 19bb1d4d..fb977ea5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java @@ -16,10 +16,11 @@ package edu.kit.datamanager.metastore2.dao; import edu.kit.datamanager.metastore2.domain.SchemaRecord; -import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.List; + /** * DAO for the record of a schema document. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java index c8630596..6f3254f4 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java @@ -16,9 +16,10 @@ package edu.kit.datamanager.metastore2.dao; import edu.kit.datamanager.metastore2.domain.SchemaSynchronizationEvent; -import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + /** * DAO for synchronization events. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDao.java index c5276a10..64eddf99 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDao.java @@ -6,11 +6,12 @@ package edu.kit.datamanager.metastore2.dao; import edu.kit.datamanager.metastore2.domain.Url2Path; -import java.util.List; -import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.List; +import java.util.Optional; + /** * Database linking URL to local path (if available) * @author Torridity diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java index 940cc881..34c4867f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java @@ -21,11 +21,12 @@ import jakarta.persistence.OneToMany; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.http.MediaType; + import java.io.Serializable; import java.util.HashSet; import java.util.Set; -import lombok.Data; -import org.springframework.http.MediaType; /** * Record holding metadata document + list of SIDs allowed to at least read the document. diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/DataRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/DataRecord.java index b81dca4d..bd422f96 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/DataRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/DataRecord.java @@ -15,17 +15,13 @@ */ package edu.kit.datamanager.metastore2.domain; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import lombok.Data; + import java.io.Serializable; import java.time.Instant; -import lombok.Data; /** * Simplified record for a metadata document. diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/LinkedMetadataRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/LinkedMetadataRecord.java index 1b3275da..2373c2da 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/LinkedMetadataRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/LinkedMetadataRecord.java @@ -16,15 +16,12 @@ package edu.kit.datamanager.metastore2.domain; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + /** * Simplified record for linked resources for metadata document. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataRecord.java index dfec5342..b88709c2 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataRecord.java @@ -29,13 +29,14 @@ import jakarta.persistence.OneToMany; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.http.MediaType; + import java.io.Serializable; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Set; -import lombok.Data; -import org.springframework.http.MediaType; /** * Record for a metadata document. diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecord.java index c06ad45a..049eebf5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecord.java @@ -24,24 +24,18 @@ import edu.kit.datamanager.repo.domain.acl.AclEntry; import edu.kit.datamanager.util.json.CustomInstantDeserializer; import edu.kit.datamanager.util.json.CustomInstantSerializer; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Transient; +import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.http.MediaType; + import java.io.Serializable; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Locale; import java.util.Set; -import lombok.Data; -import org.springframework.http.MediaType; /** * Record for a metadata document. diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java b/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java index 7cc02ea0..9e64b83d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java @@ -1,15 +1,16 @@ package edu.kit.datamanager.metastore2.domain; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import java.util.HashMap; -import java.util.Map; import com.google.gson.annotations.SerializedName; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.validation.constraints.NotBlank; +import lombok.Data; + import java.io.Serializable; import java.util.Arrays; -import lombok.Data; +import java.util.HashMap; +import java.util.Map; @Entity @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index 1cea82d3..8c5145c3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -15,20 +15,13 @@ */ package edu.kit.datamanager.metastore2.domain; -import edu.kit.datamanager.entities.Identifier; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + /** * Simplified record for a schema document. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaSynchronizationEvent.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaSynchronizationEvent.java index b22cdcb4..05f23008 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaSynchronizationEvent.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaSynchronizationEvent.java @@ -19,9 +19,10 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import java.time.Instant; import lombok.Data; +import java.time.Instant; + /** * Event for synchronization. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/Url2Path.java b/src/main/java/edu/kit/datamanager/metastore2/domain/Url2Path.java index 43f254e9..774047c9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/Url2Path.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/Url2Path.java @@ -22,9 +22,10 @@ import jakarta.persistence.Id; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + /** * Entity for holding internal path information for given URL. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/oaipmh/MetadataFormat.java b/src/main/java/edu/kit/datamanager/metastore2/domain/oaipmh/MetadataFormat.java index c182a1ed..3f7414ba 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/oaipmh/MetadataFormat.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/oaipmh/MetadataFormat.java @@ -19,9 +19,10 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.validation.constraints.NotBlank; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + /** * Record for OAI-PMH holding * <ul> diff --git a/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadata.java b/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadata.java index 5e82fd3c..74adf9ed 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadata.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadata.java @@ -6,11 +6,12 @@ package edu.kit.datamanager.metastore2.dto; import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import java.util.List; import lombok.Builder; import lombok.Getter; import org.json.simple.JSONObject; +import java.util.List; + /** * Data transfer object for Web UI. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchema.java b/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchema.java index fc30a060..d9eda377 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchema.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchema.java @@ -6,11 +6,12 @@ package edu.kit.datamanager.metastore2.dto; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import java.util.List; import lombok.Builder; import lombok.Getter; import org.json.simple.JSONObject; +import java.util.List; + /** * Data transfer object for Web UI. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorLocalPagination.java b/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorLocalPagination.java index 4a3fdb15..e50422d3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorLocalPagination.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorLocalPagination.java @@ -5,10 +5,11 @@ package edu.kit.datamanager.metastore2.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; import lombok.Builder; import lombok.Getter; +import java.util.List; + /** * Helper class for tabulator local pagination used by web frontend. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorRemotePagination.java b/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorRemotePagination.java index 109d21c7..f48e0960 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorRemotePagination.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dto/TabulatorRemotePagination.java @@ -6,10 +6,11 @@ package edu.kit.datamanager.metastore2.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; import lombok.Builder; import lombok.Getter; +import java.util.List; + /** * Helper class for tabulator remote pagination used by web frontend. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/filter/AccessLoggingFilter.java b/src/main/java/edu/kit/datamanager/metastore2/filter/AccessLoggingFilter.java index d26a8a07..9582146f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/filter/AccessLoggingFilter.java +++ b/src/main/java/edu/kit/datamanager/metastore2/filter/AccessLoggingFilter.java @@ -16,17 +16,14 @@ package edu.kit.datamanager.metastore2.filter; import edu.kit.datamanager.security.filter.KeycloakTokenFilter; -import jakarta.servlet.Filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; +import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; + /** * Filter for logging access by users */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/health/ElasticsearchHealthCheck.java b/src/main/java/edu/kit/datamanager/metastore2/health/ElasticsearchHealthCheck.java index ce162ad4..ebbb7060 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/health/ElasticsearchHealthCheck.java +++ b/src/main/java/edu/kit/datamanager/metastore2/health/ElasticsearchHealthCheck.java @@ -17,13 +17,14 @@ import edu.kit.datamanager.configuration.SearchConfiguration; import edu.kit.datamanager.metastore2.util.ActuatorUtil; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; +import java.util.Map; + /** * Collect information about metadata document repository for health actuator. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/health/MetadataRepoHealthCheck.java b/src/main/java/edu/kit/datamanager/metastore2/health/MetadataRepoHealthCheck.java index bda57ca7..705143e5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/health/MetadataRepoHealthCheck.java +++ b/src/main/java/edu/kit/datamanager/metastore2/health/MetadataRepoHealthCheck.java @@ -18,14 +18,15 @@ import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; -import java.net.URL; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; +import java.net.URL; +import java.util.Map; + /** * Collect information about metadata document repository for health actuator. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/health/SchemaRepoHealthCheck.java b/src/main/java/edu/kit/datamanager/metastore2/health/SchemaRepoHealthCheck.java index b5ba1529..9c0ba9f8 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/health/SchemaRepoHealthCheck.java +++ b/src/main/java/edu/kit/datamanager/metastore2/health/SchemaRepoHealthCheck.java @@ -18,14 +18,15 @@ import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; -import java.net.URL; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; +import java.net.URL; +import java.util.Map; + /** * Collect information about schema repository for health actuator. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/AbstractOAIPMHRepository.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/AbstractOAIPMHRepository.java index aba3f202..47b9d387 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/AbstractOAIPMHRepository.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/AbstractOAIPMHRepository.java @@ -16,21 +16,16 @@ package edu.kit.datamanager.metastore2.oaipmh.service; import edu.kit.datamanager.metastore2.oaipmh.util.OAIPMHBuilder; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.List; -import java.util.TimeZone; import org.openarchives.oai._2.DeletedRecordType; import org.openarchives.oai._2.DescriptionType; import org.openarchives.oai._2.GranularityType; -import static org.openarchives.oai._2.VerbType.GET_RECORD; -import static org.openarchives.oai._2.VerbType.IDENTIFY; -import static org.openarchives.oai._2.VerbType.LIST_IDENTIFIERS; -import static org.openarchives.oai._2.VerbType.LIST_METADATA_FORMATS; -import static org.openarchives.oai._2.VerbType.LIST_RECORDS; -import static org.openarchives.oai._2.VerbType.LIST_SETS; import org.slf4j.LoggerFactory; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.TimeZone; + /** * An abstract OAI-PMH repository implementation that can be used to implement * specific OAI-PMH repositories according to the OAI-PMH 2.0 protocol. diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java index 23f0ed38..be1dd819 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java @@ -15,66 +15,54 @@ */ package edu.kit.datamanager.metastore2.oaipmh.service; -import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.configuration.OaiPmhConfiguration; import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; import edu.kit.datamanager.metastore2.domain.DataRecord; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; -import edu.kit.datamanager.metastore2.configuration.OaiPmhConfiguration; import edu.kit.datamanager.metastore2.oaipmh.util.OAIPMHBuilder; import edu.kit.datamanager.repo.util.DataResourceUtils; import edu.kit.datamanager.util.xml.DataCiteMapper; import edu.kit.datamanager.util.xml.DublinCoreMapper; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Marshaller; -import java.util.function.UnaryOperator; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.datacite.schema.kernel_4.Resource; -import org.openarchives.oai._2.DeletedRecordType; -import org.openarchives.oai._2.DescriptionType; -import org.openarchives.oai._2.GranularityType; -import org.openarchives.oai._2.MetadataFormatType; -import org.openarchives.oai._2.OAIPMHerrorcodeType; -import org.openarchives.oai._2.ResumptionTokenType; +import org.openarchives.oai._2.*; import org.purl.dc.elements._1.ElementContainer; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Component; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.w3c.dom.Document; import org.xml.sax.SAXException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.function.UnaryOperator; + /** * Simple OAI-PMH repository implementation taking its information from a KIT * Data Manager instance. Metadata formats are obtained from the metadata schema diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java index f3ee08f7..cf000349 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java @@ -15,33 +15,16 @@ */ package edu.kit.datamanager.metastore2.oaipmh.util; +import edu.kit.datamanager.metastore2.oaipmh.service.AbstractOAIPMHRepository; +import org.openarchives.oai._2.*; +import org.slf4j.LoggerFactory; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; import java.text.ParseException; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; -import org.openarchives.oai._2.AboutType; -import org.openarchives.oai._2.DescriptionType; -import org.openarchives.oai._2.GetRecordType; -import org.openarchives.oai._2.HeaderType; -import org.openarchives.oai._2.IdentifyType; -import org.openarchives.oai._2.ListIdentifiersType; -import org.openarchives.oai._2.ListMetadataFormatsType; -import org.openarchives.oai._2.ListRecordsType; -import org.openarchives.oai._2.ListSetsType; -import org.openarchives.oai._2.MetadataFormatType; -import org.openarchives.oai._2.MetadataType; -import org.openarchives.oai._2.OAIPMHerrorType; -import org.openarchives.oai._2.OAIPMHerrorcodeType; -import org.openarchives.oai._2.OAIPMHtype; -import org.openarchives.oai._2.RecordType; -import org.openarchives.oai._2.RequestType; -import org.openarchives.oai._2.SetType; -import org.openarchives.oai._2.VerbType; -import edu.kit.datamanager.metastore2.oaipmh.service.AbstractOAIPMHRepository; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; -import org.openarchives.oai._2.ResumptionTokenType; -import org.slf4j.LoggerFactory; /** * Helper class for collecting request parameters and building OAI-PMH response. diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhController.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhController.java index 640ba1ec..deb319a3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhController.java @@ -20,10 +20,6 @@ import io.swagger.v3.oas.annotations.Parameter; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.Marshaller; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.text.ParseException; -import java.util.Date; import org.apache.http.HttpStatus; import org.openarchives.oai._2.OAIPMHerrorcodeType; import org.openarchives.oai._2.OAIPMHtype; @@ -38,6 +34,11 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.Date; + /** * Controller for OAI-PMH protocol. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 47130bd0..c4a32f80 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -21,21 +21,12 @@ import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; -import edu.kit.datamanager.metastore2.domain.DataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; -import edu.kit.datamanager.metastore2.domain.SchemaRecord; -import edu.kit.datamanager.metastore2.domain.Url2Path; +import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImpl; import edu.kit.datamanager.service.IMessagingService; import edu.kit.datamanager.service.impl.LogfileMessagingService; import edu.kit.datamanager.util.ControllerUtils; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -44,6 +35,8 @@ import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.stereotype.Component; +import java.util.*; + /** * Class for indexing all metadata documents of given schemas Arguments have to * start with at least 'reindex' followed by all indices which have to be diff --git a/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java b/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java index b214c84a..8257ec46 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java +++ b/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java @@ -23,28 +23,24 @@ import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.SchemaSynchronizationEvent; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.time.Instant; -import java.util.Arrays; -import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.time.Instant; +import java.util.Arrays; +import java.util.Optional; + /** * Service for snchronizing repositories. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java index dace4206..b33a772a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java @@ -18,6 +18,9 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import edu.kit.datamanager.clients.SimpleServiceClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -28,8 +31,6 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Utility class for actuators collecting information details about local diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 9961a099..3defcc95 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -20,27 +20,14 @@ import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; -import edu.kit.datamanager.exceptions.AccessForbiddenException; -import edu.kit.datamanager.exceptions.BadArgumentException; -import edu.kit.datamanager.exceptions.CustomInternalServerError; -import edu.kit.datamanager.exceptions.ResourceNotFoundException; -import edu.kit.datamanager.exceptions.UnprocessableEntityException; +import edu.kit.datamanager.exceptions.*; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; -import edu.kit.datamanager.metastore2.domain.DataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; - -import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; -import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; - -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; -import edu.kit.datamanager.metastore2.domain.SchemaRecord; -import edu.kit.datamanager.metastore2.domain.Url2Path; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; import edu.kit.datamanager.metastore2.validation.IValidator; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; @@ -50,11 +37,7 @@ import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; -import edu.kit.datamanager.repo.domain.ContentInformation; -import edu.kit.datamanager.repo.domain.DataResource; -import edu.kit.datamanager.repo.domain.RelatedIdentifier; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Scheme; +import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.domain.acl.AclEntry; import edu.kit.datamanager.repo.service.IContentInformationService; import edu.kit.datamanager.repo.util.ContentDataUtils; @@ -62,33 +45,6 @@ import edu.kit.datamanager.util.AuthenticationHelper; import edu.kit.datamanager.util.ControllerUtils; import io.swagger.v3.core.util.Json; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.function.UnaryOperator; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Stream; - import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,6 +61,24 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.UriComponentsBuilder; +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.function.UnaryOperator; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; + /** * Utility class for handling json documents */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java index 5d3c376f..60c8155d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java @@ -17,6 +17,12 @@ import edu.kit.datamanager.clients.SimpleServiceClient; import edu.kit.datamanager.exceptions.CustomInternalServerError; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; + import java.io.File; import java.io.IOException; import java.net.URI; @@ -27,11 +33,6 @@ import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; /** * Utility class for downloading resources from internet. diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/JsonUtils.java b/src/main/java/edu/kit/datamanager/metastore2/util/JsonUtils.java index 3211f40c..f77579ba 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/JsonUtils.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/JsonUtils.java @@ -17,26 +17,20 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.networknt.schema.JsonSchema; -import com.networknt.schema.JsonSchemaException; -import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.*; import com.networknt.schema.SpecVersion.VersionFlag; -import static com.networknt.schema.SpecVersion.VersionFlag.V201909; -import static com.networknt.schema.SpecVersion.VersionFlag.V6; -import static com.networknt.schema.SpecVersion.VersionFlag.V7; -import com.networknt.schema.SpecVersionDetector; -import com.networknt.schema.ValidationMessage; import edu.kit.datamanager.clients.SimpleServiceClient; import edu.kit.datamanager.metastore2.exception.JsonValidationException; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; + import java.io.InputStream; import java.net.URI; import java.util.Objects; import java.util.Optional; import java.util.Set; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; /** * Utility class for handling json documents diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 8231124a..520b4180 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -20,28 +20,15 @@ import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; -import edu.kit.datamanager.exceptions.AccessForbiddenException; -import edu.kit.datamanager.exceptions.BadArgumentException; -import edu.kit.datamanager.exceptions.CustomInternalServerError; -import edu.kit.datamanager.exceptions.ResourceNotFoundException; -import edu.kit.datamanager.exceptions.UnprocessableEntityException; +import edu.kit.datamanager.exceptions.*; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.IDataRecordDao; -import edu.kit.datamanager.metastore2.domain.DataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; -import edu.kit.datamanager.metastore2.domain.SchemaRecord; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; -import edu.kit.datamanager.repo.domain.ContentInformation; -import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; -import edu.kit.datamanager.repo.domain.RelatedIdentifier; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Scheme; -import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.domain.acl.AclEntry; import edu.kit.datamanager.repo.service.IContentInformationService; import edu.kit.datamanager.repo.util.ContentDataUtils; @@ -49,26 +36,6 @@ import edu.kit.datamanager.util.AuthenticationHelper; import edu.kit.datamanager.util.ControllerUtils; import io.swagger.v3.core.util.Json; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.UnaryOperator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -84,6 +51,22 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.UriComponentsBuilder; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + /** * Utility class for handling json documents */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 9ca7f462..cd2f4dcd 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -26,36 +26,32 @@ import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; -import edu.kit.datamanager.metastore2.domain.DataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; -import edu.kit.datamanager.metastore2.domain.SchemaRecord; -import edu.kit.datamanager.metastore2.domain.Url2Path; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; -import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeAcl; -import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeEntry; import edu.kit.datamanager.metastore2.validation.IValidator; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImpl; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; -import edu.kit.datamanager.repo.domain.ContentInformation; -import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; -import edu.kit.datamanager.repo.domain.Description; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.service.IContentInformationService; import edu.kit.datamanager.repo.util.ContentDataUtils; import edu.kit.datamanager.repo.util.DataResourceUtils; import edu.kit.datamanager.util.ControllerUtils; import io.swagger.v3.core.util.Json; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; + +import java.io.*; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -64,25 +60,13 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.StringTokenizer; +import java.util.*; import java.util.function.BiFunction; import java.util.function.UnaryOperator; import java.util.stream.Stream; -import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.server.ResponseStatusException; + +import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeAcl; +import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeEntry; /** * Utility class for handling json documents diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java b/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java index 467f8f38..b45ce0eb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/SchemaUtils.java @@ -16,15 +16,6 @@ package edu.kit.datamanager.metastore2.util; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -32,6 +23,16 @@ import org.w3c.dom.NamedNodeMap; import org.xml.sax.SAXException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Utility class for (XML) schema documents. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java index 6242e6bb..632b212b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java @@ -16,6 +16,7 @@ package edu.kit.datamanager.metastore2.validation; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; + import java.io.File; import java.io.InputStream; diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java index e04db575..ebd9cdf3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidator.java @@ -9,15 +9,16 @@ import edu.kit.datamanager.metastore2.exception.JsonValidationException; import edu.kit.datamanager.metastore2.util.JsonUtils; import edu.kit.datamanager.metastore2.validation.IValidator; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + /** * Class for validating JSON files. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java index 1d819e2c..57867a86 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/impl/XmlValidator.java @@ -7,15 +7,15 @@ import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.validation.IValidator; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; +import org.apache.xerces.impl.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -24,16 +24,9 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import org.apache.xerces.impl.Constants; -import org.apache.xerces.impl.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.helpers.DefaultHandler; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; /** * Class for validating XML files. diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java index ba832f14..b2fb319a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java @@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletResponse; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java index d794695c..fbfc2c63 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java @@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletResponse; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java index 78ec9eee..07ac11cf 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java @@ -26,9 +26,6 @@ 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 java.net.URISyntaxException; -import java.time.Instant; -import java.util.List; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springdoc.core.converters.models.PageableAsQueryParam; @@ -38,18 +35,16 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import java.net.URISyntaxException; +import java.time.Instant; +import java.util.List; + /** * Interface for metadata documents controller. * @deprecated version 3 please use IMetadataController_v2 instead diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index 2f9458fc..4d028c4c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -27,9 +27,6 @@ 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 java.net.URISyntaxException; -import java.time.Instant; -import java.util.List; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springdoc.core.converters.models.PageableAsQueryParam; @@ -39,17 +36,16 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import java.net.URISyntaxException; +import java.time.Instant; +import java.util.List; + /** * Interface for metadata documents controller. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java index 01be4432..7339bd0c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java @@ -27,8 +27,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.time.Instant; -import java.util.List; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.boot.actuate.info.InfoContributor; import org.springframework.data.domain.Pageable; @@ -37,17 +35,15 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.messaging.handler.annotation.Header; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import java.time.Instant; +import java.util.List; + /** * Interface for schema document controller. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java index 50c5acd8..de2533a8 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java @@ -29,8 +29,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.time.Instant; -import java.util.List; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.boot.actuate.info.InfoContributor; import org.springframework.data.domain.Pageable; @@ -39,17 +37,15 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.messaging.handler.annotation.Header; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import java.time.Instant; +import java.util.List; + /** * Interface for schema document controller. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java index 15b31364..354a1e49 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java @@ -14,8 +14,6 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletResponse; -import java.util.Arrays; -import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +28,9 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.util.UriComponentsBuilder; +import java.util.Arrays; +import java.util.List; + /** * Controller used by web frontends. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImpl.java index 4c9d068b..06a787ec 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImpl.java @@ -23,12 +23,9 @@ import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.ILandingPageController; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; @@ -37,7 +34,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.request.WebRequest; -import org.springframework.web.servlet.ModelAndView; + +import java.util.ArrayList; +import java.util.List; /** * Controller for metadata documents. diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java index b4ebc9bc..249bd67d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java @@ -18,18 +18,12 @@ import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import edu.kit.datamanager.metastore2.domain.SchemaRecord; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; -import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; -import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.ILandingPageControllerV2; import edu.kit.datamanager.repo.domain.DataResource; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; @@ -39,6 +33,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.request.WebRequest; +import java.util.ArrayList; +import java.util.List; + /** * Controller for metadata documents. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index e19b508d..10e98d1f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -26,21 +26,13 @@ import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; -import edu.kit.datamanager.metastore2.domain.AclRecord; -import edu.kit.datamanager.metastore2.domain.LinkedMetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.IMetadataController; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; -import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; -import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; +import edu.kit.datamanager.repo.dao.spec.dataresource.*; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.service.IMessagingService; @@ -52,18 +44,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.UnaryOperator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -88,6 +68,15 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.time.Instant; +import java.util.*; +import java.util.function.UnaryOperator; + /** * Controller for metadata documents. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index ca920e6c..47cb5ae7 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -16,8 +16,6 @@ package edu.kit.datamanager.metastore2.web.impl; import com.fasterxml.jackson.core.JsonParseException; -import static edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE.INTERNAL; - import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; @@ -53,18 +51,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.UnaryOperator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -89,6 +75,17 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.time.Instant; +import java.util.*; +import java.util.function.UnaryOperator; + +import static edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE.INTERNAL; + /** * Controller for metadata documents. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataEditorController.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataEditorController.java index 7f9f3301..418e706f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataEditorController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataEditorController.java @@ -6,16 +6,16 @@ package edu.kit.datamanager.metastore2.web.impl; import com.fasterxml.jackson.databind.ObjectMapper; +import edu.kit.datamanager.metastore2.dto.EditorRequestMetadata; import edu.kit.datamanager.metastore2.dto.EditorRequestSchema; import edu.kit.datamanager.metastore2.dto.TabulatorItems; -import edu.kit.datamanager.metastore2.dto.EditorRequestMetadata; +import edu.kit.datamanager.metastore2.web.IMetadataEditorController; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.tags.Tag; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; +import jakarta.servlet.http.HttpServletResponse; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; @@ -26,9 +26,10 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; -import edu.kit.datamanager.metastore2.web.IMetadataEditorController; -import jakarta.servlet.http.HttpServletResponse; -import org.json.simple.parser.ParseException; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; /** * Controller for the metadata editor web frontend. diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java index 6f91ecd7..dd79510f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java @@ -33,11 +33,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; /** * Controller for search entities. diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java index e484e097..c5f8d184 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java @@ -33,11 +33,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; /** * Controller for search entities. diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index 9293c641..0bc40c22 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -25,11 +25,7 @@ import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.ISchemaRegistryController; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; -import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.TitleSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.*; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.util.AuthenticationHelper; @@ -38,18 +34,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.net.URI; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.UnaryOperator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.info.Info; @@ -73,6 +57,19 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; + /** * Controller for schema documents. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 7bdf3f50..101d86e8 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -15,7 +15,6 @@ */ package edu.kit.datamanager.metastore2.web.impl; -import static co.elastic.clients.elasticsearch._types.mapping.PropertyBuilders.object; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; @@ -27,11 +26,13 @@ import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; +import edu.kit.datamanager.metastore2.web.ISchemaRegistryControllerV2; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; +import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.util.AuthenticationHelper; @@ -40,17 +41,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.net.URI; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.UnaryOperator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.info.Info; @@ -72,9 +62,19 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; -import edu.kit.datamanager.metastore2.web.ISchemaRegistryControllerV2; -import edu.kit.datamanager.repo.domain.ContentInformation; + +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; import java.util.logging.Level; /** diff --git a/src/main/java/org/openarchives/oai/_2/AboutType.java b/src/main/java/org/openarchives/oai/_2/AboutType.java index 849801d6..a49e20be 100644 --- a/src/main/java/org/openarchives/oai/_2/AboutType.java +++ b/src/main/java/org/openarchives/oai/_2/AboutType.java @@ -12,10 +12,6 @@ import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAnyElement; import jakarta.xml.bind.annotation.XmlType; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlAnyElement; -import jakarta.xml.bind.annotation.XmlType; /** diff --git a/src/main/java/org/openarchives/oai/_2/HeaderType.java b/src/main/java/org/openarchives/oai/_2/HeaderType.java index a09c0e11..8417dcce 100644 --- a/src/main/java/org/openarchives/oai/_2/HeaderType.java +++ b/src/main/java/org/openarchives/oai/_2/HeaderType.java @@ -8,12 +8,8 @@ package org.openarchives.oai._2; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlAttribute; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlSchemaType; -import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.*; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/openarchives/oai/_2/IdentifyType.java b/src/main/java/org/openarchives/oai/_2/IdentifyType.java index 4f447d22..a8f5162f 100644 --- a/src/main/java/org/openarchives/oai/_2/IdentifyType.java +++ b/src/main/java/org/openarchives/oai/_2/IdentifyType.java @@ -8,11 +8,8 @@ package org.openarchives.oai._2; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlSchemaType; -import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.*; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/openarchives/oai/_2/ListIdentifiersType.java b/src/main/java/org/openarchives/oai/_2/ListIdentifiersType.java index 28c382a1..ac49d12f 100644 --- a/src/main/java/org/openarchives/oai/_2/ListIdentifiersType.java +++ b/src/main/java/org/openarchives/oai/_2/ListIdentifiersType.java @@ -12,6 +12,7 @@ import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlType; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/openarchives/oai/_2/ListMetadataFormatsType.java b/src/main/java/org/openarchives/oai/_2/ListMetadataFormatsType.java index 4914b1d3..58cb580d 100644 --- a/src/main/java/org/openarchives/oai/_2/ListMetadataFormatsType.java +++ b/src/main/java/org/openarchives/oai/_2/ListMetadataFormatsType.java @@ -10,6 +10,7 @@ import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlType; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/openarchives/oai/_2/ListRecordsType.java b/src/main/java/org/openarchives/oai/_2/ListRecordsType.java index ae271662..8e78a4fb 100644 --- a/src/main/java/org/openarchives/oai/_2/ListRecordsType.java +++ b/src/main/java/org/openarchives/oai/_2/ListRecordsType.java @@ -12,6 +12,7 @@ import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlType; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/openarchives/oai/_2/ListSetsType.java b/src/main/java/org/openarchives/oai/_2/ListSetsType.java index 2f6cbecb..6580afae 100644 --- a/src/main/java/org/openarchives/oai/_2/ListSetsType.java +++ b/src/main/java/org/openarchives/oai/_2/ListSetsType.java @@ -12,6 +12,7 @@ import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlType; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/openarchives/oai/_2/MetadataFormatType.java b/src/main/java/org/openarchives/oai/_2/MetadataFormatType.java index 3e1e5fc4..fa30fc3e 100644 --- a/src/main/java/org/openarchives/oai/_2/MetadataFormatType.java +++ b/src/main/java/org/openarchives/oai/_2/MetadataFormatType.java @@ -8,11 +8,7 @@ package org.openarchives.oai._2; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlSchemaType; -import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.*; /** diff --git a/src/main/java/org/openarchives/oai/_2/OAIPMHerrorType.java b/src/main/java/org/openarchives/oai/_2/OAIPMHerrorType.java index c33d67de..1aed72b9 100644 --- a/src/main/java/org/openarchives/oai/_2/OAIPMHerrorType.java +++ b/src/main/java/org/openarchives/oai/_2/OAIPMHerrorType.java @@ -8,16 +8,7 @@ package org.openarchives.oai._2; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlAttribute; -import jakarta.xml.bind.annotation.XmlType; -import jakarta.xml.bind.annotation.XmlValue; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlAttribute; -import jakarta.xml.bind.annotation.XmlType; -import jakarta.xml.bind.annotation.XmlValue; +import jakarta.xml.bind.annotation.*; /** diff --git a/src/main/java/org/openarchives/oai/_2/OAIPMHtype.java b/src/main/java/org/openarchives/oai/_2/OAIPMHtype.java index 3b6a1041..a148fa93 100644 --- a/src/main/java/org/openarchives/oai/_2/OAIPMHtype.java +++ b/src/main/java/org/openarchives/oai/_2/OAIPMHtype.java @@ -6,16 +6,12 @@ // package org.openarchives.oai._2; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlRootElement; -import jakarta.xml.bind.annotation.XmlSchemaType; -import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.*; + +import javax.xml.datatype.XMLGregorianCalendar; import java.time.Instant; import java.util.ArrayList; import java.util.List; -import javax.xml.datatype.XMLGregorianCalendar; /** * <p> diff --git a/src/main/java/org/openarchives/oai/_2/ObjectFactory.java b/src/main/java/org/openarchives/oai/_2/ObjectFactory.java index f9fa3874..405a00b9 100644 --- a/src/main/java/org/openarchives/oai/_2/ObjectFactory.java +++ b/src/main/java/org/openarchives/oai/_2/ObjectFactory.java @@ -11,6 +11,7 @@ import jakarta.xml.bind.JAXBElement; import jakarta.xml.bind.annotation.XmlElementDecl; import jakarta.xml.bind.annotation.XmlRegistry; + import javax.xml.namespace.QName; diff --git a/src/main/java/org/openarchives/oai/_2/RecordType.java b/src/main/java/org/openarchives/oai/_2/RecordType.java index 9c9b4bea..d28a9b90 100644 --- a/src/main/java/org/openarchives/oai/_2/RecordType.java +++ b/src/main/java/org/openarchives/oai/_2/RecordType.java @@ -12,6 +12,7 @@ import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlType; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/openarchives/oai/_2/RequestType.java b/src/main/java/org/openarchives/oai/_2/RequestType.java index 5dfda671..0ad8f4fa 100644 --- a/src/main/java/org/openarchives/oai/_2/RequestType.java +++ b/src/main/java/org/openarchives/oai/_2/RequestType.java @@ -8,12 +8,7 @@ package org.openarchives.oai._2; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlAttribute; -import jakarta.xml.bind.annotation.XmlSchemaType; -import jakarta.xml.bind.annotation.XmlType; -import jakarta.xml.bind.annotation.XmlValue; +import jakarta.xml.bind.annotation.*; /** diff --git a/src/main/java/org/openarchives/oai/_2/ResumptionTokenType.java b/src/main/java/org/openarchives/oai/_2/ResumptionTokenType.java index a793ba34..f5faeb4c 100644 --- a/src/main/java/org/openarchives/oai/_2/ResumptionTokenType.java +++ b/src/main/java/org/openarchives/oai/_2/ResumptionTokenType.java @@ -8,14 +8,10 @@ package org.openarchives.oai._2; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlAttribute; -import jakarta.xml.bind.annotation.XmlSchemaType; -import jakarta.xml.bind.annotation.XmlType; -import jakarta.xml.bind.annotation.XmlValue; -import java.math.BigInteger; +import jakarta.xml.bind.annotation.*; + import javax.xml.datatype.XMLGregorianCalendar; +import java.math.BigInteger; /** diff --git a/src/main/java/org/openarchives/oai/_2/SetType.java b/src/main/java/org/openarchives/oai/_2/SetType.java index e27f8068..a2a6aaab 100644 --- a/src/main/java/org/openarchives/oai/_2/SetType.java +++ b/src/main/java/org/openarchives/oai/_2/SetType.java @@ -12,12 +12,9 @@ import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlType; + import java.util.ArrayList; import java.util.List; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlType; /** diff --git a/src/main/java/org/openarchives/oai/_2/StatusType.java b/src/main/java/org/openarchives/oai/_2/StatusType.java index 5eb541d2..5d5202cb 100644 --- a/src/main/java/org/openarchives/oai/_2/StatusType.java +++ b/src/main/java/org/openarchives/oai/_2/StatusType.java @@ -11,9 +11,6 @@ import jakarta.xml.bind.annotation.XmlEnum; import jakarta.xml.bind.annotation.XmlEnumValue; import jakarta.xml.bind.annotation.XmlType; -import jakarta.xml.bind.annotation.XmlEnum; -import jakarta.xml.bind.annotation.XmlEnumValue; -import jakarta.xml.bind.annotation.XmlType; /** diff --git a/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-brands-400.svg b/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-brands-400.svg index 2c8659c1..52bd997a 100644 --- a/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-brands-400.svg +++ b/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-brands-400.svg @@ -4,7 +4,7 @@ Font Awesome Free 5.14.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> +<svg xmlns="http://www.w3.org/2000/svg" version="1.1"> <metadata> Created by FontForge 20200314 at Wed Jul 15 11:59:41 2020 By Robert Madole diff --git a/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-regular-400.svg b/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-regular-400.svg index 7947ca8f..13664ccf 100644 --- a/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-regular-400.svg +++ b/src/main/resources/static/editor/dependencies/fontawesome/webfonts/fa-regular-400.svg @@ -4,7 +4,7 @@ Font Awesome Free 5.14.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> +<svg xmlns="http://www.w3.org/2000/svg" version="1.1"> <metadata> Created by FontForge 20200314 at Wed Jul 15 11:59:40 2020 By Robert Madole diff --git a/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java b/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java index 4628a5ff..789d62f7 100644 --- a/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java +++ b/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java @@ -9,11 +9,8 @@ import edu.kit.datamanager.exceptions.MessageValidationException; import edu.kit.datamanager.metastore2.domain.MetadataRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.*; + import static org.junit.Assert.*; /** diff --git a/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java b/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java index 58fd93d4..56de6a64 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java @@ -6,19 +6,7 @@ package edu.kit.datamanager.metastore2.dao; import edu.kit.datamanager.metastore2.domain.DataRecord; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.Optional; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.*; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.domain.EntityScan; @@ -39,6 +27,16 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.ServletTestExecutionListener; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; + /** */ @RunWith(SpringRunner.class) diff --git a/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java b/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java index f7103b9e..c0620569 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java @@ -6,13 +6,7 @@ import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.SchemaRecord; -import java.util.List; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.*; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.domain.EntityScan; @@ -31,6 +25,10 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.ServletTestExecutionListener; +import java.util.List; + +import static org.junit.Assert.*; + /** * * @author hartmann-v diff --git a/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java b/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java index 6dc4b6e7..b5eee2fb 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java @@ -7,21 +7,7 @@ import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.Url2Path; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.Optional; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.*; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.domain.EntityScan; @@ -40,6 +26,18 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.ServletTestExecutionListener; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; + /** */ @RunWith(SpringRunner.class) diff --git a/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadataTest.java b/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadataTest.java index a935091a..698e7a86 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadataTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestMetadataTest.java @@ -16,14 +16,12 @@ package edu.kit.datamanager.metastore2.dto; import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import org.json.simple.JSONObject; +import org.junit.*; + import java.util.ArrayList; import java.util.List; -import org.json.simple.JSONObject; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; + import static org.junit.Assert.*; /** diff --git a/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchemaTest.java b/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchemaTest.java index 2a8e600c..8476e530 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchemaTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dto/EditorRequestSchemaTest.java @@ -16,14 +16,12 @@ package edu.kit.datamanager.metastore2.dto; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import org.json.simple.JSONObject; +import org.junit.*; + import java.util.ArrayList; import java.util.List; -import org.json.simple.JSONObject; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; + import static org.junit.Assert.*; /** diff --git a/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorFormatterParamTest.java b/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorFormatterParamTest.java index d60bc9f1..69f21766 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorFormatterParamTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorFormatterParamTest.java @@ -15,12 +15,9 @@ */ package edu.kit.datamanager.metastore2.dto; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.*; + +import static org.junit.Assert.assertEquals; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorItemsTest.java b/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorItemsTest.java index 66ee56a7..39aea097 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorItemsTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dto/TabulatorItemsTest.java @@ -15,12 +15,9 @@ */ package edu.kit.datamanager.metastore2.dto; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.*; + +import static org.junit.Assert.assertEquals; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/ActuatorTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/ActuatorTest.java index c2ae9b3e..83149857 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/ActuatorTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/ActuatorTest.java @@ -10,22 +10,10 @@ import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Comparator; -import java.util.stream.Stream; import org.hamcrest.Matchers; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; import org.hamcrest.core.IsNot; import org.javers.core.Javers; import org.junit.Before; @@ -39,9 +27,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -52,13 +38,27 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.ServletTestExecutionListener; import org.springframework.test.web.servlet.MockMvc; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.stream.Stream; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** * * @author Torridity diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java index 96a53045..56efce4a 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java @@ -17,8 +17,6 @@ import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.acl.AclEntry; import edu.kit.datamanager.util.AuthenticationHelper; -import java.util.HashSet; -import java.util.Set; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -30,8 +28,12 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import org.springframework.test.web.servlet.request.RequestPostProcessor; + +import java.util.HashSet; +import java.util.Set; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/FrontendControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/FrontendControllerTest.java index 81d70d80..678f1e0d 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/FrontendControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/FrontendControllerTest.java @@ -17,17 +17,8 @@ import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; import org.hamcrest.Matchers; +import org.hamcrest.core.IsNot; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -40,7 +31,6 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @@ -54,15 +44,27 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import org.hamcrest.core.IsNot; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java index 4f57c119..27f2d017 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java @@ -11,32 +11,12 @@ import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.SchemaRecord; -import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtilTest; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.domain.Agent; -import edu.kit.datamanager.repo.domain.ContentInformation; -import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; -import edu.kit.datamanager.repo.domain.Description; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.Calendar; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; -import java.util.stream.Stream; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -50,9 +30,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -65,15 +43,27 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.*; +import java.util.stream.Stream; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java index 42958d8a..9b8ab78f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java @@ -16,16 +16,6 @@ import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -37,9 +27,7 @@ import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -53,12 +41,26 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index 8e99f448..cd8e2e1a 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -20,18 +20,6 @@ import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; import org.hamcrest.Matchers; import org.javers.core.Javers; import org.junit.Assert; @@ -46,10 +34,10 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @@ -63,20 +51,28 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import org.springframework.test.web.servlet.request.RequestPostProcessor; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.*; +import java.util.stream.Stream; + import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.*; -import java.util.Locale; -import org.springframework.http.MediaType; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 4410916a..2308c8ed 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -16,22 +16,12 @@ import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; import org.hamcrest.Matchers; import org.javers.core.Javers; import org.junit.Assert; @@ -46,10 +36,10 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @@ -63,27 +53,28 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import org.springframework.test.web.servlet.request.RequestPostProcessor; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.*; +import java.util.stream.Stream; + import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.*; -import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; -import edu.kit.datamanager.repo.domain.ContentInformation; -import edu.kit.datamanager.repo.domain.DataResource; -import edu.kit.datamanager.repo.domain.RelatedIdentifier; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Scheme; -import java.util.Locale; -import java.util.UUID; -import org.springframework.http.MediaType; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java index 66aa018d..489b4bdb 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java @@ -22,17 +22,6 @@ import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; import org.hamcrest.Matchers; import org.javers.core.Javers; import org.junit.Assert; @@ -51,9 +40,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -66,16 +53,31 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import org.springframework.test.web.servlet.request.RequestPostProcessor; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** * * @author Torridity diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java index ffa63a48..30bf8280 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java @@ -14,28 +14,10 @@ import edu.kit.datamanager.metastore2.domain.MetadataRecord; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; -import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.XML_DOCUMENT_V1; -import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.XML_DOCUMENT_V2; -import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.XML_DOCUMENT_V3; -import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.XML_SCHEMA_V1; -import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.XML_SCHEMA_V2; -import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.XML_SCHEMA_V3; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; -import java.util.stream.Stream; import org.hamcrest.Matchers; import org.javers.core.Javers; import org.junit.Assert; @@ -53,9 +35,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -68,15 +48,32 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Stream; + +import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import org.springframework.test.web.servlet.request.RequestPostProcessor; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithWrongRegistryTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithWrongRegistryTest.java index 3d67a97a..1ec2eb99 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithWrongRegistryTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithWrongRegistryTest.java @@ -27,6 +27,7 @@ import org.springframework.test.context.web.ServletTestExecutionListener; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithoutRegistryTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithoutRegistryTest.java index ee271e1f..230b8cff 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithoutRegistryTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerWithoutRegistryTest.java @@ -19,16 +19,6 @@ import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; import org.javers.core.Javers; import org.junit.Before; import org.junit.Rule; @@ -40,9 +30,7 @@ import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -54,12 +42,26 @@ import org.springframework.test.context.web.ServletTestExecutionListener; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataEditorControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataEditorControllerTest.java index f3231312..2c003ee3 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataEditorControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataEditorControllerTest.java @@ -17,7 +17,6 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; import org.springframework.security.web.FilterChainProxy; import org.springframework.test.annotation.DirtiesContext; @@ -31,11 +30,13 @@ import org.springframework.test.context.web.ServletTestExecutionListener; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; /** * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index 986dd410..a3deb4e2 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -20,32 +20,11 @@ import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.domain.Agent; -import edu.kit.datamanager.repo.domain.ContentInformation; -import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; -import edu.kit.datamanager.repo.domain.Description; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.domain.acl.AclEntry; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.Calendar; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.stream.Stream; import org.hamcrest.Matchers; import org.junit.Assert; -import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -62,9 +41,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -78,18 +55,29 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import org.springframework.test.web.servlet.request.RequestPostProcessor; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.*; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + /** * * @author Torridity diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 4f1c8aa4..cd126893 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -23,36 +23,12 @@ import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.domain.Agent; -import edu.kit.datamanager.repo.domain.ContentInformation; -import edu.kit.datamanager.repo.domain.Contributor; -import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; -import edu.kit.datamanager.repo.domain.Description; -import edu.kit.datamanager.repo.domain.RelatedIdentifier; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Scheme; -import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.domain.acl.AclEntry; import edu.kit.datamanager.util.AuthenticationHelper; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.Calendar; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.stream.Stream; import org.hamcrest.Matchers; import org.junit.Assert; -import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -70,9 +46,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; @@ -87,18 +61,29 @@ import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import org.springframework.test.web.servlet.request.RequestPostProcessor; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.*; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + /** * * @author Torridity From ceab5e34028c73b136e11068320f7b605b8c983b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 23 Aug 2024 12:01:50 +0200 Subject: [PATCH 056/181] Code cleanup --- .../dao/ISchemaSynchronizationEventDao.java | 2 +- .../metastore2/domain/ResourceIdentifier.java | 26 +++---- .../metastore2/domain/SchemaRecord.java | 2 +- .../service/MetastoreOAIPMHRepository.java | 18 ++--- .../runner/ElasticIndexerRunner.java | 2 +- .../service/SchemaSynchronizationService.java | 3 +- .../metastore2/util/ActuatorUtil.java | 6 +- .../util/DataResourceRecordUtil.java | 28 +++---- .../metastore2/util/DownloadUtil.java | 10 +-- .../metastore2/util/MetadataRecordUtil.java | 24 +++--- .../util/MetadataSchemaRecordUtil.java | 22 ++---- .../metastore2/web/IFrontendController.java | 22 +++--- .../web/ILandingPageController.java | 6 +- .../web/ILandingPageControllerV2.java | 6 +- .../metastore2/web/IMetadataController.java | 40 +++++----- .../metastore2/web/IMetadataControllerV2.java | 48 ++++++------ .../web/IMetadataEditorController.java | 12 +-- .../web/ISchemaRegistryController.java | 40 +++++----- .../web/ISchemaRegistryControllerV2.java | 48 ++++++------ .../web/impl/FrontendControllerImpl.java | 4 +- .../web/impl/MetadataControllerImpl.java | 2 +- .../web/impl/MetadataControllerImplV2.java | 4 +- .../web/impl/MetadataSearchController.java | 2 +- .../web/impl/MetadataSearchControllerV2.java | 2 +- .../impl/SchemaRegistryControllerImpl.java | 4 +- .../impl/SchemaRegistryControllerImplV2.java | 4 +- .../MetadataResourceMessageTest.java | 10 +-- .../metastore2/dao/IDataRecordDaoTest.java | 8 +- .../metastore2/dao/ISchemaRecordDaoTest.java | 18 ++--- .../metastore2/dao/IUrl2PathDaoTest.java | 2 +- .../RestDocumentation4WebpageTest.java | 4 +- ...istryControllerDocumentation4JsonTest.java | 6 +- ...maRegistryControllerDocumentationTest.java | 6 +- .../metastore2/domain/AclRecordTest.java | 6 +- .../domain/MetadataSchemaRecordTest.java | 2 +- .../domain/ResourceIdentifierTest.java | 4 +- .../oaipmh/web/OaiPmhControllerTest.java | 14 ++-- .../runner/ElasticIndexerRunnerTest.java | 4 +- .../JsonSchemaRegistryControllerTest.java | 12 +-- .../test/MetadataControllerTest.java | 58 +++++++------- ...tAccessWithAuthenticationEnabled4Json.java | 2 +- .../test/MetadataControllerTestV2.java | 68 ++++++++--------- ...ntrollerTestWithAuthenticationEnabled.java | 16 ++-- ...rollerTestWithAuthenticationEnabledV2.java | 20 ++--- ...trollerTestWithInternalSchemaRegistry.java | 18 ++--- .../test/SchemaRegistryControllerTest.java | 38 +++++----- .../test/SchemaRegistryControllerTestV2.java | 40 +++++----- .../metastore2/util/DownloadUtilTest.java | 68 ++++++++--------- .../metastore2/util/JsonUtilsFailingTest.java | 4 +- .../metastore2/util/JsonUtilsTest.java | 38 +++++----- .../util/MetadataRecordUtilTest.java | 36 ++++----- .../util/MetadataSchemaRecordUtilTest.java | 40 +++++----- .../validation/impl/JsonValidatorTest.java | 8 +- .../openarchives/oai/_2/HeaderTypeTest.java | 4 +- .../openarchives/oai/_2/IdentifyTypeTest.java | 10 +-- .../oai/_2/MetadataFormatTypeTest.java | 6 +- .../oai/_2/OAIPMHerrorTypeTest.java | 2 +- .../test-config/application-test.properties | 76 +++++++++---------- 58 files changed, 508 insertions(+), 527 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java index 6f3254f4..e58b5459 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaSynchronizationEventDao.java @@ -27,5 +27,5 @@ */ public interface ISchemaSynchronizationEventDao extends JpaRepository<SchemaSynchronizationEvent, Long> { - public Optional<SchemaSynchronizationEvent> findBySourceName(String sourceName); + Optional<SchemaSynchronizationEvent> findBySourceName(String sourceName); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java b/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java index 9e64b83d..168e983e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java @@ -29,18 +29,17 @@ public class ResourceIdentifier implements Serializable { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(ResourceIdentifier.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); - sb.append("resourceIdentifier"); - sb.append('='); - sb.append(((this.getIdentifier() == null) ? "<null>" : this.getIdentifier())); - sb.append(','); - sb.append("resourceIdentifierType"); - sb.append('='); - sb.append(((this.getIdentifierType() == null) ? "<null>" : this.getIdentifierType())); - sb.append(']'); - - return sb.toString(); + String sb = ResourceIdentifier.class.getName() + '@' + Integer.toHexString(System.identityHashCode(this)) + '[' + + "resourceIdentifier" + + '=' + + ((this.getIdentifier() == null) ? "<null>" : this.getIdentifier()) + + ',' + + "resourceIdentifierType" + + '=' + + ((this.getIdentifierType() == null) ? "<null>" : this.getIdentifierType()) + + ']'; + + return sb; } @Override @@ -56,8 +55,7 @@ public boolean equals(Object other) { boolean returnValue = true; if (other != this) { returnValue = false; - if (other instanceof ResourceIdentifier) { - ResourceIdentifier rhs = ((ResourceIdentifier) other); + if (other instanceof ResourceIdentifier rhs) { // check for id if ((((this.getId() == null) && (rhs.getId() == null)) || ((this.getId() != null) && this.getId().equals(rhs.getId()))) diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index 8c5145c3..668eecee 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -54,7 +54,7 @@ public class SchemaRecord implements Serializable { public String getSchemaIdWithoutVersion() { String pureSchemaId = null; if (schemaId != null) { - String split[] = schemaId.split("/"); + String[] split = schemaId.split("/"); pureSchemaId = schemaId.split("/")[0]; } return pureSchemaId; diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java index be1dd819..141c224b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java @@ -97,10 +97,10 @@ public class MetastoreOAIPMHRepository extends AbstractOAIPMHRepository { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(MetastoreOAIPMHRepository.class); - private MetadataFormatType DC_SCHEMA; - private MetadataFormatType DATACITE_SCHEMA; + private final MetadataFormatType DC_SCHEMA; + private final MetadataFormatType DATACITE_SCHEMA; - private OaiPmhConfiguration pluginConfiguration; + private final OaiPmhConfiguration pluginConfiguration; @Autowired private IDataRecordDao dataRecordDao; @@ -148,12 +148,12 @@ public DeletedRecordType getDeletedRecordSupport() { @Override public List<String> getAdminEmail() { - return Arrays.asList(pluginConfiguration.getAdminEmail()); + return Collections.singletonList(pluginConfiguration.getAdminEmail()); } @Override public String getEarliestDatestamp() { - return getDateFormat().format(new Date(0l)); + return getDateFormat().format(new Date(0L)); } @Override @@ -237,12 +237,12 @@ public void listIdentifiers(OAIPMHBuilder builder) { LOGGER.trace("Adding {} records to result.", results.size()); results.stream().forEach(result -> { //TODO get proper date - Date changeDate = new Date(0l); + Date changeDate = new Date(0L); if (result.getLastUpdate() != null) { changeDate = Date.from(result.getLastUpdate()); } - builder.addRecord(result.getMetadataId(), changeDate, Arrays.asList("default")); + builder.addRecord(result.getMetadataId(), changeDate, List.of("default")); }); } @@ -376,11 +376,11 @@ private void addRecordEntry(DataRecord result, OAIPMHBuilder builder) { Document doc = getMetadataDocument(result, builder.getMetadataPrefix()); if (doc != null) { LOGGER.trace("Adding record using obtained metadata document."); - Date resourceDate = new Date(0l); + Date resourceDate = new Date(0L); if (result.getLastUpdate() != null) { resourceDate = Date.from(result.getLastUpdate()); } - builder.addRecord(result.getMetadataId(), resourceDate, Arrays.asList("default"), doc.getDocumentElement()); + builder.addRecord(result.getMetadataId(), resourceDate, List.of("default"), doc.getDocumentElement()); } else { LOGGER.error("No metadata document found for prefix {} and object identifier {}. Returning OAI-PMH error CANNOT_DISSEMINATE_FORMAT.", builder.getMetadataPrefix(), result.getId()); builder.addError(OAIPMHerrorcodeType.CANNOT_DISSEMINATE_FORMAT, null); diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index c4a32f80..473eff72 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -164,7 +164,7 @@ public void run(String... args) throws Exception { * @return MetadataRecord of metadata document. */ private MetadataRecord toMetadataRecord(DataRecord dataRecord, String baseUrl) { - String metadataIdWithVersion = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImpl.class).getMetadataDocumentById(dataRecord.getMetadataId(), dataRecord.getVersion(), null, null)).toUri().toString(); + String metadataIdWithVersion = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImpl.class).getMetadataDocumentById(dataRecord.getMetadataId(), dataRecord.getVersion(), null, null)).toUri(); MetadataRecord returnValue = new MetadataRecord(); returnValue.setId(dataRecord.getMetadataId()); returnValue.setSchemaVersion(dataRecord.getSchemaVersion()); diff --git a/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java b/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java index 8257ec46..ccc0f624 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java +++ b/src/main/java/edu/kit/datamanager/metastore2/service/SchemaSynchronizationService.java @@ -39,6 +39,7 @@ import java.io.IOException; import java.time.Instant; import java.util.Arrays; +import java.util.List; import java.util.Optional; /** @@ -106,7 +107,7 @@ public void performSynchronization() { } HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Arrays.asList(MediaType.parseMediaType("application/vnd.datamanager.schema-record+json"))); + headers.setAccept(List.of(MediaType.parseMediaType("application/vnd.datamanager.schema-record+json"))); HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(headers); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java index b33a772a..5e027708 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/ActuatorUtil.java @@ -59,7 +59,7 @@ public static final Map<String, String> testDirectory(URL pathUrl) { Path path = Paths.get(pathUrl.toURI()); properties = determineDetailsForPath(path); } catch (URISyntaxException ex) { - LOG.error("Invalid base path uri of '" + pathUrl.toString() + "'.", ex); + LOG.error("Invalid base path uri of '" + pathUrl + "'.", ex); } return properties; } @@ -107,12 +107,12 @@ private static final Map<String, String> determineDetailsForPath(Path path) { properties.put("Free space", freeSpace); } catch (IOException ioe) { - LOG.error("Failed to check repository folder at '" + path.toString() + "'."); + LOG.error("Failed to check repository folder at '" + path + "'."); } finally { try { Files.deleteIfExists(probe); } catch (IOException ignored) { - LOG.error("Can't delete file '{}'.", probe.toString()); + LOG.error("Can't delete file '{}'.", probe); } } return properties; diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 3defcc95..8dc746de 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -348,7 +348,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur ControllerUtils.checkEtag(eTag, dataResource); LOG.trace("ETag: '{}'", dataResource.getEtag()); if (metadataRecord != null) { - LOG.trace("metadataRecord: '{}'", metadataRecord.toString()); + LOG.trace("metadataRecord: '{}'", metadataRecord); metadataRecord.setVersion(dataResource.getVersion()); metadataRecord.setId(dataResource.getId()); updatedDataResource = metadataRecord; @@ -403,7 +403,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur if (version == null) { version = "0"; } - updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); ContentDataUtils.addFile(applicationProperties, updatedDataResource, document, fileName, null, true, supplier); } @@ -548,7 +548,7 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app } } - Long recordVersion = 1l; + Long recordVersion = 1L; if (dataResource.getVersion() != null) { recordVersion = Long.parseLong(dataResource.getVersion()); } @@ -574,7 +574,7 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app metadataRecord.setSchemaVersion(Long.parseLong(matcher.group(1))); } } else { - metadataRecord.setSchemaVersion(1l); + metadataRecord.setSchemaVersion(1L); } LOG.trace("Set schema to '{}'", resourceIdentifier); } @@ -1346,17 +1346,11 @@ public static final void check4validId(DataResource metadataRecord, boolean allo metadataRecord.getAlternateIdentifiers().add(Identifier.factoryIdentifier(id, Identifier.IDENTIFIER_TYPE.OTHER)); } - try { - String value = URLEncoder.encode(metadataRecord.getId(), StandardCharsets.UTF_8.toString()); - if (!value.equals(metadataRecord.getId())) { - String message = "Not a valid ID! Encoded: " + value; - LOG.error(message); - throw new BadArgumentException(message); - } - } catch (UnsupportedEncodingException ex) { - String message = "Error encoding schemaId " + metadataRecord.getId(); + String value = URLEncoder.encode(metadataRecord.getId(), StandardCharsets.UTF_8); + if (!value.equals(metadataRecord.getId())) { + String message = "Not a valid ID! Encoded: " + value; LOG.error(message); - throw new CustomInternalServerError(message); + throw new BadArgumentException(message); } } @@ -1683,7 +1677,7 @@ private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataRes SchemaRecord schemaRecord = null; RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataResource); String schemaId = schemaIdentifier.getValue(); - LOG.trace("getSchemaRecordFromDataResource: related identifier: '{}'", schemaIdentifier.toString()); + LOG.trace("getSchemaRecordFromDataResource: related identifier: '{}'", schemaIdentifier); LOG.trace("getSchemaRecordFromDataResource: '{}'", schemaId); switch (schemaIdentifier.getIdentifierType()) { case URL: @@ -1830,7 +1824,7 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app LOG.trace("Updating schema document (and increment version)..."); String version = dataResource.getVersion(); if (version != null) { - dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); } ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); SchemaRecord schemaRecord = createSchemaRecord(dataResource, contentInformation); @@ -1988,7 +1982,7 @@ public static Page<DataResource> queryDataResources(Specification spec, Pageable records = spec != null ? dataResourceDao.findAll(spec, pgbl) : dataResourceDao.findAll(pgbl); if (LOG.isTraceEnabled()) { if (spec != null) { - LOG.trace("Query data resources with spec '{}'", spec.toString()); + LOG.trace("Query data resources with spec '{}'", spec); } else { LOG.trace("Query all data resources..."); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java index 60c8155d..2476d8eb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DownloadUtil.java @@ -92,8 +92,8 @@ public static Optional<Path> downloadResource(URI resourceURL) { } } } catch (Throwable tw) { - LOGGER.error("Error reading URI '" + resourceURL.toString() + "'", tw); - throw new CustomInternalServerError("Error downloading resource from '" + resourceURL.toString() + "'!"); + LOGGER.error("Error reading URI '" + resourceURL + "'", tw); + throw new CustomInternalServerError("Error downloading resource from '" + resourceURL + "'!"); } downloadedFile = fixFileExtension(downloadedFile); @@ -114,13 +114,13 @@ public static Path fixFileExtension(Path pathToFile) { String contentOfFile = FileUtils.readFileToString(pathToFile.toFile(), StandardCharsets.UTF_8); String newExtension = guessFileExtension(contentOfFile.getBytes(StandardCharsets.UTF_8)); if ((newExtension != null) && !pathToFile.toString().endsWith(newExtension)) { - renamedFile = Paths.get(pathToFile.toString() + newExtension); + renamedFile = Paths.get(pathToFile + newExtension); FileUtils.moveFile(pathToFile.toFile(), renamedFile.toFile()); returnFile = renamedFile; } } } catch (IOException ex) { - LOGGER.error("Error moving file '{}' to '{}'.", pathToFile.toString(), renamedFile.toString()); + LOGGER.error("Error moving file '{}' to '{}'.", pathToFile, renamedFile); } return returnFile; } @@ -156,7 +156,7 @@ public static void removeFile(Path tempFile) { try { Files.deleteIfExists(tempFile); } catch (IOException ioe) { - throw new CustomInternalServerError("Error removing file '" + tempFile.toString() + "'!"); + throw new CustomInternalServerError("Error removing file '" + tempFile + "'!"); } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 520b4180..a70070db 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -146,7 +146,7 @@ public static MetadataRecord createMetadataRecord(MetastoreConfiguration applica // validate schema document validateMetadataDocument(applicationProperties, metadataRecord, document); // set internal parameters - metadataRecord.setRecordVersion(1l); + metadataRecord.setRecordVersion(1L); long nano3 = System.nanoTime() / 1000000; // create record. @@ -156,18 +156,12 @@ public static MetadataRecord createMetadataRecord(MetastoreConfiguration applica // id will be set to alternate identifier if exists. if (dataResource.getId() != null) { // check for valid identifier without any chars which may be encoded - try { - String originalId = dataResource.getId(); - String value = URLEncoder.encode(originalId, StandardCharsets.UTF_8.toString()); - if (!value.equals(originalId)) { - String message = "Not a valid id! Encoded: " + value; - LOG.error(message); - throw new BadArgumentException(message); - } - } catch (UnsupportedEncodingException ex) { - String message = "Error encoding id " + metadataRecord.getSchemaId(); + String originalId = dataResource.getId(); + String value = URLEncoder.encode(originalId, StandardCharsets.UTF_8); + if (!value.equals(originalId)) { + String message = "Not a valid id! Encoded: " + value; LOG.error(message); - throw new CustomInternalServerError(message); + throw new BadArgumentException(message); } dataResource.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(dataResource.getId())); @@ -282,7 +276,7 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica LOG.trace("Updating schema document (and increment version)..."); String version = dataResource.getVersion(); if (version != null) { - dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); } ContentDataUtils.addFile(applicationProperties, dataResource, document, fileName, null, true, supplier); } @@ -478,7 +472,7 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli } } - Long recordVersion = 1l; + Long recordVersion = 1L; if (dataResource.getVersion() != null) { recordVersion = Long.parseLong(dataResource.getVersion()); } @@ -504,7 +498,7 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli metadataRecord.setSchemaVersion(Long.parseLong(matcher.group(1))); } } else { - metadataRecord.setSchemaVersion(1l); + metadataRecord.setSchemaVersion(1L); } LOG.trace("Set schema to '{}'", resourceIdentifier); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index cd2f4dcd..9a0caa65 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -130,17 +130,11 @@ public static MetadataSchemaRecord createMetadataSchemaRecord(MetastoreConfigura LOG.error(message); throw new BadArgumentException(message); } else { - try { - String value = URLEncoder.encode(metadataRecord.getSchemaId(), StandardCharsets.UTF_8.toString()); - if (!value.equals(metadataRecord.getSchemaId())) { - String message = "Not a valid schema id! Encoded: " + value; - LOG.error(message); - throw new BadArgumentException(message); - } - } catch (UnsupportedEncodingException ex) { - String message = "Error encoding schemaId " + metadataRecord.getSchemaId(); + String value = URLEncoder.encode(metadataRecord.getSchemaId(), StandardCharsets.UTF_8); + if (!value.equals(metadataRecord.getSchemaId())) { + String message = "Not a valid schema id! Encoded: " + value; LOG.error(message); - throw new CustomInternalServerError(message); + throw new BadArgumentException(message); } } // Create schema record @@ -172,7 +166,7 @@ public static MetadataSchemaRecord createMetadataSchemaRecord(MetastoreConfigura } } } - metadataRecord.setSchemaVersion(1l); + metadataRecord.setSchemaVersion(1L); // create record. DataResource dataResource = migrateToDataResource(applicationProperties, metadataRecord); DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); @@ -292,7 +286,7 @@ public static MetadataSchemaRecord updateMetadataSchemaRecord(MetastoreConfigura LOG.trace("Updating schema document (and increment version)..."); String version = dataResource.getVersion(); if (version != null) { - dataResource.setVersion(Long.toString(Long.parseLong(version) + 1l)); + dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); } ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); } else { @@ -587,7 +581,7 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecord(RepoBaseConfigu } } - Long schemaVersion = 1l; + Long schemaVersion = 1L; if (dataResource.getVersion() != null) { schemaVersion = Long.valueOf(dataResource.getVersion()); } @@ -1199,7 +1193,7 @@ public static MetadataSchemaRecord getCurrentSchemaRecord(MetastoreConfiguration } else { msr = new MetadataSchemaRecord(); Optional<Url2Path> url2path = url2PathDao.findByUrl(schema.getIdentifier()); - Long version = 1l; + Long version = 1L; if (url2path.isPresent()) { version = url2path.get().getVersion(); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IFrontendController.java b/src/main/java/edu/kit/datamanager/metastore2/web/IFrontendController.java index 213510ae..7b7fdb90 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IFrontendController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IFrontendController.java @@ -27,7 +27,7 @@ public interface IFrontendController { @RequestMapping(value = "/schemas", method = RequestMethod.GET, produces = {"application/tabulator+json"}) @ResponseBody @PageableAsQueryParam - public ResponseEntity<TabulatorLocalPagination> findAllSchemasForTabulator( + ResponseEntity<TabulatorLocalPagination> findAllSchemasForTabulator( @Parameter(hidden = true) final Pageable pgbl, final WebRequest wr, final HttpServletResponse hsr, @@ -36,7 +36,7 @@ public ResponseEntity<TabulatorLocalPagination> findAllSchemasForTabulator( @RequestMapping(value = "/metadata", method = RequestMethod.GET, produces = {"application/tabulator+json"}) @ResponseBody @PageableAsQueryParam - public ResponseEntity<TabulatorLocalPagination> findAllMetadataForTabulator( + ResponseEntity<TabulatorLocalPagination> findAllMetadataForTabulator( @RequestParam(value = "id", required = false) String id, @Parameter(hidden = true) final Pageable pgbl, final WebRequest wr, @@ -45,17 +45,17 @@ public ResponseEntity<TabulatorLocalPagination> findAllMetadataForTabulator( @RequestMapping(value = "/schemas", method = RequestMethod.GET, produces = {"application/json"}) @ResponseBody - public ResponseEntity<TabulatorRemotePagination> getSchemaRecordsForUi(Pageable pgbl, - WebRequest wr, - HttpServletResponse hsr, - UriComponentsBuilder ucb); + ResponseEntity<TabulatorRemotePagination> getSchemaRecordsForUi(Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb); @RequestMapping(value = "/metadata", method = RequestMethod.GET, produces = {"application/json"}) @ResponseBody - public ResponseEntity<TabulatorRemotePagination> getMetadataRecordsForUi(@RequestParam(value = "id", required = false) String id, - Pageable pgbl, - WebRequest wr, - HttpServletResponse hsr, - UriComponentsBuilder ucb); + ResponseEntity<TabulatorRemotePagination> getMetadataRecordsForUi(@RequestParam(value = "id", required = false) String id, + Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java index b2fb319a..f21f4fce 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageController.java @@ -45,7 +45,7 @@ public interface ILandingPageController { @ApiResponse(responseCode = "200", description = "OK and the landingpage is returned if the id exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/schema-landing-page"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public String getLandingPageOfSchemaWithId( + String getLandingPageOfSchemaWithId( @Parameter(description = "The record identifier or schema identifier.", required = true) @RequestParam(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, WebRequest wr, @@ -60,10 +60,10 @@ public String getLandingPageOfSchemaWithId( @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/metadata-landing-page"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public String getLandingPageOfMetadataDocumentWithId( + String getLandingPageOfMetadataDocumentWithId( @Parameter(description = "The identifier of the metadata document.", required = true) @RequestParam(value = "id") String id, @Parameter(description = "The version of the digital object. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, WebRequest wr, - HttpServletResponse hsr, + HttpServletResponse hsr, Model model); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java index fbfc2c63..bc7110c8 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ILandingPageControllerV2.java @@ -45,7 +45,7 @@ public interface ILandingPageControllerV2 { @ApiResponse(responseCode = "200", description = "OK and the landingpage is returned if the id exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/schema-landing-page-v2"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public String getLandingPageOfSchemaWithId( + String getLandingPageOfSchemaWithId( @Parameter(description = "The record identifier or schema identifier.", required = true) @RequestParam(value = "schemaId") String id, @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, WebRequest wr, @@ -60,10 +60,10 @@ public String getLandingPageOfSchemaWithId( @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/metadata-landing-page-v2"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public String getLandingPageOfMetadataDocumentWithId( + String getLandingPageOfMetadataDocumentWithId( @Parameter(description = "The identifier of the metadata document.", required = true) @RequestParam(value = "id") String id, @Parameter(description = "The version of the digital object. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, WebRequest wr, - HttpServletResponse hsr, + HttpServletResponse hsr, Model model); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java index 07ac11cf..4bc3ba2b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java @@ -66,7 +66,7 @@ public interface IMetadataController extends InfoContributor { @RequestMapping(value = {"","/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @ResponseBody - public ResponseEntity createRecord( + ResponseEntity createRecord( @Parameter(description = "Json representation of the metadata record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile metadataRecord, @Parameter(description = "The metadata document associated with the record. The document must match the schema selected by the record.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, final HttpServletRequest request, @@ -81,10 +81,10 @@ public ResponseEntity createRecord( @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.metadata-record+json"}) - public ResponseEntity<MetadataRecord> getRecordById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity<MetadataRecord> getRecordById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a metadata record by id.", description = "Obtain a single record by its resource identifier. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " @@ -94,10 +94,10 @@ public ResponseEntity<MetadataRecord> getRecordById(@Parameter(description = "Th @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.acl+json"}) - public ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a landing page by id.", description = "Obtain a single record by its resource identifier. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " @@ -107,10 +107,10 @@ public ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public ModelAndView getLandingpageById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the metadata document. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ModelAndView getLandingpageById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the metadata document. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a metadata document by record identifier.", description = "Obtain a single metadata document identified by its resource identifier." + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + "Furthermore, a specific version of the metadata document can be returned by providing a version number as request parameter.", @@ -120,10 +120,10 @@ public ModelAndView getLandingpageById(@Parameter(description = "The record iden @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}) @ResponseBody - public ResponseEntity getMetadataDocumentById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity getMetadataDocumentById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get all records.", description = "List all records in a paginated and sorted form. The result can be refined by providing id, specific related resource id(s) and/or metadata schema id(s) valid records must match. " + "If 'id' is provided all available versions for given 'id' will be returned and all other parameters will be ignored." @@ -136,13 +136,13 @@ public ResponseEntity getMetadataDocumentById(@Parameter(description = "The reco @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) @PageableAsQueryParam @ResponseBody - public ResponseEntity<List<MetadataRecord>> getRecords( + ResponseEntity<List<MetadataRecord>> getRecords( @Parameter(description = "ID of the metadata document.", required = false) @RequestParam(value = "id", required = false) String id, @Parameter(description = "A list of related resource identifiers.", required = false) @RequestParam(value = "resourceId", required = false) List<String> relatedIds, @Parameter(description = "A list of metadata schema identifiers.", required = false) @RequestParam(value = "schemaId", required = false) List<String> schemaIds, @Parameter(description = "The UTC time of the earliest update of a returned record.", required = false) @RequestParam(name = "from", required = false) Instant updateFrom, @Parameter(description = "The UTC time of the latest update of a returned record.", required = false) @RequestParam(name = "until", required = false) Instant updateUntil, - @Parameter(hidden = true)@PageableDefault(sort = {"lastUpdate"}, direction = Sort.Direction.DESC) Pageable pgbl, + @Parameter(hidden = true) @PageableDefault(sort = {"lastUpdate"}, direction = Sort.Direction.DESC) Pageable pgbl, WebRequest wr, HttpServletResponse hsr, UriComponentsBuilder ucb); @@ -177,5 +177,5 @@ ResponseEntity updateRecord( @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) }) @ResponseBody - public ResponseEntity deleteRecord(@Parameter(description = "The resource identifier.", required = true) @PathVariable(value = "id") String id, WebRequest wr, HttpServletResponse hsr); + ResponseEntity deleteRecord(@Parameter(description = "The resource identifier.", required = true) @PathVariable(value = "id") String id, WebRequest wr, HttpServletResponse hsr); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index 4d028c4c..fcdf797f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -65,7 +65,7 @@ public interface IMetadataControllerV2 extends InfoContributor { @RequestMapping(value = {"/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @ResponseBody - public ResponseEntity createRecord( + ResponseEntity createRecord( @Parameter(description = "Json representation of the datacite record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile dataciteRecord, @Parameter(description = "The metadata document associated with the record. The document must match the schema selected by the record.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, final HttpServletRequest request, @@ -80,10 +80,10 @@ public ResponseEntity createRecord( @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datacite.org+json"}) - public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity<DataResource> getRecordById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a content information record by id.", description = "Obtain a single record by its resource identifier. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " @@ -93,10 +93,10 @@ public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.content-information+json"}) - public ResponseEntity<ContentInformation> getContentInformationById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity<ContentInformation> getContentInformationById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a metadata record by id.", description = "Obtain a single record by its resource identifier. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " @@ -106,10 +106,10 @@ public ResponseEntity<ContentInformation> getContentInformationById(@Parameter(d @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.acl+json"}) - public ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a landing page by id.", description = "Obtain a single record by its resource identifier. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. Furthermore, a specific version of the record can be returned " @@ -119,10 +119,10 @@ public ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public ModelAndView getLandingpageById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the metadata document. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ModelAndView getLandingpageById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the metadata document. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a metadata document by record identifier.", description = "Obtain a single metadata document identified by its resource identifier." + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " + "Furthermore, a specific version of the metadata document can be returned by providing a version number as request parameter.", @@ -132,10 +132,10 @@ public ModelAndView getLandingpageById(@Parameter(description = "The record iden @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/xml", "application/json"}) @ResponseBody - public ResponseEntity getMetadataDocumentById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, - @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity getMetadataDocumentById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get all records.", description = "List all records in a paginated and sorted form. The result can be refined by providing id, specific related resource id(s) and/or metadata schema id(s) valid records must match. " + "If 'id' is provided all available versions for given 'id' will be returned and all other parameters will be ignored." @@ -148,13 +148,13 @@ public ResponseEntity getMetadataDocumentById(@Parameter(description = "The reco @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) @PageableAsQueryParam @ResponseBody - public ResponseEntity<List<DataResource>> getRecords( + ResponseEntity<List<DataResource>> getRecords( @Parameter(description = "ID of the metadata document.", required = false) @RequestParam(value = "id", required = false) String id, @Parameter(description = "A list of related resource identifiers.", required = false) @RequestParam(value = "resourceId", required = false) List<String> relatedIds, @Parameter(description = "A list of metadata schema identifiers.", required = false) @RequestParam(value = "schemaId", required = false) List<String> schemaIds, @Parameter(description = "The UTC time of the earliest update of a returned record.", required = false) @RequestParam(name = "from", required = false) Instant updateFrom, @Parameter(description = "The UTC time of the latest update of a returned record.", required = false) @RequestParam(name = "until", required = false) Instant updateUntil, - @Parameter(hidden = true)@PageableDefault(sort = {"lastUpdate"}, direction = Sort.Direction.DESC) Pageable pgbl, + @Parameter(hidden = true) @PageableDefault(sort = {"lastUpdate"}, direction = Sort.Direction.DESC) Pageable pgbl, WebRequest wr, HttpServletResponse hsr, UriComponentsBuilder ucb); @@ -189,5 +189,5 @@ ResponseEntity updateRecord( @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) }) @ResponseBody - public ResponseEntity deleteRecord(@Parameter(description = "The resource identifier.", required = true) @PathVariable(value = "id") String id, WebRequest wr, HttpServletResponse hsr); + ResponseEntity deleteRecord(@Parameter(description = "The resource identifier.", required = true) @PathVariable(value = "id") String id, WebRequest wr, HttpServletResponse hsr); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataEditorController.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataEditorController.java index ce87b9cb..885b11d1 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataEditorController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataEditorController.java @@ -20,15 +20,15 @@ public interface IMetadataEditorController { @RequestMapping("/schema-management") - public ModelAndView schemaManagement(); + ModelAndView schemaManagement(); @RequestMapping("/metadata-management") - public ModelAndView metadataManagement(Pageable pgbl, - WebRequest wr, - HttpServletResponse hsr, - UriComponentsBuilder ucb); + ModelAndView metadataManagement(Pageable pgbl, + WebRequest wr, + HttpServletResponse hsr, + UriComponentsBuilder ucb); @RequestMapping(value = {"/dashboard", "/", ""}) - public String dashboard(); + String dashboard(); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java index 7339bd0c..9eb1647f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryController.java @@ -65,7 +65,7 @@ public interface ISchemaRegistryController extends InfoContributor { @ApiResponse(responseCode = "409", description = "A Conflict is returned, if there is already a record for the provided schema id.")}) @RequestMapping(value = {"", "/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody - public ResponseEntity createRecord( + ResponseEntity createRecord( @Parameter(description = "Json representation of the schema record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile schemaRecord, @Parameter(description = "The metadata schema document associated with the record.", required = true) @RequestPart(name = "schema", required = true) final MultipartFile document, final HttpServletRequest request, @@ -80,10 +80,10 @@ public ResponseEntity createRecord( @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.schema-record+json"}) @ResponseBody - public ResponseEntity getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get landing page of schema by schema id (and version).", description = "Show landing page by its schema id. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " @@ -92,10 +92,10 @@ public ResponseEntity getRecordById(@Parameter(description = "The record identif @ApiResponse(responseCode = "200", description = "OK and the landingpage is returned if the id exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public ModelAndView getLandingPageById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr); + ModelAndView getLandingPageById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Validate a metadata document.", description = "Validate the provided metadata document using the addressed schema. If all parameters" + " are provided, the schema is identified uniquely by schemaId and version. If the version is omitted, the most recent version of the " @@ -107,11 +107,11 @@ public ModelAndView getLandingPageById(@Parameter(description = "The record iden }) @RequestMapping(value = {"/{schemaId}/validate"}, method = {RequestMethod.POST}, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @ResponseBody - public ResponseEntity validate(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - @Parameter(description = "The metadata file to validate against the addressed schema.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity validate(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + @Parameter(description = "The metadata file to validate against the addressed schema.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a schema document by schema id.", description = "Obtain a single schema document identified by its schema id. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " @@ -122,10 +122,10 @@ public ResponseEntity validate(@Parameter(description = "The record identifier o @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}) @ResponseBody - public ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get all schema records.", description = "List all schema records in a paginated and/or sorted form. The result can be refined by providing schemaId, a list of one or more mimetypes and/or a date range. Returned schema record(s) must match. " + "if 'schemaId' is provided all other parameters were skipped and all versions of the given schemaId record will be returned. " @@ -138,7 +138,7 @@ public ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) @ResponseBody @PageableAsQueryParam - public ResponseEntity<List<MetadataSchemaRecord>> getRecords( + ResponseEntity<List<MetadataSchemaRecord>> getRecords( @Parameter(description = "SchemaId", required = false) @RequestParam(value = "schemaId", required = false) String schemaId, @Parameter(description = "A list of mime types returned schemas are associated with.", required = false) @RequestParam(value = "mimeType", required = false) List<String> mimeTypes, @Parameter(description = "The UTC time of the earliest update of a returned record.", required = false) @RequestParam(name = "from", required = false) Instant updateFrom, @@ -179,5 +179,5 @@ ResponseEntity updateRecord( @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) }) @ResponseBody - public ResponseEntity deleteRecord(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, @Header(name = "ETag", required = true) WebRequest wr, HttpServletResponse hsr); + ResponseEntity deleteRecord(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, @Header(name = "ETag", required = true) WebRequest wr, HttpServletResponse hsr); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java index de2533a8..79e01f76 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java @@ -66,7 +66,7 @@ public interface ISchemaRegistryControllerV2 extends InfoContributor { @ApiResponse(responseCode = "409", description = "A Conflict is returned, if there is already a record for the provided schema id.")}) @RequestMapping(value = {"", "/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody - public ResponseEntity<DataResource> createRecord( + ResponseEntity<DataResource> createRecord( @Parameter(description = "Json representation of the schema record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile schemaRecord, @Parameter(description = "The metadata schema document associated with the record.", required = true) @RequestPart(name = "schema", required = true) final MultipartFile document, final HttpServletRequest request, @@ -83,10 +83,10 @@ public ResponseEntity<DataResource> createRecord( @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datacite.org+json"}) @ResponseBody - public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity<DataResource> getRecordById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(operationId = "getContentInformationRecordOfSchema", summary = "Get content information record by schema id (and version).", description = "Obtain is single schema record by its schema id. " @@ -97,10 +97,10 @@ public ResponseEntity<DataResource> getRecordById(@Parameter(description = "The @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.content-information+json"}) @ResponseBody - public ResponseEntity<ContentInformation> getContentInformationById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity<ContentInformation> getContentInformationById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get landing page of schema by schema id (and version).", description = "Show landing page by its schema id. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " @@ -109,10 +109,10 @@ public ResponseEntity<ContentInformation> getContentInformationById(@Parameter(d @ApiResponse(responseCode = "200", description = "OK and the landingpage is returned if the id exists and the user has sufficient permission.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"text/html"}) - public ModelAndView getLandingPageById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr); + ModelAndView getLandingPageById(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Validate a metadata document.", description = "Validate the provided metadata document using the addressed schema. If all parameters" + " are provided, the schema is identified uniquely by schemaId and version. If the version is omitted, the most recent version of the " @@ -124,11 +124,11 @@ public ModelAndView getLandingPageById(@Parameter(description = "The record iden }) @RequestMapping(value = {"/{schemaId}/validate"}, method = {RequestMethod.POST}, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @ResponseBody - public ResponseEntity validate(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - @Parameter(description = "The metadata file to validate against the addressed schema.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity validate(@Parameter(description = "The record identifier or schema identifier.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + @Parameter(description = "The metadata file to validate against the addressed schema.", required = true) @RequestPart(name = "document", required = true) final MultipartFile document, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get a schema document by schema id.", description = "Obtain a single schema document identified by its schema id. " + "Depending on a user's role, accessing a specific record may be allowed or forbidden. " @@ -138,10 +138,10 @@ public ResponseEntity validate(@Parameter(description = "The record identifier o @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id and version was found.")}) @RequestMapping(value = {"/{schemaId}"}, method = {RequestMethod.GET}, produces = {"application/json", "application/xml"}) @ResponseBody - public ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, - @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, - WebRequest wr, - HttpServletResponse hsr); + ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, + @Parameter(description = "The version of the record.", required = false) @RequestParam(value = "version", required = false) Long version, + WebRequest wr, + HttpServletResponse hsr); @Operation(summary = "Get all schema records.", description = "List all schema records in a paginated and/or sorted form. The result can be refined by providing schemaId, a list of one or more mimetypes and/or a date range. Returned schema record(s) must match. " + "if 'schemaId' is provided all other parameters were skipped and all versions of the given schemaId record will be returned. " @@ -154,7 +154,7 @@ public ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) @ResponseBody @PageableAsQueryParam - public ResponseEntity<List<DataResource>> getRecords( + ResponseEntity<List<DataResource>> getRecords( @Parameter(description = "SchemaId", required = false) @RequestParam(value = "schemaId", required = false) String schemaId, @Parameter(description = "A list of mime types returned schemas are associated with.", required = false) @RequestParam(value = "mimeType", required = false) List<String> mimeTypes, @Parameter(description = "The UTC time of the earliest update of a returned record.", required = false) @RequestParam(name = "from", required = false) Instant updateFrom, @@ -195,5 +195,5 @@ ResponseEntity<DataResource> updateRecord( @Parameter(name = "If-Match", description = "ETag of the object. Please use quotation marks!", required = true, in = ParameterIn.HEADER) }) @ResponseBody - public ResponseEntity deleteRecord(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, @Header(name = "ETag", required = true) WebRequest wr, HttpServletResponse hsr); + ResponseEntity deleteRecord(@Parameter(description = "The schema id.", required = true) @PathVariable(value = "schemaId") String id, @Header(name = "ETag", required = true) WebRequest wr, HttpServletResponse hsr); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java index 354a1e49..e483898e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java @@ -91,7 +91,7 @@ public ResponseEntity<TabulatorLocalPagination> findAllMetadataForTabulator( pageable = PageRequest.of(pgbl.getPageNumber() < 1 ? 0 : pgbl.getPageNumber() - 1, pgbl.getPageSize(), Sort.by("id").ascending()); } LOG.trace(SHOW_PAGE, pageable); - List<String> metadataDocumentId = id == null ? null : Arrays.asList(id); + List<String> metadataDocumentId = id == null ? null : List.of(id); ResponseEntity< List<MetadataRecord>> responseEntity4metadataRecords = metadtaControllerImpl.getRecords(null, null, metadataDocumentId, null, null, pageable, wr, hsr, ucb); List<MetadataRecord> metadataRecords = responseEntity4metadataRecords.getBody(); @@ -145,7 +145,7 @@ public ResponseEntity<TabulatorRemotePagination> getMetadataRecordsForUi( } LOG.trace(SHOW_PAGE, pageable); - List<String> schemaIds = id == null ? null : Arrays.asList(id); + List<String> schemaIds = id == null ? null : List.of(id); ResponseEntity< List<MetadataRecord>> responseEntity4metadataRecords; List<MetadataRecord> metadataRecords = null; String pageSize = null; diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index 10e98d1f..41ba3edc 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -375,7 +375,7 @@ public ResponseEntity<List<MetadataRecord>> getRecords( } catch (Exception rnfe) { // schemaID not found set version to 1 currentSchemaRecord = new MetadataSchemaRecord(); - currentSchemaRecord.setSchemaVersion(1l); + currentSchemaRecord.setSchemaVersion(1L); allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); } for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 47cb5ae7..b03f81af 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -229,7 +229,7 @@ public ResponseEntity createRecord( // return ResponseEntity.status(HttpStatus.CONFLICT).body(message); // } DataResource result = DataResourceRecordUtil.createDataResourceRecord4Metadata(metadataConfig, recordDocument, document); - LOG.trace("Get dataresource: '{}'", result.toString()); + LOG.trace("Get dataresource: '{}'", result); String eTag = result.getEtag(); LOG.trace("Get ETag: ' {}'", eTag); // Successfully created metadata record. @@ -266,7 +266,7 @@ public ResponseEntity<DataResource> getRecordById( LOG.trace("Metadata record found. Prepare response."); //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN LOG.trace("Get ETag of DataResource."); - LOG.trace("Get dataresource: '{}'", metadataRecord.toString()); + LOG.trace("Get dataresource: '{}'", metadataRecord); String etag = metadataRecord.getEtag(); LOG.trace("Get ETag: ' {}'", etag); DataResourceRecordUtil.fixSchemaUrl(metadataRecord); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java index dd79510f..490d0d30 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java @@ -50,7 +50,7 @@ public class MetadataSearchController { private static final String SEARCH_PATH_POSTFIX = "/_search"; @Autowired - private SearchConfiguration searchConfiguration; + private final SearchConfiguration searchConfiguration; /** * Constructor with configuration. diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java index c5f8d184..9578df51 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchControllerV2.java @@ -49,7 +49,7 @@ public class MetadataSearchControllerV2 { private static final String SEARCH_PATH_POSTFIX = "/_search"; @Autowired - private SearchConfiguration searchConfiguration; + private final SearchConfiguration searchConfiguration; /** * Constructor with configuration. diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index 0bc40c22..7cd031a3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -288,7 +288,7 @@ public ResponseEntity<List<MetadataSchemaRecord>> getRecords(@RequestParam(value MetadataSchemaRecordUtil.fixSchemaDocumentUri(item); schemaList.add(item); if (LOG.isTraceEnabled()) { - LOG.trace("===> " + item.toString()); + LOG.trace("===> " + item); } }); @@ -316,7 +316,7 @@ public ResponseEntity updateRecord(@PathVariable("schemaId") final String schema URI locationUri; locationUri = MetadataSchemaRecordUtil.getSchemaDocumentUri(updatedSchemaRecord); - LOG.trace("Set locationUri to '{}'", locationUri.toString()); + LOG.trace("Set locationUri to '{}'", locationUri); return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updatedSchemaRecord); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 101d86e8..5ec53acc 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -141,7 +141,7 @@ public ResponseEntity<DataResource> createRecord( LOG.trace("Schema record successfully persisted."); URI locationUri; locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(dataResourceRecord); - LOG.trace("Set locationUri to '{}'", locationUri.toString()); + LOG.trace("Set locationUri to '{}'", locationUri); return ResponseEntity.created(locationUri).eTag("\"" + etag + "\"").body(dataResourceRecord); } @@ -348,7 +348,7 @@ public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final URI locationUri; locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(updatedSchemaRecord); - LOG.trace("Set locationUri to '{}'", locationUri.toString()); + LOG.trace("Set locationUri to '{}'", locationUri); return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updatedSchemaRecord); } diff --git a/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java b/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java index 789d62f7..bace5c3d 100644 --- a/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java +++ b/src/test/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessageTest.java @@ -102,7 +102,7 @@ public void testActionIsNull() throws JsonProcessingException { MetadataResourceMessage result = MetadataResourceMessage.createMessage(metadataRecord, null, DataResourceMessage.SUB_CATEGORY.DATA, uri, sender); try { checkJsonString(result, sender, caller, action, id, uri, type); - assertTrue(false); + fail(); } catch (MessageValidationException mve) { assertTrue(mve.getMessage().contains("must not be null")); } @@ -127,7 +127,7 @@ public void testIdIsNull() throws JsonProcessingException { MetadataResourceMessage result = MetadataResourceMessage.factoryCreateMetadataMessage(metadataRecord, caller, sender); try { checkJsonString(result, sender, caller, action, id, uri, type); - assertTrue(false); + fail(); } catch (MessageValidationException mve) { assertTrue(mve.getMessage().contains("must not be null")); } @@ -147,7 +147,7 @@ public void testMetadataRecordIsNull() throws JsonProcessingException { MetadataResourceMessage result = MetadataResourceMessage.factoryCreateMetadataMessage(metadataRecord, caller, sender); try { checkJsonString(result, sender, caller, action, id, uri, type); - assertTrue(false); + fail(); } catch (MessageValidationException mve) { assertTrue(mve.getMessage().contains("must not be null")); } @@ -307,8 +307,8 @@ private void checkJsonString(MetadataResourceMessage mdrm, String sender, String } else { assertFalse(jsonString.contains("\"" + MetadataResourceMessage.DOCUMENT_TYPE_PROPERTY + "\":\"")); } - - assertTrue(mdrm.getEntityName().equals("metadata")); + + assertEquals("metadata", mdrm.getEntityName()); assertTrue(mdrm.getRoutingKey().startsWith("metadata")); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java b/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java index 56de6a64..791c1d0f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dao/IDataRecordDaoTest.java @@ -94,15 +94,15 @@ public void testFindByMetadataIdAndVersion() { System.out.println("findByMetadataIdAndVersion"); String metadataId = "metadataId1"; // IDataRecordDao instance = new IDataRecordDaoImpl(); - Optional<DataRecord> result = instance.findByMetadataIdAndVersion(metadataId, 3l); + Optional<DataRecord> result = instance.findByMetadataIdAndVersion(metadataId, 3L); assertNotNull(result); assertTrue(result.isPresent()); - result = instance.findByMetadataIdAndVersion(metadataId, 1l); + result = instance.findByMetadataIdAndVersion(metadataId, 1L); assertNotNull(result); assertFalse(result.isPresent()); - result = instance.findByMetadataIdAndVersion("unknownId", 1l); + result = instance.findByMetadataIdAndVersion("unknownId", 1L); assertNotNull(result); assertFalse(result.isPresent()); } @@ -298,7 +298,7 @@ private void saveDataRecord(String metadataId, dataRecord.setDocumentHash(documentHash); dataRecord.setMetadataId(metadataId); dataRecord.setVersion(Long.valueOf(version)); - dataRecord.setSchemaVersion(1l); + dataRecord.setSchemaVersion(1L); dataRecord.setSchemaId(schemaId); dataRecord.setLastUpdate(instant); dataRecord.setMetadataDocumentUri(metadataDocumentUri); diff --git a/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java b/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java index c0620569..2de570ab 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDaoTest.java @@ -80,26 +80,26 @@ public void tearDown() { public void testExistsSchemaRecordBySchemaIdAndVersion() { System.out.println("existsSchemaRecordBySchemaIdAndVersion"); String schemaId = "schemaId"; - Long version = 1l; + Long version = 1L; ISchemaRecordDao instance = schemaRecordDao; boolean expResult = false; boolean result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); assertEquals(expResult, result); schemaId = "schemaId1"; - version = 4l; + version = 4L; result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); assertEquals(expResult, result); schemaId = "schemaId1"; - version = 3l; + version = 3L; result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. schemaId = "schemaId1/3"; - version = 2l; + version = 2L; result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. expResult = true; schemaId = "schemaId1/3"; - version = 3l; + version = 3L; result = instance.existsSchemaRecordBySchemaIdAndVersion(schemaId, version); assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. } @@ -112,17 +112,17 @@ public void testExistsSchemaRecordBySchemaIdAndVersion() { public void testExistsSchemaRecordBySchemaIdStartWithAndVersion() { System.out.println("existsSchemaRecordBySchemaIdStartsWithAndVersion"); String schemaId = "schemaId/"; - Long version = 1l; + Long version = 1L; ISchemaRecordDao instance = schemaRecordDao; boolean expResult = false; boolean result = instance.existsSchemaRecordBySchemaIdStartsWithAndVersion(schemaId, version); assertEquals(expResult, result); schemaId = "schemaId1"; - version = 4l; + version = 4L; result = instance.existsSchemaRecordBySchemaIdStartsWithAndVersion(schemaId, version); expResult = true; schemaId = "schemaId1"; - version = 3l; + version = 3L; result = instance.existsSchemaRecordBySchemaIdStartsWithAndVersion(schemaId, version); assertEquals(expResult, result); } @@ -149,7 +149,7 @@ public void testFindBySchemaId() { schemaIdWithVersion = "schemaId3/1"; result = instance.findBySchemaId(schemaIdWithVersion); assertNotNull(result); - System.out.println(result.toString()); + System.out.println(result); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java b/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java index b5eee2fb..f5fee856 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/dao/IUrl2PathDaoTest.java @@ -157,7 +157,7 @@ public void testDelete() { String metadataId = "metadataId"; Optional<Url2Path> result; long noOfDataSets = dataRecordDao.count(); - assertEquals(7l, noOfDataSets); + assertEquals(7L, noOfDataSets); for (int i = 1; i < 7; i++) { result = instance.findByUrl(metadataId + i); assertNotNull(result); diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java index 07d1ae54..dd6320db 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java @@ -381,7 +381,7 @@ public void documentSchemaRegistry4Json() throws Exception { // record.setId("my_id"); metadataRecord.setRelatedResource(RELATED_RESOURCE); metadataRecord.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - metadataRecord.setSchemaVersion(1l); + metadataRecord.setSchemaVersion(1L); recordFile = new MockMultipartFile("record", "metadata-record4json.json", "application/json", mapper.writeValueAsString(metadataRecord).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.json", "application/json", DOCUMENT_V1.getBytes()); @@ -422,7 +422,7 @@ public void documentSchemaRegistry4Json() throws Exception { mapper = new ObjectMapper(); MetadataRecord record = mapper.readValue(body, MetadataRecord.class); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - record.setSchemaVersion(2l); + record.setSchemaVersion(2L); recordFile = new MockMultipartFile("record", "metadata-record4json-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v2.json", "application/xml", DOCUMENT_V2.getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java index a786ceb6..e7132672 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java @@ -419,7 +419,7 @@ public void documentSchemaRegistry4Json() throws Exception { // record.setId("my_id"); metadataRecord.setRelatedResource(RELATED_RESOURCE); metadataRecord.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - metadataRecord.setSchemaVersion(1l); + metadataRecord.setSchemaVersion(1L); recordFile = new MockMultipartFile("record", "metadata-record4json.json", "application/json", mapper.writeValueAsString(metadataRecord).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.json", "application/json", DOCUMENT_V1.getBytes()); @@ -466,7 +466,7 @@ public void documentSchemaRegistry4Json() throws Exception { MetadataRecord record = mapper.readValue(body, MetadataRecord.class); record.getAcl().add(new AclEntry("guest", PERMISSION.READ)); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - record.setSchemaVersion(2l); + record.setSchemaVersion(2L); recordFile = new MockMultipartFile("record", "metadata-record4json-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v2.json", "application/xml", DOCUMENT_V2.getBytes()); @@ -492,7 +492,7 @@ public void documentSchemaRegistry4Json() throws Exception { andExpect(status().isOk()). andReturn().getResponse(); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - record.setSchemaVersion(3l); + record.setSchemaVersion(3L); recordFile = new MockMultipartFile("record", "metadata-record4json-v3.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v3.json", "application/xml", DOCUMENT_V3.getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java index 2ecdb8e8..503e89ec 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java @@ -402,7 +402,7 @@ public void documentSchemaRegistry() throws Exception { // record.setId("my_id"); metadataRecord.setRelatedResource(RELATED_RESOURCE); metadataRecord.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - metadataRecord.setSchemaVersion(1l); + metadataRecord.setSchemaVersion(1L); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(metadataRecord).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DOCUMENT_V1.getBytes()); @@ -450,7 +450,7 @@ public void documentSchemaRegistry() throws Exception { MetadataRecord record = mapper.readValue(body, MetadataRecord.class); record.getAcl().add(new AclEntry("guest", PERMISSION.READ)); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - record.setSchemaVersion(2l); + record.setSchemaVersion(2L); recordFile = new MockMultipartFile("record", "metadata-record-v2.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v2.xml", "application/xml", DOCUMENT_V2.getBytes()); @@ -476,7 +476,7 @@ public void documentSchemaRegistry() throws Exception { andExpect(status().isOk()). andReturn().getResponse(); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(EXAMPLE_SCHEMA_ID)); - record.setSchemaVersion(3l); + record.setSchemaVersion(3L); recordFile = new MockMultipartFile("record", "metadata-record-v3.json", "application/json", mapper.writeValueAsString(record).getBytes()); metadataFile = new MockMultipartFile("document", "metadata-v3.xml", "application/xml", DOCUMENT_V3.getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/domain/AclRecordTest.java b/src/test/java/edu/kit/datamanager/metastore2/domain/AclRecordTest.java index 2f18f7b2..64021b1b 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/domain/AclRecordTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/domain/AclRecordTest.java @@ -78,7 +78,7 @@ public void testGetReadSidsWithAdminPermission() { newAclList.add(new AclEntry(sid, PERMISSION.ADMINISTRATE)); AclRecord instance = new AclRecord(); instance.setAcl(newAclList); - assertTrue(instance.getRead().size() == 1); + assertEquals(1, instance.getRead().size()); assertTrue(instance.getRead().contains(sid)); assertNull(instance.getMetadataDocument()); assertNull(instance.getMetadataRecord()); @@ -95,7 +95,7 @@ public void testGetReadSidsWithWritePermission() { newAclList.add(new AclEntry(sid, PERMISSION.WRITE)); AclRecord instance = new AclRecord(); instance.setAcl(newAclList); - assertTrue(instance.getRead().size() == 1); + assertEquals(1, instance.getRead().size()); assertTrue(instance.getRead().contains(sid)); assertNull(instance.getMetadataDocument()); assertNull(instance.getMetadataRecord()); @@ -112,7 +112,7 @@ public void testGetReadSidsWithReadPermission() { newAclList.add(new AclEntry(sid, PERMISSION.READ)); AclRecord instance = new AclRecord(); instance.setAcl(newAclList); - assertTrue(instance.getRead().size() == 1); + assertEquals(1, instance.getRead().size()); assertTrue(instance.getRead().contains(sid)); assertNull(instance.getMetadataDocument()); assertNull(instance.getMetadataRecord()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecordTest.java b/src/test/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecordTest.java index 3aad6796..7515509a 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecordTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/domain/MetadataSchemaRecordTest.java @@ -93,7 +93,7 @@ public void testSetAndGetSchemaIdWithCapitalLetters() { public void testSetAndGetSchemaVersion() { System.out.println("getSchemaVersion"); MetadataSchemaRecord instance = new MetadataSchemaRecord(); - Long expResult = 3l; + Long expResult = 3L; instance.setSchemaVersion(expResult); Long result = instance.getSchemaVersion(); assertEquals(expResult, result); diff --git a/src/test/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifierTest.java b/src/test/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifierTest.java index 174fd670..4488a3be 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifierTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifierTest.java @@ -173,7 +173,7 @@ public void testSetAndtGetIdentifierType() { assertEquals(expResult, result); try { ResourceIdentifier.IdentifierType.fromValue("wrongType"); - assertTrue(false); + fail(); } catch (IllegalArgumentException iae) { assertTrue(true); } @@ -230,7 +230,7 @@ public void testFactoryResourceIdentifier() { public void testSetAndGetId() { System.out.println("getId"); ResourceIdentifier instance = new ResourceIdentifier(); - Long expResult = 123l; + Long expResult = 123L; instance.setId(expResult); Long result = instance.getId(); assertEquals(expResult, result); diff --git a/src/test/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhControllerTest.java index 631c4351..b9d421e6 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/oaipmh/web/OaiPmhControllerTest.java @@ -842,10 +842,10 @@ private void checkInvalidResponseListRecords(String from, String until, String m private void checkInvalidResponseList(VerbType type, String from, String until, String metadataPrefix, String token, OAIPMHerrorcodeType errorType) throws Exception { MockHttpServletRequestBuilder param = get("/oaipmh").param("verb", type.value()); if (from != null) { - param = param.param("from", from.toString()); + param = param.param("from", from); } if (until != null) { - param = param.param("until", until.toString()); + param = param.param("until", until); } if (metadataPrefix != null) { param = param.param("metadataPrefix", metadataPrefix); @@ -985,7 +985,7 @@ public void ingestMetadataRecord(String schemaId, String metadataDocument) throw // record.setId("my_id"); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(schemaId)); UUID randomUUID = UUID.randomUUID(); - record.setRelatedResource(ResourceIdentifier.factoryUrlResourceIdentifier("http://example.org/" + randomUUID.toString())); + record.setRelatedResource(ResourceIdentifier.factoryUrlResourceIdentifier("http://example.org/" + randomUUID)); Set<AclEntry> aclEntries = new HashSet<>(); // aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); // aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); @@ -1036,9 +1036,9 @@ private void validateResponse(String response) { Validator validator = schema.newValidator(); validator.validate(xmlFile); } catch (SAXException e) { - Assert.assertFalse("Response is not valid!" + e.getMessage(), true); + Assert.fail("Response is not valid!" + e.getMessage()); } catch (IOException e) { - Assert.assertFalse(e.getMessage(), true); + Assert.fail(e.getMessage()); } } @@ -1066,9 +1066,9 @@ private void validateResponse(String schemaId, String response) throws Malformed Validator validator = schema.newValidator(); validator.validate(xmlFile); } catch (SAXException e) { - Assert.assertFalse("Response is not valid!" + e.getMessage(), true); + Assert.fail("Response is not valid!" + e.getMessage()); } catch (IOException e) { - Assert.assertFalse(e.getMessage(), true); + Assert.fail(e.getMessage()); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunnerTest.java b/src/test/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunnerTest.java index 41101c1f..2d701881 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunnerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunnerTest.java @@ -242,7 +242,7 @@ public void testElasticRunnerWithDate() throws Exception { Date now = new Date(); Date oneHourBack = new Date(now.getTime() - 3600000); String date = new SimpleDateFormat("yyyy-MM-dd").format(oneHourBack); - System.out.println(oneHourBack.toString()); + System.out.println(oneHourBack); System.out.println(date); eir.run("-u", date); Assert.assertTrue(true); @@ -279,7 +279,7 @@ public void testElasticRunnerWithAllIndicesAndMetadataDocument() throws Exceptio private String ingestSchemaRecord(String schemaId) throws Exception { MetadataSchemaRecord schemaRecord = new MetadataSchemaRecord(); schemaRecord.setSchemaId(schemaId); - schemaRecord.setSchemaVersion(1l); + schemaRecord.setSchemaVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); Set<AclEntry> aclEntries = new HashSet<>(); aclEntries.add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java index 27f2d017..8f18517c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java @@ -527,7 +527,7 @@ public void testCreateTwoVersionsOfSchemaRecord() throws Exception { file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataSchemaRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); - Assert.assertEquals(result.getSchemaVersion(), Long.valueOf(1l)); + Assert.assertEquals(result.getSchemaVersion(), Long.valueOf(1L)); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/schemas/"). file(recordFile). @@ -835,7 +835,7 @@ public void testUpdateRecordAndDocument() throws Exception { testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion()); + Assert.assertEquals(record.getSchemaVersion() + 1L, (long) record2.getSchemaVersion()); if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -868,7 +868,7 @@ public void testUpdateOnlyDocument() throws Exception { Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion()); + Assert.assertEquals(record.getSchemaVersion() + 1L, (long) record2.getSchemaVersion()); if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1016,7 +1016,7 @@ public void testDeleteSchemaRecord() throws Exception { // create should return conflict SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(schemaId); - schemaRecord.setVersion(1l); + schemaRecord.setVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); ObjectMapper mapper = new ObjectMapper(); @@ -1039,7 +1039,7 @@ public void testDeleteSchemaRecord() throws Exception { private void ingestSchemaRecord(String schemaId) throws Exception { SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(schemaId); - schemaRecord.setVersion(1l); + schemaRecord.setVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); ObjectMapper mapper = new ObjectMapper(); @@ -1090,7 +1090,7 @@ private void ingestSchemaRecord() throws Exception { SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(dataResource.getId() + "/1"); - schemaRecord.setVersion(1l); + schemaRecord.setVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); schemaRecord.setSchemaDocumentUri(ci.getContentUri()); schemaRecord.setDocumentHash(ci.getHash()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index cd8e2e1a..2f4225f7 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -833,7 +833,7 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/metadata/"). file(recordFile). @@ -861,7 +861,7 @@ public void testCreateTwoVersions() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); record.setRelatedResource(RELATED_RESOURCE_2); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -870,7 +870,7 @@ public void testCreateTwoVersions() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); } @Test @@ -972,18 +972,18 @@ public void testFindRecordsOfMultipleVersionsBySchemaId() throws Exception { ObjectMapper map = new ObjectMapper(); String[] multipleVersions = {"1", "2", "3"}; // Ingest 1st version of document. - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 1l, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 1L, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); MvcResult res = this.mockMvc.perform(get("/api/v1/metadata/").param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); Assert.assertEquals(1, result.length); // Ingest 2nd version of document - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 2l, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 2L, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get("/api/v1/metadata/").param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); Assert.assertEquals(2, result.length); // Ingest 3rd version of document - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 3l, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 3L, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get("/api/v1/metadata/").param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); @@ -1207,7 +1207,7 @@ public void testUpdateRecord() throws Exception { Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1232,7 +1232,7 @@ public void testUpdateRecordWithWrongVersion() throws Exception { ObjectMapper mapper = new ObjectMapper(); MetadataRecord record = mapper.readValue(body, MetadataRecord.class); - record.setRecordVersion(0l); + record.setRecordVersion(0L); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); @@ -1247,7 +1247,7 @@ public void testUpdateRecordWithWrongVersion() throws Exception { Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals(Long.valueOf(2l), record2.getRecordVersion());// version should be 2 + Assert.assertEquals(Long.valueOf(2L), record2.getRecordVersion());// version should be 2 if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1299,7 +1299,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(SchemaRegistryControllerTest.isSameSetOfAclEntries(record.getAcl(), record2.getAcl())); Assert.assertFalse(SchemaRegistryControllerTest.isSameSetOfAclEntries(oldRecord.getAcl(), record2.getAcl())); @@ -1353,7 +1353,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1L);// version should be 1 higher if (record2.getAcl() != null) { Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); } @@ -1363,12 +1363,12 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { @Test public void testUpdateRecordWithSameDocument() throws Exception { ObjectMapper mapper = new ObjectMapper(); - MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1L, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); MetadataRecord record1 = mapper.readValue(body, MetadataRecord.class); // Update without any changes. - result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1L, "document", DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); @@ -1378,17 +1378,17 @@ public void testUpdateRecordWithSameDocument() throws Exception { @Test public void testUpdateRecordWithSmallChangesInDocument() throws Exception { ObjectMapper mapper = new ObjectMapper(); - MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, SCHEMA_ID, 1L, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); MetadataRecord record1 = mapper.readValue(body, MetadataRecord.class); // Update without any changes. - result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1L, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); Assert.assertNotEquals("Version should change!", record1.getRecordVersion(), record2.getRecordVersion()); - Assert.assertEquals("Version should incremented!", (long) record1.getRecordVersion(), (long) (record2.getRecordVersion() - 1l)); + Assert.assertEquals("Version should incremented!", (long) record1.getRecordVersion(), record2.getRecordVersion() - 1L); } @Test @@ -1446,7 +1446,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1475,7 +1475,7 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1588,13 +1588,13 @@ public void testUpdateRecordWithInvalidSetting4Json() throws Exception { String alternativeSchemaId = "testupdate"; CreateSchemaUtil.ingestXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V1, schemaConfig.getJwtSecret()); CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V2, schemaConfig.getJwtSecret(), true, status().isOk()); - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, alternativeSchemaId, 2l, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, alternativeSchemaId, 2L, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); // Change only version of schema to a version which is not valid. - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, alternativeSchemaId, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, alternativeSchemaId, 1L, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); // Change to a nonexistent version of schema. CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isNotFound()); // Change to another schema - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1L, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); } @Test @@ -1630,13 +1630,13 @@ public void testUpdateRecordWithLicense() throws Exception { Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); Assert.assertNotNull(record2.getLicenseUri()); - Assert.assertTrue(record2.getLicenseUri().equals(APACHE_2_LICENSE)); + Assert.assertEquals(APACHE_2_LICENSE, record2.getLicenseUri()); // Check for new metadata document. result = this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId)). andDo(print()). @@ -1682,7 +1682,7 @@ record = mapper.readValue(body, MetadataRecord.class); } Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); Assert.assertNotNull(record3.getLicenseUri()); - Assert.assertTrue(record3.getLicenseUri().equals(MIT_LICENSE)); + Assert.assertEquals(MIT_LICENSE, record3.getLicenseUri()); result = this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId). header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). andDo(print()). @@ -1835,7 +1835,7 @@ public void testIssue52() throws Exception { .header(HttpHeaders.ACCEPT, "application/json")) .andDo(print()) .andExpect(status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))) + .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(version))) .andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + "\"")); // check for higher versions which should be not available @@ -1846,7 +1846,7 @@ public void testIssue52() throws Exception { version++; metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1856,7 +1856,7 @@ public void testIssue52() throws Exception { version++; metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1865,7 +1865,7 @@ public void testIssue52() throws Exception { metadataRecordId = ingestMetadataRecordWithVersion(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); // check for higher versions which should be not available (if version > 2) this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isOk()); @@ -1875,7 +1875,7 @@ public void testIssue52() throws Exception { version++; metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get("/api/v1/metadata/").param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); result = this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4Json.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4Json.java index 708f9310..78954f26 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4Json.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestAccessWithAuthenticationEnabled4Json.java @@ -420,7 +420,7 @@ private void ingestMetadataRecord4UnregisteredUsers(String schemaId) throws Exce private void ingestSchemaRecord(String schemaId) throws Exception { MetadataSchemaRecord schemaRecord = new MetadataSchemaRecord(); schemaRecord.setSchemaId(schemaId); - schemaRecord.setSchemaVersion(1l); + schemaRecord.setSchemaVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); Set<AclEntry> aclEntries = new HashSet<>(); aclEntries.add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 2308c8ed..2d0d27a2 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -912,7 +912,7 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - Assert.assertEquals(1l, Long.parseLong(result.getVersion())); + Assert.assertEquals(1L, Long.parseLong(result.getVersion())); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). file(recordFile). @@ -937,7 +937,7 @@ public void testCreateTwoVersions() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - Assert.assertEquals(1l, Long.parseLong(result.getVersion())); + Assert.assertEquals(1L, Long.parseLong(result.getVersion())); DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setValue(RELATED_RESOURCE_2.toString()); record.getAlternateIdentifiers().clear(); @@ -948,7 +948,7 @@ public void testCreateTwoVersions() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - Assert.assertEquals(1l, Long.parseLong(result.getVersion())); + Assert.assertEquals(1L, Long.parseLong(result.getVersion())); } @Test @@ -1084,18 +1084,18 @@ public void testFindRecordsOfMultipleVersionsBySchemaId() throws Exception { ObjectMapper map = new ObjectMapper(); String[] multipleVersions = {"1", "2", "3"}; // Ingest 1st version of document. - CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema1, 1l, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema1, 1L, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(1, result.length); // Ingest 2nd version of document - CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema2, 2l, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema2, 2L, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals(2, result.length); // Ingest 3rd version of document - CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema3, 3l, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, schema3, 3L, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get(API_METADATA_PATH).param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); @@ -1463,7 +1463,7 @@ public void testUpdateRecord() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Check ContentInformation of second version @@ -1480,7 +1480,7 @@ public void testUpdateRecord() throws Exception { Assert.assertNotEquals(contentInformation1, contentInformation2); Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); - Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); + Assert.assertEquals(contentInformation1.getVersion() + 1, (long)contentInformation2.getVersion()); Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); @@ -1526,7 +1526,7 @@ public void testUpdateRecordWithWrongVersion() throws Exception { SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); // Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record.getRelatedIdentifiers(), record2.getRelatedIdentifiers()); - Assert.assertEquals(2l, Long.parseLong(record2.getVersion()));// version should be 2 + Assert.assertEquals(2L, Long.parseLong(record2.getVersion()));// version should be 2 // if (record.getAcl() != null) { // Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); // } @@ -1591,7 +1591,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(oldRecord.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(oldRecord), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(oldRecord.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(oldRecord.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(oldRecord.getAcls(), record2.getAcls()); Assert.assertTrue(oldRecord.getLastUpdate().isBefore(record2.getLastUpdate())); String locationUri2 = result.getResponse().getHeader("Location"); @@ -1659,7 +1659,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record2.getRelatedIdentifiers(), record2.getRelatedIdentifiers()); - Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); // Check ContentInformation of second version @@ -1675,7 +1675,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { Assert.assertNotEquals(contentInformation1, contentInformation2); Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); - Assert.assertEquals((long)(contentInformation1.getVersion() + 1), contentInformation2.getVersion().longValue()); + Assert.assertEquals(contentInformation1.getVersion() + 1, contentInformation2.getVersion().longValue()); Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); Assert.assertEquals(DC_DOCUMENT.length(), contentInformation1.getSize()); @@ -1686,12 +1686,12 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { public void testUpdateRecordWithSameDocument() throws Exception { String id = "testUpdateRecordWithSameDocument"; ObjectMapper mapper = new ObjectMapper(); - MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, id, DC_DOCUMENT, schemaConfig.getJwtSecret()); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1L, id, DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); DataResource record1 = mapper.readValue(body, DataResource.class); // Update without any changes. - result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, id, DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1L, id, DC_DOCUMENT, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); @@ -1701,17 +1701,17 @@ public void testUpdateRecordWithSameDocument() throws Exception { @Test public void testUpdateRecordWithSmallChangesInDocument() throws Exception { ObjectMapper mapper = new ObjectMapper(); - MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); + MvcResult result = CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1L, "document", DC_DOCUMENT, schemaConfig.getJwtSecret()); String body = result.getResponse().getContentAsString(); DataResource record1 = mapper.readValue(body, DataResource.class); // Update without any changes. - result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); + result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1L, "document", DC_DOCUMENT_SMALL_CHANGE, schemaConfig.getJwtSecret(), true, status().isOk()); body = result.getResponse().getContentAsString(); DataResource record2 = mapper.readValue(body, DataResource.class); Assert.assertNotEquals("Version should change!", record1.getVersion(), record2.getVersion()); - Assert.assertEquals("Version should incremented!", Long.parseLong(record1.getVersion()), Long.parseLong(record2.getVersion()) - 1l); + Assert.assertEquals("Version should incremented!", Long.parseLong(record1.getVersion()), Long.parseLong(record2.getVersion()) - 1L); } @Test @@ -1774,7 +1774,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); @@ -1785,7 +1785,7 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { String metadataRecordId = "testUpdateRecordWithoutRecord4Json"; // Update only Json document CreateSchemaUtil.ingestOrUpdateJsonSchemaRecordV2(mockMvc, JSON_SCHEMA_ID, JSON_SCHEMA, schemaConfig.getJwtSecret(), false, status().isCreated()); - MvcResult result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, JSON_SCHEMA_ID, 1l, metadataRecordId, JSON_DOCUMENT_VERSION_1, schemaConfig.getJwtSecret(), false, status().isCreated()); + MvcResult result = CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, JSON_SCHEMA_ID, 1L, metadataRecordId, JSON_DOCUMENT_VERSION_1, schemaConfig.getJwtSecret(), false, status().isCreated()); String locationUri = result.getResponse().getHeader("Location"); String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); @@ -1808,7 +1808,7 @@ public void testUpdateRecordWithoutRecord4Json() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); @@ -1974,13 +1974,13 @@ public void testUpdateRecordWithInvalidSetting4Json() throws Exception { String alternativeSchemaId = "testupdate"; CreateSchemaUtil.ingestXmlSchemaRecordV2(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V1, schemaConfig.getJwtSecret()); CreateSchemaUtil.ingestOrUpdateXmlSchemaRecordV2(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V2, schemaConfig.getJwtSecret(), true, status().isOk()); - CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 2l, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 2L, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); // Change only version of schema to a version which is not valid. - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 1L, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); // Change to a nonexistent version of schema. CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isBadRequest()); // Change to another schema - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1l, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1L, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); } @Test @@ -2023,14 +2023,14 @@ public void testUpdateRecordWithLicense() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); Assert.assertTrue("No license available", record.getRights().isEmpty()); Assert.assertNotNull(record2.getRights()); - Assert.assertTrue(!record2.getRights().isEmpty()); - Assert.assertTrue(record2.getRights().iterator().next().getSchemeUri().equals(APACHE_2_LICENSE)); + Assert.assertFalse(record2.getRights().isEmpty()); + Assert.assertEquals(APACHE_2_LICENSE, record2.getRights().iterator().next().getSchemeUri()); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)). andDo(print()). @@ -2124,14 +2124,14 @@ public void testUpdateRecordWithLicenseNull() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); Assert.assertTrue("No license available", record.getRights().isEmpty()); Assert.assertNotNull(record2.getRights()); - Assert.assertTrue(!record2.getRights().isEmpty()); - Assert.assertTrue(record2.getRights().iterator().next().getSchemeUri().equals(APACHE_2_LICENSE)); + Assert.assertFalse(record2.getRights().isEmpty()); + Assert.assertEquals(APACHE_2_LICENSE, record2.getRights().iterator().next().getSchemeUri()); // Check for new metadata document. result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)). andDo(print()). @@ -2305,7 +2305,7 @@ public void testIssue52() throws Exception { .header(HttpHeaders.ACCEPT, "application/json")) .andDo(print()) .andExpect(status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))) + .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(version))) .andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + "\"")); // check for higher versions which should be not available @@ -2316,7 +2316,7 @@ public void testIssue52() throws Exception { version++; metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -2326,7 +2326,7 @@ public void testIssue52() throws Exception { version++; metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -2335,7 +2335,7 @@ public void testIssue52() throws Exception { metadataRecordId = ingestMetadataRecordWithVersion(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); // check for higher versions which should be not available (if version > 2) this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId).param("version", "2")).andDo(print()).andExpect(status().isOk()); @@ -2345,7 +2345,7 @@ public void testIssue52() throws Exception { version++; metadataRecordId = ingestNewMetadataRecord(metadataRecordId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH).param("id", metadataRecordId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + RELATED_RESOURCE_STRING + version + " is not available", result.getResponse().getContentAsString().contains("\"" + RELATED_RESOURCE_STRING + version + "\"")); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java index 489b4bdb..4c8aedb4 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java @@ -775,7 +775,7 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { andReturn(); MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/metadata/"). file(recordFile). @@ -811,7 +811,7 @@ public void testCreateTwoVersions() throws Exception { andReturn(); MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); record.setRelatedResource(RELATED_RESOURCE_2); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -824,7 +824,7 @@ public void testCreateTwoVersions() throws Exception { andReturn(); result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); } @Test @@ -1084,7 +1084,7 @@ public void testUpdateRecord() throws Exception { Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1144,7 +1144,7 @@ public void testUpdateAclOnly() throws Exception { if (record.getAcl() != null) { Assert.assertTrue(record2.getAcl().containsAll(oldRecord.getAcl())); // There should be an additional entry - Assert.assertTrue(oldRecord.getAcl().size() + 1 == record2.getAcl().size()); + Assert.assertEquals(oldRecord.getAcl().size() + 1, record2.getAcl().size()); } Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); } @@ -1330,7 +1330,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1L);// version should be 1 higher if (record2.getAcl() != null) { Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); } @@ -1424,7 +1424,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1690,7 +1690,7 @@ public void testGetAllVersionsOfRecord() throws Exception { Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java index 9c45e1ce..495bbf6d 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -817,7 +817,7 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { andReturn(); DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - Assert.assertEquals(Long.valueOf(1l).toString(), result.getVersion()); + Assert.assertEquals(Long.valueOf(1L).toString(), result.getVersion()); record = SchemaRegistryControllerTestV2.createDataResource4Document(id + "_2", schemaId); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -850,7 +850,7 @@ public void testCreateTwoVersions() throws Exception { andReturn(); DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - Assert.assertEquals(Long.valueOf(1l).toString(), result.getVersion()); + Assert.assertEquals(Long.valueOf(1L).toString(), result.getVersion()); RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(record); Set<RelatedIdentifier> relatedIdentifiers = record.getRelatedIdentifiers(); @@ -876,7 +876,7 @@ public void testCreateTwoVersions() throws Exception { andReturn(); result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - Assert.assertEquals(Long.valueOf(1l).toString(), result.getVersion()); + Assert.assertEquals(Long.valueOf(1L).toString(), result.getVersion()); } @Test @@ -1152,7 +1152,7 @@ public void testUpdateRecord() throws Exception { // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); @@ -1169,7 +1169,7 @@ public void testUpdateRecord() throws Exception { Assert.assertNotEquals(contentInformation1, contentInformation2); Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); - Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); + Assert.assertEquals(contentInformation1.getVersion() + 1, (long)contentInformation2.getVersion()); Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); @@ -1465,7 +1465,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { DataResource record3 = mapper.readValue(body, DataResource.class); SchemaRegistryControllerTestV2.validateCreateDates(record2.getDates(), record3.getDates()); SchemaRegistryControllerTestV2.validateRelatedIdentifierSets(record2.getRelatedIdentifiers(), record2.getRelatedIdentifiers()); - Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record2.getVersion()), Long.parseLong(record3.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); } @@ -1566,7 +1566,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { DataResource record2 = mapper.readValue(body, DataResource.class); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); // Check ContentInformation of second version @@ -1582,7 +1582,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { Assert.assertNotEquals(contentInformation1, contentInformation2); Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); - Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); + Assert.assertEquals(contentInformation1.getVersion() + 1, (long)contentInformation2.getVersion()); Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); @@ -1899,7 +1899,7 @@ public void testGetAllVersionsOfRecord() throws Exception { DataResource record2 = mapper.readValue(body, DataResource.class); SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); - Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1l);// version should be 1 higher + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); @@ -2032,7 +2032,7 @@ private String getSchemaUrl(String schemaId) throws Exception { param("version", "1")). andDo(print()).andExpect(status().isOk()). andReturn(); - String result = res.getRequest().getRequestURL().toString() + "?version=1"; + String result = res.getRequest().getRequestURL() + "?version=1"; System.out.println("result " + result); return result.replaceFirst("8080", "41428"); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java index 30bf8280..48e20072 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java @@ -635,7 +635,7 @@ public void testCreateTwoVersionsOfSameRecord() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); res = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/metadata/"). file(recordFile). @@ -663,7 +663,7 @@ public void testCreateTwoVersions() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); record.setRelatedResource(RELATED_RESOURCE_2); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -672,7 +672,7 @@ public void testCreateTwoVersions() throws Exception { file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); result = mapper.readValue(res.getResponse().getContentAsString(), MetadataRecord.class); - Assert.assertEquals(Long.valueOf(1l), result.getRecordVersion()); + Assert.assertEquals(Long.valueOf(1L), result.getRecordVersion()); } @Test @@ -739,18 +739,18 @@ public void testFindRecordsOfMultipleVersionsBySchemaId() throws Exception { ObjectMapper map = new ObjectMapper(); String[] multipleVersions = {"1", "2", "3"}; // Ingest 1st version of document. - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 1l, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 1L, multipleVersions[0], XML_DOCUMENT_V1, metadataConfig.getJwtSecret()); MvcResult res = this.mockMvc.perform(get("/api/v1/metadata/").param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); Assert.assertEquals(1, result.length); // Ingest 2nd version of document - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 2l, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 2L, multipleVersions[1], XML_DOCUMENT_V2, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get("/api/v1/metadata/").param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); Assert.assertEquals(2, result.length); // Ingest 3rd version of document - CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 3l, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, schemaId, 3L, multipleVersions[2], XML_DOCUMENT_V3, metadataConfig.getJwtSecret()); res = this.mockMvc.perform(get("/api/v1/metadata/").param("schemaId", schemaId)).andDo(print()).andExpect(status().isOk()).andReturn(); result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); @@ -850,7 +850,7 @@ public void testUpdateRecord() throws Exception { Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash());//mime type was changed by update Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -903,7 +903,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); Assert.assertEquals(record2.getMetadataDocumentUri().replace("version=1", "version=2"), record3.getMetadataDocumentUri()); Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record2.getRecordVersion(), record3.getRecordVersion() - 1L);// version should be 1 higher if (record2.getAcl() != null) { Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); } @@ -965,7 +965,7 @@ public void testUpdateRecordWithoutRecord() throws Exception { Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); - Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1l);// version should be 1 higher + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index a3deb4e2..ec0eb771 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -540,7 +540,7 @@ public void testCreateTwoVersionsOfSchemaRecord() throws Exception { file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); MetadataSchemaRecord result = mapper.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord.class); - Assert.assertEquals(result.getSchemaVersion(), Long.valueOf(1l)); + Assert.assertEquals(result.getSchemaVersion(), Long.valueOf(1L)); // Can't create same resource twice -> Conflict res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). @@ -977,7 +977,7 @@ public void testUpdateRecordAndDocument() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + Assert.assertEquals(record.getSchemaVersion() + 1L, (long) record2.getSchemaVersion());//version is not changing for metadata update if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1030,7 +1030,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + Assert.assertEquals(record.getSchemaVersion() + 1L, (long) record2.getSchemaVersion());//version is not changing for metadata update if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1062,7 +1062,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); Assert.assertEquals(record2.getSchemaHash(), record3.getSchemaHash()); Assert.assertEquals(record2.getSchemaId(), record3.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + Assert.assertEquals(record.getSchemaVersion() + 1L, (long) record2.getSchemaVersion());//version is not changing for metadata update if (record.getAcl() != null) { Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); } @@ -1079,7 +1079,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { ObjectMapper mapper = new ObjectMapper(); MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); - record.setSchemaVersion(0l); + record.setSchemaVersion(0L); String mimeTypeBefore = record.getMimeType(); record.setMimeType(MediaType.APPLICATION_JSON.toString()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1096,7 +1096,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals(2l, (long) record2.getSchemaVersion());//version is not changing for metadata update + Assert.assertEquals(2L, (long) record2.getSchemaVersion());//version is not changing for metadata update if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1140,7 +1140,7 @@ public void testUpdateOnlyDocument() throws Exception { testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getSchemaId(), record2.getSchemaId()); - Assert.assertEquals((long) record.getSchemaVersion() + 1l, (long) record2.getSchemaVersion());//version is not changing for metadata update + Assert.assertEquals(record.getSchemaVersion() + 1L, (long) record2.getSchemaVersion());//version is not changing for metadata update if (record.getAcl() != null) { Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); } @@ -1157,7 +1157,7 @@ public void testUpdateRecordWithSmallChangesInDocument() throws Exception { String schemaId = "updateRecordWithSmallChanges".toLowerCase(Locale.getDefault()); MetadataSchemaRecord schemaRecord = new MetadataSchemaRecord(); schemaRecord.setSchemaId(schemaId); - schemaRecord.setSchemaVersion(1l); + schemaRecord.setSchemaVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); ObjectMapper mapper = new ObjectMapper(); @@ -1328,7 +1328,7 @@ public void testDeleteSchemaRecord() throws Exception { // create should return conflict SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(schemaId); - schemaRecord.setVersion(1l); + schemaRecord.setVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(schemaRecord).getBytes()); @@ -1387,7 +1387,7 @@ public void testGetAllVersionsOfRecord() throws Exception { xmlDocument = XML_DOCUMENT_V3.getBytes(); break; default: - Assert.assertTrue("Unknown document: '" + document + "'", false); + Assert.fail("Unknown document: '" + document + "'"); } ResultMatcher resultMatcher = null; @@ -1414,7 +1414,7 @@ public void testGetAllVersionsOfRecord() throws Exception { xmlDocument = XML_DOCUMENT_V3.getBytes(); break; default: - Assert.assertTrue("Unknown document: '" + document + "'", false); + Assert.fail("Unknown document: '" + document + "'"); } ResultMatcher resultMatcher = status().isNoContent(); @@ -1462,7 +1462,7 @@ public void testIssue52() throws Exception { ingestSchemaWithVersion(schemaId, version); // Test get record with one version // Read all versions - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(version))).andReturn(); Assert.assertTrue("Reference to '" + COMMENT + version + "' is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1472,7 +1472,7 @@ public void testIssue52() throws Exception { version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1482,7 +1482,7 @@ public void testIssue52() throws Exception { version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1491,7 +1491,7 @@ public void testIssue52() throws Exception { ingestSchemaWithVersion(schemaId, 2); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available (if version > 2) this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()); @@ -1501,7 +1501,7 @@ public void testIssue52() throws Exception { version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -1631,7 +1631,7 @@ private void ingestSchemaRecord(String schemaId) throws Exception { schemaRecord.setLabel(LABEL); schemaRecord.setDefinition(DEFINITION); schemaRecord.setComment(COMMENT); - schemaRecord.setSchemaVersion(1l); + schemaRecord.setSchemaVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); ObjectMapper mapper = new ObjectMapper(); @@ -1682,7 +1682,7 @@ private void ingestSchemaRecord() throws Exception { SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(dataResource.getId() + "/1"); - schemaRecord.setVersion(1l); + schemaRecord.setVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.valueOf(dataResource.getFormats().iterator().next())); schemaRecord.setSchemaDocumentUri(ci.getContentUri()); schemaRecord.setDocumentHash(ci.getHash()); @@ -1722,7 +1722,7 @@ private void ingestSchemaWithVersion(String schemaId, long version) throws Excep schemaContent = SCHEMA_V3.getBytes(); break; default: - Assert.assertTrue("Unknown version: '" + version + "'", false); + Assert.fail("Unknown version: '" + version + "'"); } MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", schemaContent); MvcResult result; diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index cd126893..307c10ac 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -564,7 +564,7 @@ public void testCreateTwoVersionsOfSchemaRecord() throws Exception { file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - Assert.assertEquals(result.getVersion(), Long.toString(1l)); + Assert.assertEquals(result.getVersion(), Long.toString(1L)); // Can't create same resource twice -> Conflict res = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). file(recordFile). @@ -855,7 +855,7 @@ public void testUpdateRecord() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getId(), record2.getId()); - Assert.assertEquals((long) Long.parseLong(record.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); // if (record.getAcl() != null) { // Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); @@ -902,7 +902,7 @@ public void testUpdateRecordRemovingLabel() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getId(), record2.getId()); - Assert.assertEquals((long) Long.parseLong(record.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); // if (recUpdateord.getAcl() != null) { // Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); @@ -961,7 +961,7 @@ public void testUpdateRecordIgnoreACL() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getId(), record2.getId()); - Assert.assertEquals((long) Long.parseLong(record.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()));//version is not changing for metadata update if (record.getAcls() != null) { Assert.assertTrue(isSameSetOfAclEntries(record.getAcls(), record2.getAcls())); Assert.assertFalse(isSameSetOfAclEntries(oldRecord.getAcls(), record.getAcls())); @@ -1134,7 +1134,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); // Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getId(), record2.getId()); - Assert.assertEquals((long) Long.parseLong(record.getVersion()) + 1l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(Long.parseLong(record.getVersion()) + 1L, Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); // if (record.getAcl() != null) { // Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); @@ -1185,7 +1185,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); - record.setVersion(Long.toString(0l)); + record.setVersion(Long.toString(0L)); String mimeTypeBefore = record.getFormats().iterator().next(); record.getFormats().clear(); record.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); @@ -1200,7 +1200,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { Assert.assertNotEquals(mimeTypeBefore, record2.getFormats().iterator().next());//mime type was changed by update validateCreateDates(record.getDates(), record2.getDates()); Assert.assertEquals(record.getId(), record2.getId()); - Assert.assertEquals(2l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(2L, Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); validateUpdateDates(record.getDates(), record2.getDates()); // Test also document for update @@ -1253,7 +1253,7 @@ public void testUpdateOnlyDocument() throws Exception { testForNextVersion(record.getVersion(), record2.getVersion()); // Assert.assertNotEquals(record.getSchemaHash(), record2.getSchemaHash()); Assert.assertEquals(record.getId(), record2.getId()); - Assert.assertEquals((long) Long.parseLong(record.getVersion()) + 1l, (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(Long.parseLong(record.getVersion()) + 1L, Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); // if (record.getAcl() != null) { // Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); @@ -1278,7 +1278,7 @@ public void testUpdateOnlyDocument() throws Exception { Assert.assertNotEquals(contentInformation1, contentInformation2); Assert.assertNotEquals(contentInformation1.getContentUri(), contentInformation2.getContentUri()); Assert.assertNotEquals(contentInformation1.getVersion(), contentInformation2.getVersion()); - Assert.assertEquals((long)(contentInformation1.getVersion() + 1), (long)contentInformation2.getVersion()); + Assert.assertEquals(contentInformation1.getVersion() + 1, (long)contentInformation2.getVersion()); Assert.assertNotEquals(contentInformation1.getHash(), contentInformation2.getHash()); Assert.assertNotEquals(contentInformation1.getSize(), contentInformation2.getSize()); } @@ -1339,7 +1339,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { validateDates(record1.getDates(), record2.getDates()); // Version shouldn't be updated Assert.assertEquals(record1.getId(), record2.getId()); - Assert.assertEquals((long) Long.parseLong(record1.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(Long.parseLong(record1.getVersion()), Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); // if (record.getAcl() != null) { // Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); @@ -1416,7 +1416,7 @@ public void testCreateSchemaRecordWithUpdateWithoutChanges() throws Exception { // Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); // Version shouldn't be updated Assert.assertEquals(record1.getId(), record2.getId()); - Assert.assertEquals((long) Long.parseLong(record1.getVersion()), (long) Long.parseLong(record2.getVersion()));//version is not changing for metadata update + Assert.assertEquals(Long.parseLong(record1.getVersion()), Long.parseLong(record2.getVersion()));//version is not changing for metadata update validateSets(record.getAcls(), record2.getAcls()); // if (record.getAcl() != null) { // Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); @@ -1505,7 +1505,7 @@ public void testGetAllVersionsOfRecord() throws Exception { xmlDocument = XML_DOCUMENT_V3.getBytes(); break; default: - Assert.assertTrue("Unknown document: '" + document + "'", false); + Assert.fail("Unknown document: '" + document + "'"); } ResultMatcher resultMatcher = null; @@ -1532,7 +1532,7 @@ public void testGetAllVersionsOfRecord() throws Exception { xmlDocument = XML_DOCUMENT_V3.getBytes(); break; default: - Assert.assertTrue("Unknown document: '" + document + "'", false); + Assert.fail("Unknown document: '" + document + "'"); } ResultMatcher resultMatcher = status().isNoContent(); @@ -1580,7 +1580,7 @@ public void testIssue52() throws Exception { ingestSchemaWithVersion(schemaId, version); // Test get record with one version // Read all versions - MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) version))).andReturn(); + MvcResult result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(version))).andReturn(); Assert.assertTrue("Reference to '" + COMMENT + version + "' is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1590,7 +1590,7 @@ public void testIssue52() throws Exception { version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1600,7 +1600,7 @@ public void testIssue52() throws Exception { version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 1))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(1))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isNotFound()); @@ -1609,7 +1609,7 @@ public void testIssue52() throws Exception { ingestSchemaWithVersion(schemaId, 2); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); // check for higher versions which should be not available (if version > 2) this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "2")).andDo(print()).andExpect(status().isOk()); @@ -1619,7 +1619,7 @@ public void testIssue52() throws Exception { version++; ingestNewSchemaRecord(schemaId, version); // Read all versions (should be still one version) - result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize((int) 2))).andReturn(); + result = this.mockMvc.perform(get(API_SCHEMA_PATH).param("schemaId", schemaId).header(HttpHeaders.ACCEPT, "application/json")).andDo(print()).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2))).andReturn(); Assert.assertTrue("Reference to " + COMMENT + version + " is not available", result.getResponse().getContentAsString().contains("\"" + COMMENT + version + "\"")); result = this.mockMvc.perform(get(API_SCHEMA_PATH + schemaId).param("version", "1")).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -1809,7 +1809,7 @@ private void ingestSchemaRecord() throws Exception { SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(dataResource.getId() + "/1"); - schemaRecord.setVersion(1l); + schemaRecord.setVersion(1L); schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.XML); schemaRecord.setSchemaDocumentUri(ci.getContentUri()); schemaRecord.setDocumentHash(ci.getHash()); @@ -1842,7 +1842,7 @@ private void ingestSchemaWithVersion(String schemaId, long version) throws Excep schemaContent = SCHEMA_V3.getBytes(); break; default: - Assert.assertTrue("Unknown version: '" + version + "'", false); + Assert.fail("Unknown version: '" + version + "'"); } MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", schemaContent); MvcResult result; diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/DownloadUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/DownloadUtilTest.java index 75c50758..3fb98a01 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/DownloadUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/DownloadUtilTest.java @@ -68,9 +68,9 @@ public void testDownloadResource() throws URISyntaxException { URI resourceURL = new URI("https://www.example.org"); Optional<Path> result = DownloadUtil.downloadResource(resourceURL); assertTrue("No file available!", result.isPresent()); - assertTrue("File '" + result.get().toString() + "' doesn't exist!", result.get().toFile().exists()); - assertTrue("Wrong suffix for file '" + result.get().toString() + "'!", result.get().toString().endsWith(DownloadUtil.DEFAULT_SUFFIX)); - assertTrue("Can't delete file '" + result.get().toString() + "'!", result.get().toFile().delete()); + assertTrue("File '" + result.get() + "' doesn't exist!", result.get().toFile().exists()); + assertTrue("Wrong suffix for file '" + result.get() + "'!", result.get().toString().endsWith(DownloadUtil.DEFAULT_SUFFIX)); + assertTrue("Can't delete file '" + result.get() + "'!", result.get().toFile().delete()); } /** @@ -83,9 +83,9 @@ public void testDownloadResourceWithPath() throws URISyntaxException { URI resourceURL = new URI("https://www.example.org/index.html"); Optional<Path> result = DownloadUtil.downloadResource(resourceURL); assertTrue("No file available!", result.isPresent()); - assertTrue("File '" + result.get().toString() + "' doesn't exist!", result.get().toFile().exists()); - assertTrue("Wrong suffix for file '" + result.get().toString() + "'!", result.get().toString().endsWith(".html")); - assertTrue("Can't delete file '" + result.get().toString() + "'!", result.get().toFile().delete()); + assertTrue("File '" + result.get() + "' doesn't exist!", result.get().toFile().exists()); + assertTrue("Wrong suffix for file '" + result.get() + "'!", result.get().toString().endsWith(".html")); + assertTrue("Can't delete file '" + result.get() + "'!", result.get().toFile().delete()); } /** @@ -98,7 +98,7 @@ public void testDownloadInvalidResource() throws URISyntaxException { try { URI resourceURL = new URI("https://invalidhttpaddress.de"); Optional<Path> result = DownloadUtil.downloadResource(resourceURL); - assertTrue(false); + fail(); } catch (CustomInternalServerError ie) { assertTrue(true); assertTrue(ie.getMessage().contains("Error downloading resource")); @@ -112,13 +112,13 @@ public void testDownloadInvalidResource() throws URISyntaxException { public void testDownloadLocalResource() throws URISyntaxException, IOException { System.out.println("testDownloadLocalResource"); File srcFile = new File("src/test/resources/examples/simple.json"); - assertTrue("File doesn't exist: " + srcFile.toString(), srcFile.exists()); + assertTrue("File doesn't exist: " + srcFile, srcFile.exists()); URI resourceURL = srcFile.toURI(); Optional<Path> result = DownloadUtil.downloadResource(resourceURL); assertTrue("No file available!", result.isPresent()); - assertTrue("File '" + result.get().toString() + "' doesn't exist!", result.get().toFile().exists()); - assertTrue("Wrong suffix for file '" + result.get().toString() + "'!", result.get().toString().endsWith(".json")); - assertTrue("Can't delete file '" + result.get().toString() + "'!", result.get().toFile().delete()); + assertTrue("File '" + result.get() + "' doesn't exist!", result.get().toFile().exists()); + assertTrue("Wrong suffix for file '" + result.get() + "'!", result.get().toString().endsWith(".json")); + assertTrue("Can't delete file '" + result.get() + "'!", result.get().toFile().delete()); } /** @@ -128,15 +128,15 @@ public void testDownloadLocalResource() throws URISyntaxException, IOException { public void testDownloadLocalJsonFileWithoutSuffix() throws URISyntaxException, IOException { System.out.println("testDownloadLocalResource"); File srcFile = new File("src/test/resources/examples/simple.json"); - assertTrue("File doesn't exist: " + srcFile.toString(), srcFile.exists()); + assertTrue("File doesn't exist: " + srcFile, srcFile.exists()); Path createTempFile = DownloadUtil.createTempFile(null, "nosuffix"); Files.copy(srcFile, createTempFile.toFile()); Optional<Path> result = DownloadUtil.downloadResource(createTempFile.toUri()); assertTrue("No file available!", result.isPresent()); - assertTrue("File '" + result.get().toString() + "' doesn't exist!", result.get().toFile().exists()); - assertTrue("Wrong suffix for file '" + result.get().toString() + "'!", result.get().toString().endsWith(".json")); - assertTrue("Can't delete file '" + result.get().toString() + "'!", result.get().toFile().delete()); - assertTrue("Can't delete file '" + createTempFile.toString() + "'!", createTempFile.toFile().delete()); + assertTrue("File '" + result.get() + "' doesn't exist!", result.get().toFile().exists()); + assertTrue("Wrong suffix for file '" + result.get() + "'!", result.get().toString().endsWith(".json")); + assertTrue("Can't delete file '" + result.get() + "'!", result.get().toFile().delete()); + assertTrue("Can't delete file '" + createTempFile + "'!", createTempFile.toFile().delete()); } /** @@ -146,15 +146,15 @@ public void testDownloadLocalJsonFileWithoutSuffix() throws URISyntaxException, public void testDownloadLocalXMLFileWithoutSuffix() throws URISyntaxException, IOException { System.out.println("testDownloadLocalResource"); File srcFile = new File("src/test/resources/examples/simple.xml"); - assertTrue("File doesn't exist: " + srcFile.toString(), srcFile.exists()); + assertTrue("File doesn't exist: " + srcFile, srcFile.exists()); Path createTempFile = DownloadUtil.createTempFile(null, "nosuffix"); Files.copy(srcFile, createTempFile.toFile()); Optional<Path> result = DownloadUtil.downloadResource(createTempFile.toUri()); assertTrue("No file available!", result.isPresent()); - assertTrue("File '" + result.get().toString() + "' doesn't exist!", result.get().toFile().exists()); - assertTrue("Wrong suffix for file '" + result.get().toString() + "'!", result.get().toString().endsWith(".xml")); - assertTrue("Can't delete file '" + result.get().toString() + "'!", result.get().toFile().delete()); - assertTrue("Can't delete file '" + createTempFile.toString() + "'!", createTempFile.toFile().delete()); + assertTrue("File '" + result.get() + "' doesn't exist!", result.get().toFile().exists()); + assertTrue("Wrong suffix for file '" + result.get() + "'!", result.get().toString().endsWith(".xml")); + assertTrue("Can't delete file '" + result.get() + "'!", result.get().toFile().delete()); + assertTrue("Can't delete file '" + createTempFile + "'!", createTempFile.toFile().delete()); } /** @@ -164,12 +164,12 @@ public void testDownloadLocalXMLFileWithoutSuffix() throws URISyntaxException, I public void testDownloadLocalResourceWithoutSuffix() throws URISyntaxException, IOException { System.out.println("testDownloadLocalResource"); File srcFile = new File("src/test/resources/examples/anyContentWithoutSuffix"); - assertTrue("File doesn't exist: " + srcFile.toString(), srcFile.exists()); + assertTrue("File doesn't exist: " + srcFile, srcFile.exists()); Optional<Path> result = DownloadUtil.downloadResource(srcFile.getAbsoluteFile().toURI()); assertTrue("No file available!", result.isPresent()); - assertTrue("File '" + result.get().toString() + "' doesn't exist!", result.get().toFile().exists()); - assertTrue("Wrong suffix for file '" + result.get().toString() + "'!", result.get().toString().endsWith(DownloadUtil.DEFAULT_SUFFIX)); - assertTrue("Can't delete file '" + result.get().toString() + "'!", result.get().toFile().delete()); + assertTrue("File '" + result.get() + "' doesn't exist!", result.get().toFile().exists()); + assertTrue("Wrong suffix for file '" + result.get() + "'!", result.get().toString().endsWith(DownloadUtil.DEFAULT_SUFFIX)); + assertTrue("Can't delete file '" + result.get() + "'!", result.get().toFile().delete()); } /** @@ -181,7 +181,7 @@ public void testDownloadInvalidLocalResource() throws URISyntaxException, IOExce try { URI resourceURL = new File("/invalid/path/to/local/file").toURI(); Optional<Path> result = DownloadUtil.downloadResource(resourceURL); - assertTrue(false); + fail(); } catch (CustomInternalServerError ie) { assertTrue(true); assertTrue(ie.getMessage().contains("Error downloading resource")); @@ -243,7 +243,7 @@ public void testCreateInvalidTempFile() { for (int index = 0; index < prefix.length; index++) { try { Path tmpPath = DownloadUtil.createTempFile(prefix[index], suffix[index]); - assertTrue(false); + fail(); } catch (CustomInternalServerError cise) { assertTrue(true); } @@ -259,7 +259,7 @@ public void testRemoveFile() { Path createTempFile = DownloadUtil.createTempFile("testRemoveDir", ".txt"); try { DownloadUtil.removeFile(createTempFile.getParent()); - assertTrue(false); + fail(); } catch (CustomInternalServerError ie) { assertTrue(ie.getMessage().contains("Error removing file")); } @@ -275,7 +275,7 @@ public void testRemoveFile() { public void testFixFileExtensionXml() throws IOException { System.out.println("testFixFileExtensionXml"); File srcFile = new File("src/test/resources/examples/simple.xml"); - assertTrue("File doesn't exist: " + srcFile.toString(), srcFile.exists()); + assertTrue("File doesn't exist: " + srcFile, srcFile.exists()); String[] extensions = {"nosuffix", "xml", ".xml ", ".xsd", ".json"}; // skip extensions with a '.' at start. No idea why at the moment. // works fine in testRemoveFile()!? @@ -288,7 +288,7 @@ public void testFixFileExtensionXml() throws IOException { Files.copy(srcFile, createTempFile.toFile()); Path result = DownloadUtil.fixFileExtension(createTempFile); assertTrue(result.toString().endsWith(".xml")); - assertTrue("Can't delete file '" + result.toString() + "'!", result.toFile().delete()); + assertTrue("Can't delete file '" + result + "'!", result.toFile().delete()); } } @@ -296,7 +296,7 @@ public void testFixFileExtensionXml() throws IOException { public void testFixFileExtensionJson() throws IOException { System.out.println("testFixFileExtensionJson"); File srcFile = new File("src/test/resources/examples/simple.json"); - assertTrue("File doesn't exist: " + srcFile.toString(), srcFile.exists()); + assertTrue("File doesn't exist: " + srcFile, srcFile.exists()); String[] extensions = {"nosuffix", "json", ".json ", ".xml"}; // skip extensions with a '.' at start. No idea why at the moment. // works fine in testRemoveFile()!? @@ -309,7 +309,7 @@ public void testFixFileExtensionJson() throws IOException { Files.copy(srcFile, createTempFile.toFile()); Path result = DownloadUtil.fixFileExtension(createTempFile); assertTrue(result.toString().endsWith(".json")); - assertTrue("Can't delete file '" + result.toString() + "'!", result.toFile().delete()); + assertTrue("Can't delete file '" + result + "'!", result.toFile().delete()); } } @@ -318,7 +318,7 @@ public void testFixFileExtensionJson() throws IOException { public void testFixFileExtensionUnknown() throws IOException { System.out.println("testFixFileExtensionUnknown"); File srcFile = new File("src/test/resources/examples/anyContentWithoutSuffix"); - assertTrue("File doesn't exist: " + srcFile.toString(), srcFile.exists()); + assertTrue("File doesn't exist: " + srcFile, srcFile.exists()); String[] extensions = {"nosuffix", "json", ".json ", ".xml"}; // skip extensions with a '.' at start. No idea why at the moment. // works fine in testRemoveFile()!? @@ -331,7 +331,7 @@ public void testFixFileExtensionUnknown() throws IOException { Files.copy(srcFile, createTempFile.toFile()); Path result = DownloadUtil.fixFileExtension(createTempFile); assertTrue(result.toString().endsWith(extension)); - assertTrue("Can't delete file '" + result.toString() + "'!", result.toFile().delete()); + assertTrue("Can't delete file '" + result + "'!", result.toFile().delete()); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsFailingTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsFailingTest.java index 8b76f1c7..aba13cad 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsFailingTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsFailingTest.java @@ -81,7 +81,7 @@ public void testValidateInvalidJsonSchemaDocumentWithNotExistingVersion() { utilities.when(() -> SimpleServiceClient.create(any(String.class))) .thenThrow(new NullPointerException()); JsonUtils.validateJsonSchemaDocument(schemaDocument, VersionFlag.V201909); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.ERROR_VALIDATING_SCHEMA)); @@ -103,7 +103,7 @@ public void testValidateJsonSchemaDocumentWithSchemaDraft201909AsStreamButWrongV VersionFlag version = mock(VersionFlag.class); when(version.ordinal()).thenReturn(Integer.valueOf(VersionFlag.values().length)); JsonUtils.validateJsonSchemaDocument(schemaDocument, version); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsTest.java index b9a2017f..638077eb 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/JsonUtilsTest.java @@ -142,7 +142,7 @@ public void testValidateJsonSchemaDocumentWithNull() { String schemaDocument = null; try { JsonUtils.validateJsonSchemaDocument(schemaDocument); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("argument \"content\" is null")); @@ -158,7 +158,7 @@ public void testValidateJsonSchemaDocumentStreamWithNull() { InputStream schemaDocument = null; try { JsonUtils.validateJsonSchemaDocument(schemaDocument); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.ERROR_READING_INPUT_STREAM)); @@ -174,7 +174,7 @@ public void testValidateJsonSchemaDocumentWithEmptyString() { String schemaDocument = ""; try { JsonUtils.validateJsonSchemaDocument(schemaDocument); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.ERROR_VALIDATING_SCHEMA)); @@ -192,7 +192,7 @@ public void testValidateJsonSchemaDocumentWithEmptyStream() throws IOException { InputStream schemaDocument = IOUtils.toInputStream("", ENCODING); try { JsonUtils.validateJsonSchemaDocument(schemaDocument); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.ERROR_VALIDATING_SCHEMA)); @@ -208,7 +208,7 @@ public void testValidateJsonSchemaDocumentWithEmptyJson() { String schemaDocument = "{}"; try { JsonUtils.validateJsonSchemaDocument(schemaDocument); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.EMPTY_SCHEMA_DETECTED)); @@ -226,7 +226,7 @@ public void testValidateJsonSchemaDocumentWithEmptyJsonStream() throws IOExcepti InputStream schemaDocument = IOUtils.toInputStream("{}", ENCODING); try { JsonUtils.validateJsonSchemaDocument(schemaDocument); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.EMPTY_SCHEMA_DETECTED)); @@ -304,7 +304,7 @@ public void testValidateJsonSchemaDocumentWithSchemaDraft201909ButWrongVersion() String schemaDocument = jsonSchemaWithversiondraft201909; try { JsonUtils.validateJsonSchemaDocument(schemaDocument, SpecVersion.VersionFlag.V4); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("Unknown MetaSchema")); @@ -320,7 +320,7 @@ public void testValidateInvalidJsonSchemaDocument() { String schemaDocument = invalidJsonSchemaDocumentWithversiondraft201909; try { JsonUtils.validateJsonSchemaDocument(schemaDocument); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.ERROR_VALIDATING_SCHEMA)); @@ -338,7 +338,7 @@ public void testValidateJsonSchemaDocumentWithSchemaDraft201909AsStreamButWrongV InputStream schemaDocument = IOUtils.toInputStream(jsonSchemaWithversiondraft201909, ENCODING); try { JsonUtils.validateJsonSchemaDocument(schemaDocument, SpecVersion.VersionFlag.V4); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("Unknown MetaSchema")); @@ -354,7 +354,7 @@ public void testValidateJsonSchemaDocumentWithSchemaDraft04ButWrongVersion() { String schemaDocument = jsonSchemaWithversiondraft04; try { JsonUtils.validateJsonSchemaDocument(schemaDocument, SpecVersion.VersionFlag.V201909); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("Unknown MetaSchema")); @@ -372,7 +372,7 @@ public void testValidateJsonSchemaDocumentWithSchemaDraft04AsStreamButWrongVersi InputStream schemaDocument = IOUtils.toInputStream(jsonSchemaWithversiondraft04, ENCODING); try { JsonUtils.validateJsonSchemaDocument(schemaDocument, SpecVersion.VersionFlag.V201909); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("Unknown MetaSchema")); @@ -389,7 +389,7 @@ public void testValidateJsonSchemaDocumentWithSchemaButNullVersion() { String schemaDocument = jsonSchemaWithversiondraft04; SpecVersion.VersionFlag version = null; JsonUtils.validateJsonSchemaDocument(schemaDocument, version); - assertTrue(false); //should not executed. + fail(); //should not executed. } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.MISSING_SCHEMA_VERSION)); @@ -408,7 +408,7 @@ public void testValidateJsonSchemaDocumentWithSchemaAsStreamButNullVersion() thr InputStream schemaDocument = IOUtils.toInputStream(jsonSchemaWithversiondraft04, ENCODING); SpecVersion.VersionFlag version = null; JsonUtils.validateJsonSchemaDocument(schemaDocument, version); - assertTrue(false); //should not executed. + fail(); //should not executed. } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.MISSING_SCHEMA_VERSION)); @@ -425,7 +425,7 @@ public void testValidateJsonSchemaDocumentWithNullSchemaButVersion() { String schemaDocument = null; SpecVersion.VersionFlag version = SpecVersion.VersionFlag.V201909; JsonUtils.validateJsonSchemaDocument(schemaDocument, version); - assertTrue(false); //should not executed. + fail(); //should not executed. } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("argument \"content\" is null")); @@ -442,7 +442,7 @@ public void testValidateJsonSchemaDocumentWithNullSchemaAsStreamButVersion() { InputStream schemaDocument = null; SpecVersion.VersionFlag version = SpecVersion.VersionFlag.V201909; JsonUtils.validateJsonSchemaDocument(schemaDocument, version); - assertTrue(false); //should not executed. + fail(); //should not executed. } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.ERROR_READING_INPUT_STREAM)); @@ -587,7 +587,7 @@ public void testValidateJsonWithEmptySchema() { SpecVersion.VersionFlag version = SpecVersion.VersionFlag.V201909; try { JsonUtils.validateJson(jsonDocument, jsonSchema, version); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.EMPTY_SCHEMA_DETECTED)); @@ -607,7 +607,7 @@ public void testValidateJsonAsStreamWithEmptySchema() throws IOException { SpecVersion.VersionFlag version = SpecVersion.VersionFlag.V201909; try { JsonUtils.validateJson(jsonDocument, jsonSchema, version); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains(JsonUtils.EMPTY_SCHEMA_DETECTED)); @@ -625,7 +625,7 @@ public void testValidateJsonWithWrongVersion() { SpecVersion.VersionFlag version = SpecVersion.VersionFlag.V7; try { JsonUtils.validateJson(jsonDocument, jsonSchema, version); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("Unknown MetaSchema")); @@ -645,7 +645,7 @@ public void testValidateJsonAsStreamWithWrongVersion() throws IOException { SpecVersion.VersionFlag version = SpecVersion.VersionFlag.V7; try { JsonUtils.validateJson(jsonDocument, jsonSchema, version); - assertTrue(false); + fail(); } catch (JsonValidationException jvex) { assertTrue(true); assertTrue(jvex.getMessage().contains("Unknown MetaSchema")); diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java index e8743f91..93962331 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java @@ -192,7 +192,7 @@ public void testCreateMetadataRecordExeption3a() { System.out.println("createMetadataRecord"); // invalid record document MetastoreConfiguration applicationProperties = metadataConfig; - MultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", new String("{nonsense}").getBytes()); + MultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", "{nonsense}".getBytes()); MultipartFile document = null; MetadataRecord expResult = null; MetadataRecord result = MetadataRecordUtil.createMetadataRecord(applicationProperties, recordDocument, document); @@ -276,7 +276,7 @@ public void testCreateMetadataRecordExeption4d() throws JsonProcessingException MetadataRecord record = new MetadataRecord(); // set id which is not allowed for create record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); - record.setSchemaVersion(1l); + record.setSchemaVersion(1L); record.setRelatedResource(RELATED_RESOURCE); record.setId("anyId"); ObjectMapper mapper = new ObjectMapper(); @@ -303,7 +303,7 @@ public void testCreateMetadataRecordExeption5() throws JsonProcessingException { MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MetastoreConfiguration applicationProperties = metadataConfig; // empty document - MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null);; + MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null); MetadataRecord expResult = null; MetadataRecord result = MetadataRecordUtil.createMetadataRecord(applicationProperties, recordDocument, document); fail("Don't reach this line!"); @@ -317,7 +317,7 @@ public void testCreateMetadataRecordExeption5a() throws JsonProcessingException record.setRelatedResource(RELATED_RESOURCE); ObjectMapper mapper = new ObjectMapper(); - MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", new String("{nonsense}").getBytes()); + MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", "{nonsense}".getBytes()); MetastoreConfiguration applicationProperties = metadataConfig; MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", mapper.writeValueAsString(record).getBytes()); MetadataRecord expResult = null; @@ -431,7 +431,7 @@ public void testCreateMetadataRecordExeption9() throws JsonProcessingException { MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", SCHEMA_ID.getBytes()); MetastoreConfiguration applicationProperties = metadataConfig; - MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null);; + MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null); MetadataRecord expResult = null; MetadataRecord result = MetadataRecordUtil.createMetadataRecord(applicationProperties, recordDocument, document); fail("Don't reach this line!"); @@ -447,7 +447,7 @@ public void testCreateMetadataRecordExeption10() throws JsonProcessingException MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MetastoreConfiguration applicationProperties = metadataConfig; - MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null);; + MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null); MetadataRecord expResult = null; MetadataRecord result = MetadataRecordUtil.createMetadataRecord(applicationProperties, recordDocument, document); fail("Don't reach this line!"); @@ -464,7 +464,7 @@ public void testCreateMetadataRecordExeption11() throws JsonProcessingException MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MetastoreConfiguration applicationProperties = metadataConfig; - MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null);; + MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null); MetadataRecord expResult = null; MetadataRecord result = MetadataRecordUtil.createMetadataRecord(applicationProperties, recordDocument, document); fail("Don't reach this line!"); @@ -481,7 +481,7 @@ public void testCreateMetadataRecordExeption12() throws JsonProcessingException MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MetastoreConfiguration applicationProperties = metadataConfig; - MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null);; + MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", (byte[]) null); MetadataRecord expResult = null; MetadataRecord result = MetadataRecordUtil.createMetadataRecord(applicationProperties, recordDocument, document); fail("Don't reach this line!"); @@ -538,7 +538,7 @@ public void testUpdateMetadataRecord3() throws JsonProcessingException { MetastoreConfiguration applicationProperties = null; String resourceId = ""; String eTag = ""; - MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", new String().getBytes()); + MockMultipartFile recordDocument = new MockMultipartFile("record", "metadata-record.json", "application/json", "".getBytes()); MultipartFile document = null; UnaryOperator<String> supplier = null; MetadataRecord expResult = null; @@ -585,7 +585,7 @@ public void testUpdateMetadataRecord5() throws JsonProcessingException { String resourceId = ""; String eTag = ""; MockMultipartFile recordDocument = null; - MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", new String().getBytes()); + MultipartFile document = new MockMultipartFile("document", "metadata.xml", "application/xml", "".getBytes()); UnaryOperator<String> supplier = null; MetadataRecord expResult = null; MetadataRecord result = MetadataRecordUtil.updateMetadataRecord(applicationProperties, resourceId, eTag, recordDocument, document, supplier); @@ -641,7 +641,7 @@ public void testMigrateToDataResource() { System.out.println("migrateToDataResource"); MetadataRecord record = new MetadataRecord(); record.setId("08/15"); - record.setRecordVersion(1l); + record.setRecordVersion(1L); record.setCreatedAt(Instant.now()); record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(SCHEMA_ID)); record.setRelatedResource(RELATED_RESOURCE); @@ -670,9 +670,9 @@ public void testMigrateToMetadataRecord() { result = MetadataRecordUtil.migrateToMetadataRecord(applicationProperties, dataResource, false); assertNotNull(result.getId()); assertEquals("Id should be the same!", result.getId(), dataResource.getId()); - assertEquals("Version should be '1'", Long.valueOf(1l), result.getRecordVersion()); + assertEquals("Version should be '1'", Long.valueOf(1L), result.getRecordVersion()); assertTrue("ACL should be empty", result.getAcl().isEmpty()); - assertTrue("License should be 'null'", result.getLicenseUri() == null); + assertNull("License should be 'null'", result.getLicenseUri()); assertNull("PID should be empty", result.getPid()); assertNull("Create date should be empty!", result.getCreatedAt()); assertNull("Last update date should be empty!", result.getLastUpdate()); @@ -683,9 +683,9 @@ public void testMigrateToMetadataRecord() { result = MetadataRecordUtil.migrateToMetadataRecord(applicationProperties, dataResource, false); assertNotNull(result.getId()); assertEquals("Id should be the same!", result.getId(), dataResource.getId()); - assertEquals("Version should be '1'", Long.valueOf(1l), result.getRecordVersion()); + assertEquals("Version should be '1'", Long.valueOf(1L), result.getRecordVersion()); assertTrue("ACL should be empty", result.getAcl().isEmpty()); - assertTrue("License should be 'null'", result.getLicenseUri() == null); + assertNull("License should be 'null'", result.getLicenseUri()); assertNull("PID should be empty", result.getPid()); assertNull("Create date should be empty!", result.getCreatedAt()); assertNull("Last update date should be empty!", result.getLastUpdate()); @@ -695,9 +695,9 @@ public void testMigrateToMetadataRecord() { result = MetadataRecordUtil.migrateToMetadataRecord(applicationProperties, dataResource, false); assertNotNull(result.getId()); assertEquals("Id should be the same!", result.getId(), dataResource.getId()); - assertEquals("Version should be '1'", Long.valueOf(1l), result.getRecordVersion()); + assertEquals("Version should be '1'", Long.valueOf(1L), result.getRecordVersion()); assertTrue("ACL should be empty", result.getAcl().isEmpty()); - assertTrue("License should be 'null'", result.getLicenseUri() == null); + assertNull("License should be 'null'", result.getLicenseUri()); assertNull("Create date should be empty!", result.getCreatedAt()); assertNull("Last update date should be empty!", result.getLastUpdate()); // PID should be set @@ -709,7 +709,7 @@ public void testMigrateToMetadataRecord() { result = MetadataRecordUtil.migrateToMetadataRecord(applicationProperties, dataResource, false); assertNotNull(result.getId()); assertEquals("Id should be the same!", result.getId(), dataResource.getId()); - assertEquals("Version should be '1'", Long.valueOf(1l), result.getRecordVersion()); + assertEquals("Version should be '1'", Long.valueOf(1L), result.getRecordVersion()); assertTrue("ACL should be empty", result.getAcl().isEmpty()); assertNull("Create date should be empty!", result.getCreatedAt()); assertNull("Last update date should be empty!", result.getLastUpdate()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java index 68791d2a..0cd29aeb 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java @@ -148,8 +148,8 @@ public void testCreateMetadataSchemaRecordEmpty() { MetastoreConfiguration conf = new MetastoreConfiguration(); String resourceId = "any"; String eTag = "neverMind"; - MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); + MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); MetadataSchemaRecordUtil.createMetadataSchemaRecord(conf, recordDocument, schemaDocument, null); fail("Don't reach this line!"); } @@ -159,8 +159,8 @@ public void testCreateMetadataSchemaRecordInvalid() { MetastoreConfiguration conf = new MetastoreConfiguration(); String resourceId = "any"; String eTag = "neverMind"; - MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String("{something really strange}").getBytes()); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); + MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", "{something really strange}".getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); MetadataSchemaRecordUtil.createMetadataSchemaRecord(conf, recordDocument, schemaDocument, null); fail("Don't reach this line!"); } @@ -181,8 +181,8 @@ public void testUpdateMetadataSchemaRecordEmpty() { MetastoreConfiguration conf = new MetastoreConfiguration(); String resourceId = "any"; String eTag = "neverMind"; - MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); + MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); MetadataSchemaRecordUtil.updateMetadataSchemaRecord(conf, resourceId, eTag, recordDocument, schemaDocument, null); fail("Don't reach this line!"); } @@ -192,8 +192,8 @@ public void testUpdateMetadataSchemaRecordInvalid() { MetastoreConfiguration conf = new MetastoreConfiguration(); String resourceId = "any"; String eTag = "neverMind"; - MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String("{something really strange}").getBytes()); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); + MultipartFile recordDocument = new MockMultipartFile("schema", "schema.json", "application/json", "{something really strange}".getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); MetadataSchemaRecordUtil.updateMetadataSchemaRecord(conf, resourceId, eTag, recordDocument, schemaDocument, null); fail("Don't reach this line!"); } @@ -201,14 +201,14 @@ public void testUpdateMetadataSchemaRecordInvalid() { @Test(expected = edu.kit.datamanager.exceptions.BadArgumentException.class) public void testValidateResourceIdentifierNull() { MetastoreConfiguration conf = new MetastoreConfiguration(); - MetadataSchemaRecordUtil.validateMetadataDocument(conf, null, (ResourceIdentifier) null, 1l); + MetadataSchemaRecordUtil.validateMetadataDocument(conf, null, (ResourceIdentifier) null, 1L); fail("Don't reach this line!"); } @Test(expected = edu.kit.datamanager.exceptions.BadArgumentException.class) public void testValidateResourceIdentifierNull_2() { MetastoreConfiguration conf = new MetastoreConfiguration(); - MetadataSchemaRecordUtil.validateMetadataDocument(conf, null, (String) null, 1l); + MetadataSchemaRecordUtil.validateMetadataDocument(conf, null, (String) null, 1L); fail("Don't reach this line!"); } @@ -216,8 +216,8 @@ public void testValidateResourceIdentifierNull_2() { public void testValidateResourceIdentifierNoValue() { MetastoreConfiguration conf = new MetastoreConfiguration(); ResourceIdentifier identifier = ResourceIdentifier.factoryInternalResourceIdentifier(null); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); - MetadataSchemaRecordUtil.validateMetadataDocument(conf, schemaDocument, identifier, 1l); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); + MetadataSchemaRecordUtil.validateMetadataDocument(conf, schemaDocument, identifier, 1L); fail("Don't reach this line!"); } @@ -242,7 +242,7 @@ public void testValidateMetadataDocumentEmpty() { MetastoreConfiguration conf = new MetastoreConfiguration(); SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaDocumentUri("any"); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String().getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "".getBytes()); MetadataSchemaRecordUtil.validateMetadataDocument(conf, schemaDocument, schemaRecord); fail("Don't reach this line!"); } @@ -251,7 +251,7 @@ public void testValidateMetadataDocumentEmpty() { public void testValidateMetadataDocumentSchemaRecordNull() { MetastoreConfiguration conf = new MetastoreConfiguration(); SchemaRecord schemaRecord = null; - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String("any content").getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "any content".getBytes()); MetadataSchemaRecordUtil.validateMetadataDocument(conf, schemaDocument, schemaRecord); fail("Don't reach this line!"); } @@ -260,7 +260,7 @@ public void testValidateMetadataDocumentSchemaRecordNull() { public void testValidateMetadataDocumentSchemaRecordUriNull() { MetastoreConfiguration conf = new MetastoreConfiguration(); SchemaRecord schemaRecord = new SchemaRecord(); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String("any content").getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "any content".getBytes()); MetadataSchemaRecordUtil.validateMetadataDocument(conf, schemaDocument, schemaRecord); fail("Don't reach this line!"); } @@ -270,7 +270,7 @@ public void testValidateMetadataDocumentSchemaRecordUriEmpty() { MetastoreConfiguration conf = new MetastoreConfiguration(); SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaDocumentUri(" "); - MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", new String("any content").getBytes()); + MultipartFile schemaDocument = new MockMultipartFile("schema", "schema.json", "application/json", "any content".getBytes()); MetadataSchemaRecordUtil.validateMetadataDocument(conf, schemaDocument, schemaRecord); fail("Don't reach this line!"); } @@ -312,12 +312,12 @@ private MetadataSchemaRecord buildMSR(Set<AclEntry> aclEntry, String comment, In public MetadataSchemaRecord createSchemaRecord(int... skipped) { Set<AclEntry> aclEntries = new HashSet<>(); AclEntry entry = new AclEntry(); - entry.setId(1l); + entry.setId(1L); entry.setPermission(PERMISSION.WRITE); entry.setSid("write"); - aclEntries.add(createEntry(1l, PERMISSION.NONE, "none")); - aclEntries.add(createEntry(2l, PERMISSION.READ, "read")); - MetadataSchemaRecord msr = buildMSR(aclEntries, "comment", Instant.now().truncatedTo(ChronoUnit.SECONDS), "definition", "eTag", "label", Instant.MAX.truncatedTo(ChronoUnit.SECONDS), true, "mimetype", "pid", "schemadocument", "hash", "schemaId", 1l, SCHEMA_TYPE.XML); + aclEntries.add(createEntry(1L, PERMISSION.NONE, "none")); + aclEntries.add(createEntry(2L, PERMISSION.READ, "read")); + MetadataSchemaRecord msr = buildMSR(aclEntries, "comment", Instant.now().truncatedTo(ChronoUnit.SECONDS), "definition", "eTag", "label", Instant.MAX.truncatedTo(ChronoUnit.SECONDS), true, "mimetype", "pid", "schemadocument", "hash", "schemaId", 1L, SCHEMA_TYPE.XML); for (int remove : skipped) { switch (remove) { case 1: diff --git a/src/test/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidatorTest.java b/src/test/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidatorTest.java index df082c33..30178ffe 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidatorTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/validation/impl/JsonValidatorTest.java @@ -41,7 +41,7 @@ public static void tearDownClass() { } @Before - public void setUp() throws FileNotFoundException, IOException { + public void setUp() throws IOException { File schemaFile = new File(jsonSchemaFile); if (!schemaFile.exists()) { try ( FileOutputStream fout = new FileOutputStream(schemaFile)) { @@ -177,7 +177,7 @@ public void testValidateMetadataDocumentWithNullArguments() { assertNotNull(instance.getErrorMessage()); } @Test - public void testValidateMetadataDocumentWithNullDocument() throws FileNotFoundException, IOException { + public void testValidateMetadataDocumentWithNullDocument() throws IOException { System.out.println("testValidateMetadataDocumentWithEmptyDocument"); File schemaFile = new File(jsonSchemaFile); InputStream metadataDocumentStream = null; @@ -187,7 +187,7 @@ public void testValidateMetadataDocumentWithNullDocument() throws FileNotFoundEx assertEquals(expResult, result); } @Test - public void testValidateMetadataDocumentWithEmptyDocument() throws FileNotFoundException, IOException { + public void testValidateMetadataDocumentWithEmptyDocument() throws IOException { System.out.println("testValidateMetadataDocumentWithEmptyDocument"); File schemaFile = new File(jsonSchemaFile); InputStream metadataDocumentStream = null; @@ -199,7 +199,7 @@ public void testValidateMetadataDocumentWithEmptyDocument() throws FileNotFoundE } @Test - public void testValidateMetadataDocumentWithValidDocument() throws FileNotFoundException, IOException { + public void testValidateMetadataDocumentWithValidDocument() throws IOException { System.out.println("testValidateMetadataDocumentWithEmptyDocument"); File schemaFile = new File(jsonSchemaFile); InputStream metadataDocumentStream = null; diff --git a/src/test/java/org/openarchives/oai/_2/HeaderTypeTest.java b/src/test/java/org/openarchives/oai/_2/HeaderTypeTest.java index b6c45adf..bd1ac9d1 100644 --- a/src/test/java/org/openarchives/oai/_2/HeaderTypeTest.java +++ b/src/test/java/org/openarchives/oai/_2/HeaderTypeTest.java @@ -49,7 +49,7 @@ public void testSetAndGetIdentifier() { String expResult = value; String result = instance.getIdentifier(); assertEquals(expResult, result); - value = new String("identifier"); + value = "identifier"; expResult = value; instance.setIdentifier(value); result = instance.getIdentifier(); @@ -67,7 +67,7 @@ public void testSetAndGetDatestamp() { String expResult = value; String result = instance.getDatestamp(); assertEquals(expResult, result); - value = new String("identifier"); + value = "identifier"; expResult = value; instance.setDatestamp(value); result = instance.getDatestamp(); diff --git a/src/test/java/org/openarchives/oai/_2/IdentifyTypeTest.java b/src/test/java/org/openarchives/oai/_2/IdentifyTypeTest.java index c484905a..dbefdfb8 100644 --- a/src/test/java/org/openarchives/oai/_2/IdentifyTypeTest.java +++ b/src/test/java/org/openarchives/oai/_2/IdentifyTypeTest.java @@ -49,7 +49,7 @@ public void testSetAndGetRepositoryName() { String expResult = value; String result = instance.getRepositoryName(); assertEquals(expResult, result); - value = new String("repoName"); + value = "repoName"; expResult = value; instance.setRepositoryName(value); result = instance.getRepositoryName(); @@ -66,7 +66,7 @@ public void testSetAndGetBaseURL() { String expResult = value; String result = instance.getBaseURL(); assertEquals(expResult, result); - value = new String("baseUrl"); + value = "baseUrl"; expResult = value; instance.setBaseURL(value); result = instance.getBaseURL(); @@ -84,7 +84,7 @@ public void testSetAndGetProtocolVersion() { String expResult = value; String result = instance.getProtocolVersion(); assertEquals(expResult, result); - value = new String("protocolVersion"); + value = "protocolVersion"; expResult = value; instance.setProtocolVersion(value); result = instance.getProtocolVersion(); @@ -119,7 +119,7 @@ public void testSetAndGetEarliestDatestamp() { String expResult = value; String result = instance.getEarliestDatestamp(); assertEquals(expResult, result); - value = new String("firstDateStamp"); + value = "firstDateStamp"; expResult = value; instance.setEarliestDatestamp(value); result = instance.getEarliestDatestamp(); @@ -174,7 +174,7 @@ public void testGetCompression() { List<String> result = instance.getCompression(); assertTrue(result.isEmpty()); expResult = result; - expResult.add(new String("gzip")); + expResult.add("gzip"); result = instance.getCompression(); assertEquals(expResult, result); } diff --git a/src/test/java/org/openarchives/oai/_2/MetadataFormatTypeTest.java b/src/test/java/org/openarchives/oai/_2/MetadataFormatTypeTest.java index d1da5501..795aecd2 100644 --- a/src/test/java/org/openarchives/oai/_2/MetadataFormatTypeTest.java +++ b/src/test/java/org/openarchives/oai/_2/MetadataFormatTypeTest.java @@ -48,7 +48,7 @@ public void testSetAndGetMetadataPrefix() { String expResult = value; String result = instance.getMetadataPrefix(); assertEquals(expResult, result); - value = new String("metadataprefix"); + value = "metadataprefix"; expResult = value; instance.setMetadataPrefix(value); result = instance.getMetadataPrefix(); @@ -66,7 +66,7 @@ public void testSetAndGetSchema() { String expResult = value; String result = instance.getSchema(); assertEquals(expResult, result); - value = new String("schema"); + value = "schema"; expResult = value; instance.setSchema(value); result = instance.getSchema(); @@ -84,7 +84,7 @@ public void testSetAndGetMetadataNamespace() { String expResult = value; String result = instance.getMetadataNamespace(); assertEquals(expResult, result); - value = new String("metadatanamespace"); + value = "metadatanamespace"; expResult = value; instance.setMetadataNamespace(value); result = instance.getMetadataNamespace(); diff --git a/src/test/java/org/openarchives/oai/_2/OAIPMHerrorTypeTest.java b/src/test/java/org/openarchives/oai/_2/OAIPMHerrorTypeTest.java index 29a9f5b1..ef657b4b 100644 --- a/src/test/java/org/openarchives/oai/_2/OAIPMHerrorTypeTest.java +++ b/src/test/java/org/openarchives/oai/_2/OAIPMHerrorTypeTest.java @@ -48,7 +48,7 @@ public void testSetAndGetValue() { String expResult = value; String result = instance.getValue(); assertEquals(expResult, result); - value = new String("value"); + value = "value"; expResult = value; instance.setValue(value); result = instance.getValue(); diff --git a/src/test/resources/test-config/application-test.properties b/src/test/resources/test-config/application-test.properties index ecd4d988..9a63593e 100644 --- a/src/test/resources/test-config/application-test.properties +++ b/src/test/resources/test-config/application-test.properties @@ -3,11 +3,11 @@ ############################################################################### # Port ############################################################################### -server.port: 41400 +server.port= 41400 ############################################################################### # KIT DM settings ############################################################################### -repo.auth.jwtSecret: vkfvoswsohwrxgjaxipuiyyjgubggzdaqrcuupbugxtnalhiegkppdgjgwxsmvdb +repo.auth.jwtSecret= vkfvoswsohwrxgjaxipuiyyjgubggzdaqrcuupbugxtnalhiegkppdgjgwxsmvdb ############################################################################### # KIT DM JaVers settings @@ -18,14 +18,14 @@ repo.auth.jwtSecret: vkfvoswsohwrxgjaxipuiyyjgubggzdaqrcuupbugxtnalhiegkppdgjgwx ############################################################################### # Messaging - RabbitMQ ############################################################################### -repo.schedule.rate:1000 -repo.messaging.enabled: false -repo.messaging.hostname:localhost -repo.messaging.port:5672 -repo.messaging.sender.exchange: metastore_events -repo.messaging.receiver.exchange: metastore_events -repo.messaging.receiver.queue: metastoreEventQueue -repo.messaging.receiver.routingKeys: metadata.# +repo.schedule.rate=1000 +repo.messaging.enabled= false +repo.messaging.hostname=localhost +repo.messaging.port=5672 +repo.messaging.sender.exchange= metastore_events +repo.messaging.receiver.exchange= metastore_events +repo.messaging.receiver.queue= metastoreEventQueue +repo.messaging.receiver.routingKeys= metadata.# ################################################################################ # Search - Elasticsearch @@ -38,49 +38,49 @@ repo.search.url = http://localhost:9200 ############################################################################### # Database ############################################################################### -spring.datasource.driver-class-name: org.h2.Driver -spring.datasource.url: jdbc:h2:mem:db_test;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE -spring.datasource.username: sa -spring.datasource.password: sa +spring.datasource.driver-class-name= org.h2.Driver +spring.datasource.url= jdbc:h2:mem:db_test;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE +spring.datasource.username= sa +spring.datasource.password= sa ############################################################################### # Logging settings ############################################################################### -logging.level.root: OFF -logging.level.edu.kit.datamanager: TRACE +logging.level.root= OFF +logging.level.edu.kit.datamanager= TRACE #logging.level.org.springframework.security: TRACE #logging.level.org.springframework: TRACE ############################################################################### # Setup paths for schema and metadata ############################################################################### -metastore.schema.schemaFolder:file:///tmp/metastore2/schema -metastore.metadata.metadataFolder:file:///tmp/metastore2/metadata +metastore.schema.schemaFolder=file:///tmp/metastore2/schema +metastore.metadata.metadataFolder=file:///tmp/metastore2/metadata ############################################################################### # Setup schema registries. (Optional, no longer necessary) ############################################################################### -metastore.metadata.schemaRegistries: +metastore.metadata.schemaRegistries= ############################################################################### # OAI PMH Plugin ############################################################################### -repo.plugin.repositoryBaseUrl:http://localhost:41400/api/v1/metadata -repo.plugin.oaipmh.adminEmail:admin@example.org -repo.plugin.oaipmh.maxElementsPerList:3 +repo.plugin.repositoryBaseUrl=http://localhost:41400/api/v1/metadata +repo.plugin.oaipmh.adminEmail=admin@example.org +repo.plugin.oaipmh.maxElementsPerList=3 ############################################################################### # DOIP Plugin ############################################################################### -repo.plugin.doip.enabled: false -repo.plugin.doip.port: 41420 -repo.plugin.doip.serviceId:35.TEST/DOIPServer -repo.plugin.doip.serviceName:DOIP4MetaStore -repo.plugin.doip.serviceDescription:Generic repository especially for metadata. +repo.plugin.doip.enabled= false +repo.plugin.doip.port= 41420 +repo.plugin.doip.serviceId=35.TEST/DOIPServer +repo.plugin.doip.serviceName=DOIP4MetaStore +repo.plugin.doip.serviceDescription=Generic repository especially for metadata. # 'localhost' has to be replaced by hostname -repo.plugin.doip.address:localhost -repo.plugin.doip.authenticationEnabled:true -repo.plugin.doip.defaultToken:REPLACE_BY_YOUR_TOKEN +repo.plugin.doip.address=localhost +repo.plugin.doip.authenticationEnabled=true +repo.plugin.doip.defaultToken=REPLACE_BY_YOUR_TOKEN ############################################################################### # Spring @@ -92,19 +92,19 @@ eureka.client.enabled=false ############################################################################### # Management endpoint settings ############################################################################### -management.endpoint.health.enabled: true -management.endpoint.health.show-details: ALWAYS -management.endpoint.health.sensitive: false -management.endpoints.web.exposure.include: * +management.endpoint.health.enabled= true +management.endpoint.health.show-details= ALWAYS +management.endpoint.health.sensitive= false +management.endpoints.web.exposure.include= * # Disable unused service # Remove or enable the corresponding lines if you want to check the health of # dependent services as well. -management.health.elasticsearch.enabled: false -management.health.rabbit.enabled: false +management.health.elasticsearch.enabled= false +management.health.rabbit.enabled= false -spring.main.allow-bean-definition-overriding:true +spring.main.allow-bean-definition-overriding=true #spring.jpa.properties.javax.persistence.validation.mode:none -spring.data.rest.detection-strategy: annotated +spring.data.rest.detection-strategy= annotated ############################################################################### # Add detailed message to REST response (NOT RECOMMENDED for PRODUCTION MODE) From fa75164049a7c9cfe838d57033d0cf3d81ea49ce Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 26 Aug 2024 11:30:22 +0200 Subject: [PATCH 057/181] Disable old info builder for API v1. --- .../metastore2/util/DataResourceRecordUtil.java | 2 +- .../metastore2/web/impl/MetadataControllerImpl.java | 11 ++--------- .../web/impl/SchemaRegistryControllerImpl.java | 11 ++--------- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 8dc746de..e56211fa 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -1991,7 +1991,7 @@ public static Page<DataResource> queryDataResources(Specification spec, Pageable LOG.trace("-----------------------------------------------"); int itemNo = 1; for (DataResource item : records.getContent()) { - LOG.trace("#{} - '{}'", itemNo++, item); + LOG.trace("#{} - '{}'- Version: '{}'", itemNo++, item.getId(), item.getVersion()); } LOG.trace("-----------------------------------------------"); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index 41ba3edc..6fc9f1be 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -479,15 +479,8 @@ public ResponseEntity deleteRecord( @Override public void contribute(Info.Builder builder) { - LOG.trace("Check for MetadataRepo actuator information..."); - - URL basePath = metadataConfig.getBasepath(); - Map<String, String> details = ActuatorUtil.testDirectory(basePath); - - if (!details.isEmpty()) { - details.put("No of metadata documents", Long.toString(MetadataRecordUtil.getNoOfDocuments())); - builder.withDetail("metadataRepo", details); - } + LOG.trace("Check for MetadataRepo actuator information (v1)..."); + LOG.trace("Check for MetadataRepo actuator information (v1) disabled!"); } @Bean diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index 7cd031a3..d2d647ad 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -336,15 +336,8 @@ public ResponseEntity deleteRecord(@PathVariable("schemaId") final String schema @Override public void contribute(Info.Builder builder) { - LOG.trace("Check for SchemaRepo actuator information..."); - - URL basePath = schemaConfig.getBasepath(); - Map<String, String> details = ActuatorUtil.testDirectory(basePath); - - if (!details.isEmpty()) { - details.put("No of schema documents", Long.toString(MetadataSchemaRecordUtil.getNoOfSchemas())); - builder.withDetail("schemaRepo", details); - } + LOG.trace("Check for SchemaRepo actuator information (v1)..."); + LOG.trace("Check for SchemaRepo actuator information (v1) disabled!"); } private Specification<DataResource> addAuthenticationSpecification(Specification<DataResource> spec) { From 8e05b63037a132870a6898e3d39af809488f4647 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:49:18 +0000 Subject: [PATCH 058/181] Bump org.postgresql:postgresql from 42.7.3 to 42.7.4 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.3 to 42.7.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.3...REL42.7.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e3f33ad6..3c8201b9 100644 --- a/build.gradle +++ b/build.gradle @@ -94,7 +94,7 @@ dependencies { implementation "org.javers:javers-core:${javersVersion}" // driver for postgres - implementation "org.postgresql:postgresql:42.7.3" + implementation "org.postgresql:postgresql:42.7.4" //driver for h2 implementation "com.h2database:h2:2.3.232" From 57864a544b9a93babc619db4ed6af93574ca8148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:49:24 +0000 Subject: [PATCH 059/181] Bump org.springframework.boot from 3.3.2 to 3.3.3 Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 3.3.2 to 3.3.3. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.2...v3.3.3) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e3f33ad6..76473b75 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.3.2' + id 'org.springframework.boot' version '3.3.3' id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.10' id 'io.freefair.maven-publish-java' version '8.10' From 2afd70c32ed0a082b86a53153863d56057b3b2b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 05:58:00 +0000 Subject: [PATCH 060/181] Bump com.google.errorprone:error_prone_core from 2.30.0 to 2.31.0 Bumps [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) from 2.30.0 to 2.31.0. - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.30.0...v2.31.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 38480585..adc3f714 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ ext { springDocVersion = '2.6.0' javersVersion = '7.6.1' keycloakVersion = '19.0.0' - errorproneVersion = '2.30.0' + errorproneVersion = '2.31.0' // directory for generated code snippets during tests snippetsDir = file("build/generated-snippets") } From acb2ec228b2c4f7917f885a2508b762a33ac59fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 05:58:03 +0000 Subject: [PATCH 061/181] Bump org.mockito:mockito-core from 5.12.0 to 5.13.0 Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.12.0 to 5.13.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.12.0...v5.13.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 38480585..be8d15a3 100644 --- a/build.gradle +++ b/build.gradle @@ -127,7 +127,7 @@ dependencies { testImplementation "org.springframework.security:spring-security-test" //Java 11 Support - testImplementation "org.mockito:mockito-core:5.12.0" + testImplementation "org.mockito:mockito-core:5.13.0" testImplementation "junit:junit:4.13.2" testImplementation "com.github.stefanbirkner:system-lambda:1.2.1" From 86f52fd8f5a779819f5957a749dd0b03afab827b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 30 Aug 2024 10:53:55 +0200 Subject: [PATCH 062/181] Make metadata documents compatible with version 2 (datacite). --- .../metastore2/util/MetadataRecordUtil.java | 12 +++++++++--- .../metastore2/util/MetadataSchemaRecordUtil.java | 3 ++- .../metastore2/web/impl/MetadataControllerImpl.java | 4 +++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index a70070db..511891c5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -415,14 +415,20 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati String defaultTitle = "Metadata 4 metastore"; boolean titleExists = false; for (Title title : dataResource.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER && title.getValue().equals(defaultTitle)) { + if (title.getTitleType() == null && title.getValue().equals(defaultTitle)) { titleExists = true; } } if (!titleExists) { - dataResource.getTitles().add(Title.factoryTitle(defaultTitle, Title.TYPE.OTHER)); + dataResource.getTitles().add(Title.factoryTitle(defaultTitle)); } - dataResource.setResourceType(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE)); + + // Set ResourceType due to new version + ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); + String prefixDocument = MetadataSchemaRecordUtil.getCurrentSchemaRecord(schemaConfig, schemaIdentifier).getType().name(); + ResourceType resourceType = ResourceType.createResourceType(prefixDocument + DataResourceRecordUtil.METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); + dataResource.setResourceType(resourceType); + checkLicense(dataResource, metadataRecord.getLicenseUri()); return dataResource; diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 9a0caa65..7df95048 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -1179,7 +1179,7 @@ public static void updateMetadataFormat(MetadataSchemaRecord metadataRecord) { } /** - * Returns schema record with the current version. + * Returns schema record with the current version and type. * * @param metastoreProperties Configuration for accessing services * @param schema Identifier of the schema. @@ -1196,6 +1196,7 @@ public static MetadataSchemaRecord getCurrentSchemaRecord(MetastoreConfiguration Long version = 1L; if (url2path.isPresent()) { version = url2path.get().getVersion(); + msr.setType(url2path.get().getType()); } msr.setSchemaVersion(version); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index 41ba3edc..786eb8e3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -28,6 +28,7 @@ import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.util.ActuatorUtil; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.IMetadataController; @@ -340,7 +341,8 @@ public ResponseEntity<List<MetadataRecord>> getRecords( return getAllVersions(id, pgbl); } // Search for resource type of MetadataSchemaRecord - Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE)); + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResourceRecordUtil.METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); +// Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE)); // Add authentication if enabled if (metadataConfig.isAuthEnabled()) { boolean isAdmin; From 97c9f8dfce5d6a1783e7da2f4933a49488c46743 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 30 Aug 2024 11:13:06 +0200 Subject: [PATCH 063/181] Remove type for title in schema due to compatibility to version 2. --- .../datamanager/metastore2/util/MetadataSchemaRecordUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 7df95048..40f69d95 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -403,13 +403,13 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati String defaultTitle = metadataSchemaRecord.getMimeType(); boolean titleExists = false; for (Title title : dataResource.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER) { + if (title.getTitleType() == null) { title.setValue(defaultTitle); titleExists = true; } } if (!titleExists) { - dataResource.getTitles().add(Title.factoryTitle(defaultTitle, Title.TYPE.OTHER)); + dataResource.getTitles().add(Title.factoryTitle(defaultTitle, null)); } dataResource.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE)); dataResource.getFormats().clear(); From 26a70984421ed1dd602cd134ecbfde8ed316b254 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 30 Aug 2024 12:02:34 +0200 Subject: [PATCH 064/181] Make schema documents compatible with version 2 (datacite). --- .../util/MetadataSchemaRecordUtil.java | 2 +- .../impl/SchemaRegistryControllerImpl.java | 25 ++++++++++++++++++- .../JsonSchemaRegistryControllerTest.java | 3 ++- .../test/SchemaRegistryControllerTest.java | 3 ++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 40f69d95..55f66904 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -411,7 +411,7 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati if (!titleExists) { dataResource.getTitles().add(Title.factoryTitle(defaultTitle, null)); } - dataResource.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE)); + dataResource.setResourceType(ResourceType.createResourceType(metadataSchemaRecord.getType().name() + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); dataResource.getFormats().clear(); dataResource.getFormats().add(metadataSchemaRecord.getType().name()); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index d2d647ad..97529a04 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -22,6 +22,7 @@ import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.util.ActuatorUtil; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.ISchemaRegistryController; import edu.kit.datamanager.repo.dao.IDataResourceDao; @@ -252,7 +253,29 @@ public ResponseEntity<List<MetadataSchemaRecord>> getRecords(@RequestParam(value return getAllVersions(schemaId, pgbl); } // Search for resource type of MetadataSchemaRecord - Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE)); + ResourceType resourceType = ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); + if (mimeTypes != null) { + boolean searchForJson = false; + boolean searchForXml = false; + for (String mimeType : mimeTypes) { + if (mimeType.contains("json")) { + searchForJson = true; + } + if (mimeType.contains("xml")) { + searchForXml = true; + } + } + if (searchForJson && !searchForXml) { + resourceType = ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + } + if (!searchForJson && searchForXml) { + resourceType = ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + } + if (!searchForJson && !searchForXml) { + resourceType = ResourceType.createResourceType("unknown"); + } + } + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); // Add authentication if enabled spec = addAuthenticationSpecification(spec); //one of given mimetypes. diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java index 8f18517c..6a464bb9 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java @@ -11,6 +11,7 @@ import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; @@ -1058,7 +1059,7 @@ private void ingestSchemaRecord() throws Exception { dataResource.setPublisher("SELF"); Instant now = Instant.now(); dataResource.setPublicationYear(Integer.toString(Calendar.getInstance().get(Calendar.YEAR))); - dataResource.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE)); + dataResource.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); dataResource.getDates().add(Date.factoryDate(now, Date.DATE_TYPE.CREATED)); dataResource.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.JSON.name()); dataResource.setLastUpdate(now); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index ec0eb771..0ce178f8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -14,6 +14,7 @@ import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtilTest; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; @@ -1650,7 +1651,7 @@ private void ingestSchemaRecord() throws Exception { dataResource.setPublisher("SELF"); Instant now = Instant.now(); dataResource.setPublicationYear(Integer.toString(Calendar.getInstance().get(Calendar.YEAR))); - dataResource.setResourceType(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE)); + dataResource.setResourceType(ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL)); dataResource.getDates().add(Date.factoryDate(now, Date.DATE_TYPE.CREATED)); dataResource.getFormats().add(MetadataSchemaRecord.SCHEMA_TYPE.XML.name()); dataResource.setLastUpdate(now); From 92fe5c52847532931fb5ff01b6234f13bf83edb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:22:06 +0000 Subject: [PATCH 065/181] Bump org.owasp.dependencycheck from 10.0.3 to 10.0.4 Bumps org.owasp.dependencycheck from 10.0.3 to 10.0.4. --- updated-dependencies: - dependency-name: org.owasp.dependencycheck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 853977a8..e0f5621d 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.10' id 'io.freefair.maven-publish-java' version '8.10' - id 'org.owasp.dependencycheck' version '10.0.3' + id 'org.owasp.dependencycheck' version '10.0.4' id 'org.asciidoctor.jvm.convert' version '4.0.3' id 'net.ltgt.errorprone' version '4.0.1' id 'net.researchgate.release' version '3.0.2' From e0263ec53540436d3923b746db5a41e72e82dc6f Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 11 Sep 2024 08:33:45 +0200 Subject: [PATCH 066/181] Add tool for migration to version 2 to runner. --- .../runner/ElasticIndexerRunner.java | 310 ++++++++++++++---- 1 file changed, 242 insertions(+), 68 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 473eff72..fe1a1dcc 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -17,16 +17,30 @@ import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; +import edu.kit.datamanager.entities.RepoServiceRole; import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; import edu.kit.datamanager.metastore2.domain.*; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.METADATA_SUFFIX; +import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.SCHEMA_SUFFIX; +import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.queryDataResources; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImpl; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.security.filter.JwtAuthenticationToken; import edu.kit.datamanager.service.IMessagingService; import edu.kit.datamanager.service.impl.LogfileMessagingService; import edu.kit.datamanager.util.ControllerUtils; +import edu.kit.datamanager.util.JwtBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -36,6 +50,11 @@ import org.springframework.stereotype.Component; import java.util.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.transaction.annotation.Transactional; /** * Class for indexing all metadata documents of given schemas Arguments have to @@ -43,8 +62,23 @@ * reindexed. If no indices are given all indices will be reindexed. */ @Component +@Transactional public class ElasticIndexerRunner implements CommandLineRunner { + /** + * *************************************************************************** + * Parameter for migrating MetaStore version 1.x to version 2.x This should be + * executed only once. + * *************************************************************************** + */ + @Parameter(names = {"--migrate2DataCite"}, description = "Migrate database from version 1.X to 2.X.") + boolean doMigration2DataCite; + + /** + * *************************************************************************** + * Parameters for reindexing elasticsearch. This should be executed only once. + * *************************************************************************** + */ @Parameter(names = {"--reindex"}, description = "Elasticsearch index should be build from existing documents.") boolean updateIndex; @Parameter(names = {"--indices", "-i"}, description = "Only for given indices (comma separated) or all indices if not present.") @@ -54,10 +88,16 @@ public class ElasticIndexerRunner implements CommandLineRunner { private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexerRunner.class); @Autowired + private IDataResourceDao dataResourceDao; + @Autowired private ISchemaRecordDao schemaRecordDao; @Autowired private IDataRecordDao dataRecordDao; @Autowired + private MetastoreConfiguration schemaConfig; + @Autowired + private MetastoreConfiguration metadataConfig; + @Autowired private IUrl2PathDao url2PathDao; /** @@ -76,90 +116,113 @@ public void run(String... args) throws Exception { .build(); try { argueParser.parse(args); - if (updateDate == null) { - updateDate = new Date(0); + if (updateIndex) { + if (updateDate == null) { + updateDate = new Date(0); + } + if (indices == null) { + indices = new HashSet<>(); + } + updateElasticsearchIndex(); } - if (indices == null) { - indices = new HashSet<>(); + if (doMigration2DataCite) { + // Set adminitrative rights for reading. + JwtAuthenticationToken jwtAuthenticationToken = JwtBuilder.createServiceToken("migrationTool", RepoServiceRole.SERVICE_READ).getJwtAuthenticationToken(schemaConfig.getJwtSecret()); + SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken); + + migrateToVersion2(); } } catch (Exception ex) { + LOG.error("Error while executing runner!", ex); argueParser.usage(); System.exit(0); } - if (updateIndex) { - LOG.info("Start ElasticIndexer Runner for indices '{}' and update date '{}'", indices, updateDate); - LOG.info("No of schemas: '{}'", schemaRecordDao.count()); - // Try to determine URL of repository - List<SchemaRecord> findAllSchemas = schemaRecordDao.findAll(PageRequest.of(0, 3)).getContent(); - if (!findAllSchemas.isEmpty()) { - // There is at least one schema. - // Try to fetch baseURL from this - SchemaRecord get = findAllSchemas.get(0); - Url2Path findByPath = url2PathDao.findByPath(get.getSchemaDocumentUri()).get(0); - String baseUrl = findByPath.getUrl().split("/api/v1/schema")[0]; - LOG.trace("Found baseUrl: '{}'", baseUrl); - - if (indices.isEmpty()) { - LOG.info("Reindex all indices!"); - long noOfEntries = url2PathDao.count(); - long entriesPerPage = 50; - long page = 0; - // add also the schema registered in the schema registry - do { - List<SchemaRecord> allSchemas = schemaRecordDao.findAll(PageRequest.of((int) page, (int) entriesPerPage)).getContent(); - LOG.trace("Add '{}' schemas of '{}'", allSchemas.size(), noOfEntries); - for (SchemaRecord item : allSchemas) { - indices.add(item.getSchemaIdWithoutVersion()); - } - page++; - } while (page * entriesPerPage < noOfEntries); - } + } + + private void updateElasticsearchIndex() throws InterruptedException { + LOG.info("Start ElasticIndexer Runner for indices '{}' and update date '{}'", indices, updateDate); + LOG.info("No of schemas: '{}'", schemaRecordDao.count()); + // Try to determine URL of repository + List<SchemaRecord> findAllSchemas = schemaRecordDao.findAll(PageRequest.of(0, 3)).getContent(); + if (!findAllSchemas.isEmpty()) { + // There is at least one schema. + // Try to fetch baseURL from this + SchemaRecord get = findAllSchemas.get(0); + Url2Path findByPath = url2PathDao.findByPath(get.getSchemaDocumentUri()).get(0); + String baseUrl = findByPath.getUrl().split("/api/v1/schema")[0]; + LOG.trace("Found baseUrl: '{}'", baseUrl); + + determineIndices(indices); - for (String index : indices) { - LOG.info("Reindex '{}'", index); - List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaIdAndLastUpdateAfter(index, updateDate.toInstant()); - List<SchemaRecord> findSchemaBySchemaId = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(index + "/"); - LOG.trace("Search for documents for schema '{}' and update date '{}'", index, updateDate); - LOG.trace("No of documents: '{}'", findBySchemaId.size()); - for (DataRecord item : findBySchemaId) { - MetadataRecord result = toMetadataRecord(item, baseUrl); - LOG.trace("Sending CREATE event."); - messagingService.orElse(new LogfileMessagingService()). - send(MetadataResourceMessage.factoryCreateMetadataMessage(result, this.getClass().toString(), ControllerUtils.getLocalHostname())); - } - LOG.trace("Search for alternative schemaId (given as URL)"); - DataRecord templateRecord = new DataRecord(); - for (SchemaRecord debug : findSchemaBySchemaId) { - templateRecord.setSchemaId(debug.getSchemaIdWithoutVersion()); - templateRecord.setSchemaVersion(debug.getVersion()); - List<Url2Path> findByPath1 = url2PathDao.findByPath(debug.getSchemaDocumentUri()); - for (Url2Path path : findByPath1) { - LOG.trace("SchemaRecord: '{}'", debug); - List<DataRecord> findBySchemaUrl = dataRecordDao.findBySchemaIdAndLastUpdateAfter(path.getUrl(), updateDate.toInstant()); - LOG.trace("Search for documents for schema '{}' and update date '{}'", path.getUrl(), updateDate); - LOG.trace("No of documents: '{}'", findBySchemaUrl.size()); - for (DataRecord item : findBySchemaUrl) { - templateRecord.setMetadataId(item.getMetadataId()); - templateRecord.setVersion(item.getVersion()); - MetadataRecord result = toMetadataRecord(templateRecord, baseUrl); - LOG.trace("Sending CREATE event."); - messagingService.orElse(new LogfileMessagingService()). - send(MetadataResourceMessage.factoryCreateMetadataMessage(result, this.getClass().toString(), ControllerUtils.getLocalHostname())); - } - } - } + for (String index : indices) { + LOG.info("Reindex '{}'", index); + List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaIdAndLastUpdateAfter(index, updateDate.toInstant()); + LOG.trace("Search for documents for schema '{}' and update date '{}'", index, updateDate); + LOG.trace("No of documents: '{}'", findBySchemaId.size()); + for (DataRecord item : findBySchemaId) { + MetadataRecord result = toMetadataRecord(item, baseUrl); + LOG.trace("Sending CREATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryCreateMetadataMessage(result, this.getClass().toString(), ControllerUtils.getLocalHostname())); } - Thread.sleep(5000); + indexAlternativeSchemaIds(index, baseUrl); } + Thread.sleep(5000); + } + + LOG.trace("Finished ElasticIndexerRunner!"); + } + + private void determineIndices(Set<String> indices) { + if (indices.isEmpty()) { + LOG.info("Reindex all indices!"); + long noOfEntries = url2PathDao.count(); + long entriesPerPage = 50; + long page = 0; + // add also the schema registered in the schema registry + do { + List<SchemaRecord> allSchemas = schemaRecordDao.findAll(PageRequest.of((int) page, (int) entriesPerPage)).getContent(); + LOG.trace("Add '{}' schemas of '{}'", allSchemas.size(), noOfEntries); + for (SchemaRecord item : allSchemas) { + indices.add(item.getSchemaIdWithoutVersion()); + } + page++; + } while (page * entriesPerPage < noOfEntries); + } + + } - LOG.trace("Finished ElasticIndexerRunner!"); + private void indexAlternativeSchemaIds(String index, String baseUrl) { + LOG.trace("Search for alternative schemaId (given as URL)"); + List<SchemaRecord> findSchemaBySchemaId = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(index + "/"); + DataRecord templateRecord = new DataRecord(); + for (SchemaRecord debug : findSchemaBySchemaId) { + templateRecord.setSchemaId(debug.getSchemaIdWithoutVersion()); + templateRecord.setSchemaVersion(debug.getVersion()); + List<Url2Path> findByPath1 = url2PathDao.findByPath(debug.getSchemaDocumentUri()); + for (Url2Path path : findByPath1) { + LOG.trace("SchemaRecord: '{}'", debug); + List<DataRecord> findBySchemaUrl = dataRecordDao.findBySchemaIdAndLastUpdateAfter(path.getUrl(), updateDate.toInstant()); + LOG.trace("Search for documents for schema '{}' and update date '{}'", path.getUrl(), updateDate); + LOG.trace("No of documents: '{}'", findBySchemaUrl.size()); + for (DataRecord item : findBySchemaUrl) { + templateRecord.setMetadataId(item.getMetadataId()); + templateRecord.setVersion(item.getVersion()); + MetadataRecord result = toMetadataRecord(templateRecord, baseUrl); + LOG.trace("Sending CREATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryCreateMetadataMessage(result, this.getClass().toString(), ControllerUtils.getLocalHostname())); + } + } } + } /** * Transform DataRecord to MetadataRecord. * - * @param dataRecord DataRecord holding all information about metadata document. + * @param dataRecord DataRecord holding all information about metadata + * document. * @param baseUrl Base URL for accessing service. * @return MetadataRecord of metadata document. */ @@ -187,4 +250,115 @@ private String toSchemaUrl(DataRecord dataRecord, String baseUrl) { schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImpl.class).getSchemaDocumentById(dataRecord.getSchemaId(), dataRecord.getVersion(), null, null)).toUri(); return schemaUrl; } + + private void migrateToVersion2() throws InterruptedException { + LOG.info("Start Migrate2DataCite Runner for migrating database from version 1 to version 2."); + // Try to determine URL of repository + // Search for resource type of MetadataSchemaRecord + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + spec.or(ResourceTypeSpec.toSpecification(ResourceType.createResourceType(SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL))); + Pageable pgbl = PageRequest.of(0, 1); + long totalElements = queryDataResources(spec, pgbl).getTotalElements(); + if (totalElements == 0) { + // Migrate all schemas... + migrateAllSchemasToDataciteVersion2(); + migrateAllMetadataDocumentsToDataciteVersion2(); + Thread.sleep(5000); + } + + LOG.trace("Finished Migrate2DataCite!"); + } + + /** + * Migrate dataresources of schemas using version 1 to version 2. + */ + private void migrateAllSchemasToDataciteVersion2() { + Specification spec; + spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE, ResourceType.TYPE_GENERAL.DATASET)); + int pageNumber = 0; + int pageSize = 1; + Pageable pgbl = PageRequest.of(pageNumber, pageSize); + Page<DataResource> queryDataResources; + do { + queryDataResources = queryDataResources(spec, pgbl); + for (DataResource schema : queryDataResources.getContent()) { + migrateSchemaToDataciteVersion2(schema); + } + } while (queryDataResources.getTotalPages() > 1); + } + + /** + * Migrate dataresources of schemas using version 1 to version 2. + */ + private void migrateSchemaToDataciteVersion2(DataResource schema) { + long version = Long.parseLong(schema.getVersion()); + String id = schema.getId(); + for (int versionNo = 1; versionNo <= version; versionNo++) { + DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); + // Remove type from first title with type 'OTHER' + for (Title title : recordByIdAndVersion.getTitles()) { + if (title.getTitleType() == Title.TYPE.OTHER) { + title.setTitleType(null); + break; + } + } + // Set resource type to new definition of version 2 ('...'_Schema) + ResourceType resourceType = recordByIdAndVersion.getResourceType(); + resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); + resourceType.setValue(recordByIdAndVersion.getFormats().iterator().next() + DataResourceRecordUtil.SCHEMA_SUFFIX); + // Save migrated version + dataResourceDao.save(recordByIdAndVersion); + } + } + + /** + * Migrate dataresources of schemas using version 1 to version 2. + */ + private void migrateAllMetadataDocumentsToDataciteVersion2() { + Specification spec; + spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE, ResourceType.TYPE_GENERAL.DATASET)); + int pageNumber = 0; + int pageSize = 1; + Pageable pgbl = PageRequest.of(pageNumber, pageSize); + Page<DataResource> queryDataResources; + do { + queryDataResources = queryDataResources(spec, pgbl); + for (DataResource schema : queryDataResources.getContent()) { + migrateMetadataDocumentsToDataciteVersion2(schema); + } + } while (queryDataResources.getTotalPages() > 1); + } + + /** + * Migrate dataresources of schemas using version 1 to version 2. + */ + private void migrateMetadataDocumentsToDataciteVersion2(DataResource schema) { + long version = Long.parseLong(schema.getVersion()); + String id = schema.getId(); + // Get resource type of schema.... + String format = null; + for (RelatedIdentifier identifier : schema.getRelatedIdentifiers()) { + if (identifier.getRelationType() == RelatedIdentifier.RELATION_TYPES.ISDERIVEDFROM) { + String schemaUrl = identifier.getValue(); + Optional<Url2Path> findByUrl = url2PathDao.findByUrl(schemaUrl); + format = findByUrl.get().getType().toString(); + } + } + for (int versionNo = 1; versionNo <= version; versionNo++) { + DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + // Remove type from first title with type 'OTHER' + for (Title title : recordByIdAndVersion.getTitles()) { + if (title.getTitleType() == Title.TYPE.OTHER) { + title.setTitleType(null); + break; + } + } + // Set resource type to new definition of version 2 ('...'_Schema) + ResourceType resourceType = recordByIdAndVersion.getResourceType(); + resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); + resourceType.setValue(format + DataResourceRecordUtil.METADATA_SUFFIX); + // Save migrated version + dataResourceDao.save(recordByIdAndVersion); + } + } } From c4995b675cb0e46e6d123c31443e81a7ed7e4b38 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 11 Sep 2024 09:10:14 +0200 Subject: [PATCH 067/181] Fix wrong relation type for schema. --- .../datamanager/metastore2/runner/ElasticIndexerRunner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index fe1a1dcc..a69e4284 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -338,9 +338,10 @@ private void migrateMetadataDocumentsToDataciteVersion2(DataResource schema) { // Get resource type of schema.... String format = null; for (RelatedIdentifier identifier : schema.getRelatedIdentifiers()) { - if (identifier.getRelationType() == RelatedIdentifier.RELATION_TYPES.ISDERIVEDFROM) { + if (identifier.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { String schemaUrl = identifier.getValue(); Optional<Url2Path> findByUrl = url2PathDao.findByUrl(schemaUrl); + LOG.trace("Found entry for schema: {}", findByUrl.get().toString()); format = findByUrl.get().getType().toString(); } } From 3577d6c9945fc56a54247894967e78ac10b7b640 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 11 Sep 2024 15:56:14 +0200 Subject: [PATCH 068/181] Tests with Javers while migrating database. --- .../datamanager/metastore2/runner/ElasticIndexerRunner.java | 2 ++ .../datamanager/metastore2/util/DataResourceRecordUtil.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index a69e4284..857d822d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -308,6 +308,7 @@ private void migrateSchemaToDataciteVersion2(DataResource schema) { resourceType.setValue(recordByIdAndVersion.getFormats().iterator().next() + DataResourceRecordUtil.SCHEMA_SUFFIX); // Save migrated version dataResourceDao.save(recordByIdAndVersion); + schemaConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); } } @@ -360,6 +361,7 @@ private void migrateMetadataDocumentsToDataciteVersion2(DataResource schema) { resourceType.setValue(format + DataResourceRecordUtil.METADATA_SUFFIX); // Save migrated version dataResourceDao.save(recordByIdAndVersion); + metadataConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); } } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index e56211fa..eb6cc371 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -828,6 +828,11 @@ public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metast Page<DataResource> dataResource; try { dataResource = metastoreProperties.getDataResourceService().findAllVersions(recordId, null); + if (LOG.isTraceEnabled()) { + for (DataResource item : dataResource.getContent()) { + LOG.trace("Id: '{}' - Version: '{}' -> Type: '{}'", item.getId(), item.getVersion(), item.getResourceType().toString()); + } + } } catch (ResourceNotFoundException ex) { ex.setDetail("Document with ID '" + recordId + "' doesn't exist!"); throw ex; From dabf5dd933c8882155efa63f5510f251b6b3cfa9 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 13 Sep 2024 15:29:51 +0200 Subject: [PATCH 069/181] Fix bug while migrating old versions of data resources. --- .../runner/ElasticIndexerRunner.java | 191 +++++++++++++----- 1 file changed, 145 insertions(+), 46 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 857d822d..3f01e33f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -36,6 +36,7 @@ import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.util.DataResourceUtils; import edu.kit.datamanager.security.filter.JwtAuthenticationToken; import edu.kit.datamanager.service.IMessagingService; import edu.kit.datamanager.service.impl.LogfileMessagingService; @@ -50,6 +51,7 @@ import org.springframework.stereotype.Component; import java.util.*; +import org.javers.core.Javers; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; @@ -57,9 +59,11 @@ import org.springframework.transaction.annotation.Transactional; /** - * Class for indexing all metadata documents of given schemas Arguments have to + * This class contains 2 runners: + * <ul><li>Runner for indexing all metadata documents of given schemas Arguments have to * start with at least 'reindex' followed by all indices which have to be - * reindexed. If no indices are given all indices will be reindexed. + * reindexed. If no indices are given all indices will be reindexed.</li> + * <li>Runner for migrating dataresources from version 1 to version2. */ @Component @Transactional @@ -71,6 +75,9 @@ public class ElasticIndexerRunner implements CommandLineRunner { * executed only once. * *************************************************************************** */ + /** + * Start migration to version 2 + */ @Parameter(names = {"--migrate2DataCite"}, description = "Migrate database from version 1.X to 2.X.") boolean doMigration2DataCite; @@ -79,27 +86,61 @@ public class ElasticIndexerRunner implements CommandLineRunner { * Parameters for reindexing elasticsearch. This should be executed only once. * *************************************************************************** */ + /** + * Start reindexing... + */ @Parameter(names = {"--reindex"}, description = "Elasticsearch index should be build from existing documents.") boolean updateIndex; + /** + * Restrict reindexing to provided indices only. + */ @Parameter(names = {"--indices", "-i"}, description = "Only for given indices (comma separated) or all indices if not present.") Set<String> indices; + /** + * Restrict reindexing to dataresources new than given date. + */ @Parameter(names = {"--updateDate", "-u"}, description = "Starting reindexing only for documents updated at earliest on update date.") Date updateDate; - + /** + * Logger. + */ private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexerRunner.class); + /** + * DAO for all data resources. + */ @Autowired private IDataResourceDao dataResourceDao; + /** + * DAO for all schema records. + */ @Autowired private ISchemaRecordDao schemaRecordDao; + /** + * DAO for all data records. + */ @Autowired private IDataRecordDao dataRecordDao; + /** + * DAO for linking URLS to files and format. + */ + @Autowired + private IUrl2PathDao url2PathDao; + /** + * Instance for managing versions. + */ + @Autowired + private Javers javers; + /** + * Instance of schema repository. + */ @Autowired private MetastoreConfiguration schemaConfig; + /** + * Instande of metadata reository. + * + */ @Autowired private MetastoreConfiguration metadataConfig; - @Autowired - private IUrl2PathDao url2PathDao; - /** * Optional messagingService bean may or may not be available, depending on a * service's configuration. If messaging capabilities are disabled, this bean @@ -107,7 +148,11 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Autowired private Optional<IMessagingService> messagingService; - + /** + * Start runner for actions before starting service. + * @param args Arguments for the runner. + * @throws Exception Something went wrong. + */ @Override @SuppressWarnings({"StringSplitter", "JavaUtilDate"}) public void run(String... args) throws Exception { @@ -129,7 +174,7 @@ public void run(String... args) throws Exception { // Set adminitrative rights for reading. JwtAuthenticationToken jwtAuthenticationToken = JwtBuilder.createServiceToken("migrationTool", RepoServiceRole.SERVICE_READ).getJwtAuthenticationToken(schemaConfig.getJwtSecret()); SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken); - + migrateToVersion2(); } } catch (Exception ex) { @@ -138,7 +183,10 @@ public void run(String... args) throws Exception { System.exit(0); } } - + /** + * Start runner to reindex dataresources according to the given parameters. + * @throws InterruptedException Something went wrong. + */ private void updateElasticsearchIndex() throws InterruptedException { LOG.info("Start ElasticIndexer Runner for indices '{}' and update date '{}'", indices, updateDate); LOG.info("No of schemas: '{}'", schemaRecordDao.count()); @@ -172,7 +220,12 @@ private void updateElasticsearchIndex() throws InterruptedException { LOG.trace("Finished ElasticIndexerRunner!"); } - + + /** + * Determine all indices if an empty set is provided. + * Otherwise return provided set without any change. + * @param indices Indices which should be reindexed. + */ private void determineIndices(Set<String> indices) { if (indices.isEmpty()) { LOG.info("Reindex all indices!"); @@ -250,7 +303,11 @@ private String toSchemaUrl(DataRecord dataRecord, String baseUrl) { schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImpl.class).getSchemaDocumentById(dataRecord.getSchemaId(), dataRecord.getVersion(), null, null)).toUri(); return schemaUrl; } - + + /** + * Migrate all data resources from version 1 to version 2. + * @throws InterruptedException + */ private void migrateToVersion2() throws InterruptedException { LOG.info("Start Migrate2DataCite Runner for migrating database from version 1 to version 2."); // Try to determine URL of repository @@ -289,31 +346,50 @@ private void migrateAllSchemasToDataciteVersion2() { /** * Migrate dataresources of schemas using version 1 to version 2. + * @param schema Current version of schema document. */ private void migrateSchemaToDataciteVersion2(DataResource schema) { long version = Long.parseLong(schema.getVersion()); String id = schema.getId(); - for (int versionNo = 1; versionNo <= version; versionNo++) { - DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); - // Remove type from first title with type 'OTHER' - for (Title title : recordByIdAndVersion.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER) { - title.setTitleType(null); - break; - } + // Migrate all versions of schema. + for (long versionNo = 1; versionNo <= version; versionNo++) { + saveSchema(id, versionNo); + } + } + + + /** + * Migrate metadata of schema document from version 1 to version 2 and store new version in the database. + * @param id ID of the schema document. + * @param version Version of the schema document. + * @param format Format of the schema document. (XML/JSON) + */ + private void saveSchema(String id, long version) { + DataResource currentVersion = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); + DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentVersion); + // Remove type from first title with type 'OTHER' + for (Title title : recordByIdAndVersion.getTitles()) { + if (title.getTitleType() == Title.TYPE.OTHER) { + title.setTitleType(null); + break; } - // Set resource type to new definition of version 2 ('...'_Schema) - ResourceType resourceType = recordByIdAndVersion.getResourceType(); - resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); - resourceType.setValue(recordByIdAndVersion.getFormats().iterator().next() + DataResourceRecordUtil.SCHEMA_SUFFIX); - // Save migrated version - dataResourceDao.save(recordByIdAndVersion); - schemaConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); } + // Set resource type to new definition of version 2 ('...'_Schema) + ResourceType resourceType = recordByIdAndVersion.getResourceType(); + resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); + resourceType.setValue(recordByIdAndVersion.getFormats().iterator().next() + DataResourceRecordUtil.SCHEMA_SUFFIX); + // Save migrated version + LOG.trace("Persisting created resource."); + dataResourceDao.save(recordByIdAndVersion); + + //Capture state change + LOG.trace("Capturing audit information."); + schemaConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); + } /** - * Migrate dataresources of schemas using version 1 to version 2. + * Migrate dataresources of metadata documents from version 1 to version 2. */ private void migrateAllMetadataDocumentsToDataciteVersion2() { Specification spec; @@ -331,14 +407,18 @@ private void migrateAllMetadataDocumentsToDataciteVersion2() { } /** - * Migrate dataresources of schemas using version 1 to version 2. + * Migrate all versions of a dataresource of metadata documents from version 1 + * to version 2. + * + * @param metadataDocument Current version of metadata document. */ - private void migrateMetadataDocumentsToDataciteVersion2(DataResource schema) { - long version = Long.parseLong(schema.getVersion()); - String id = schema.getId(); + private void migrateMetadataDocumentsToDataciteVersion2(DataResource metadataDocument) { + long version = Long.parseLong(metadataDocument.getVersion()); + String id = metadataDocument.getId(); + // Get resource type of schema.... String format = null; - for (RelatedIdentifier identifier : schema.getRelatedIdentifiers()) { + for (RelatedIdentifier identifier : metadataDocument.getRelatedIdentifiers()) { if (identifier.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { String schemaUrl = identifier.getValue(); Optional<Url2Path> findByUrl = url2PathDao.findByUrl(schemaUrl); @@ -346,22 +426,41 @@ private void migrateMetadataDocumentsToDataciteVersion2(DataResource schema) { format = findByUrl.get().getType().toString(); } } + // Migrate all versions of data resource. for (int versionNo = 1; versionNo <= version; versionNo++) { - DataResource recordByIdAndVersion = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); - // Remove type from first title with type 'OTHER' - for (Title title : recordByIdAndVersion.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER) { - title.setTitleType(null); - break; - } + saveMetadata(id, versionNo, format); + } + } + + /** + * Migrate metadata of metadata document from version 1 to version 2 and store + * new version in the database. + * @param id ID of the metadata document. + * @param version Version of the metadata document. + * @param format Format of the metadata document. (XML/JSON) + */ + private void saveMetadata(String id, long version, String format) { + DataResource currentVersion = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentVersion); + // Remove type from first title with type 'OTHER' + for (Title title : recordByIdAndVersion.getTitles()) { + if (title.getTitleType() == Title.TYPE.OTHER) { + title.setTitleType(null); + break; } - // Set resource type to new definition of version 2 ('...'_Schema) - ResourceType resourceType = recordByIdAndVersion.getResourceType(); - resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); - resourceType.setValue(format + DataResourceRecordUtil.METADATA_SUFFIX); - // Save migrated version - dataResourceDao.save(recordByIdAndVersion); - metadataConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); } + // Set resource type to new definition of version 2 ('...'_Schema) + ResourceType resourceType = recordByIdAndVersion.getResourceType(); + resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); + resourceType.setValue(format + DataResourceRecordUtil.METADATA_SUFFIX); + + // Save migrated version + LOG.trace("Persisting created resource."); + dataResourceDao.save(recordByIdAndVersion); + + //capture state change + LOG.trace("Capturing audit information."); + metadataConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); + } } From 32e00e7ac7493d0f8b3ec3aec7f229256404a36c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 14:17:06 +0000 Subject: [PATCH 070/181] Bump org.springframework.data:spring-data-elasticsearch Bumps [org.springframework.data:spring-data-elasticsearch](https://github.com/spring-projects/spring-data-elasticsearch) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/spring-projects/spring-data-elasticsearch/releases) - [Commits](https://github.com/spring-projects/spring-data-elasticsearch/compare/5.3.3...5.3.4) --- updated-dependencies: - dependency-name: org.springframework.data:spring-data-elasticsearch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e0f5621d..bc35c5a9 100644 --- a/build.gradle +++ b/build.gradle @@ -113,7 +113,7 @@ dependencies { implementation "edu.kit.datamanager:service-base:1.3.1" // elasticsearch (since service-base 1.1.0) - implementation "org.springframework.data:spring-data-elasticsearch:5.3.3" + implementation "org.springframework.data:spring-data-elasticsearch:5.3.4" // DOIP SDK implementation "net.dona.doip:doip-sdk:2.2.0" From cbe6e83f615aca457dfd8540272ddab19a7cd803 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 14:17:15 +0000 Subject: [PATCH 071/181] Bump com.google.errorprone:error_prone_core from 2.31.0 to 2.32.0 Bumps [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) from 2.31.0 to 2.32.0. - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.31.0...v2.32.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e0f5621d..8c0747d7 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ ext { springDocVersion = '2.6.0' javersVersion = '7.6.1' keycloakVersion = '19.0.0' - errorproneVersion = '2.31.0' + errorproneVersion = '2.32.0' // directory for generated code snippets during tests snippetsDir = file("build/generated-snippets") } From 6f593d9f23bc76e54941296c4d864d7c46227538 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:12:03 +0000 Subject: [PATCH 072/181] Bump commons-io:commons-io from 2.16.1 to 2.17.0 Bumps commons-io:commons-io from 2.16.1 to 2.17.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 22ed1b22..43af066f 100644 --- a/build.gradle +++ b/build.gradle @@ -99,7 +99,7 @@ dependencies { implementation "com.h2database:h2:2.3.232" // apache - implementation "commons-io:commons-io:2.16.1" + implementation "commons-io:commons-io:2.17.0" implementation "org.apache.tika:tika-core:2.9.2" // JSON validator From d4893f1ff6c8b5053f1e07f6aa48127b5743adfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:12:07 +0000 Subject: [PATCH 073/181] Bump javersVersion from 7.6.1 to 7.6.2 Bumps `javersVersion` from 7.6.1 to 7.6.2. Updates `org.javers:javers-spring-boot-starter-sql` from 7.6.1 to 7.6.2 - [Release notes](https://github.com/javers/javers/releases) - [Commits](https://github.com/javers/javers/compare/javers-7.6.1...javers-7.6.2) Updates `org.javers:javers-core` from 7.6.1 to 7.6.2 - [Release notes](https://github.com/javers/javers/releases) - [Commits](https://github.com/javers/javers/compare/javers-7.6.1...javers-7.6.2) --- updated-dependencies: - dependency-name: org.javers:javers-spring-boot-starter-sql dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.javers:javers-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 22ed1b22..8c90ed74 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ group = 'edu.kit.datamanager' ext { // versions of dependencies springDocVersion = '2.6.0' - javersVersion = '7.6.1' + javersVersion = '7.6.2' keycloakVersion = '19.0.0' errorproneVersion = '2.32.0' // directory for generated code snippets during tests From 06f8aea2bb18e17c75ecbef701a72d3ba3f5dff9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:12:12 +0000 Subject: [PATCH 074/181] Bump org.springframework.boot from 3.3.3 to 3.3.4 Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 3.3.3 to 3.3.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.3...v3.3.4) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 22ed1b22..19c3787c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.3.3' + id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.10' id 'io.freefair.maven-publish-java' version '8.10' From c0d4272e0138d86169eb379c2b3331a876c106b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:12:15 +0000 Subject: [PATCH 075/181] Bump com.networknt:json-schema-validator from 1.5.1 to 1.5.2 Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.5.1 to 1.5.2. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.1...1.5.2) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 22ed1b22..c9dae6ab 100644 --- a/build.gradle +++ b/build.gradle @@ -103,7 +103,7 @@ dependencies { implementation "org.apache.tika:tika-core:2.9.2" // JSON validator - implementation "com.networknt:json-schema-validator:1.5.1" + implementation "com.networknt:json-schema-validator:1.5.2" // XML validator // https://mvnrepository.com/artifact/xerces/xercesImpl implementation 'xerces:xercesImpl:2.12.2' From f1823dd275d06240e75c0539fa4fd2b61b74a51d Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 24 Sep 2024 16:53:40 +0200 Subject: [PATCH 076/181] Add new format of datacite for indexing. --- .../metastore2/domain/AclRecord.java | 8 +- .../metastore2/domain/ElasticWrapper.java | 91 +++++++++++++++++++ .../metastore2/web/IMetadataController.java | 4 +- .../metastore2/web/IMetadataControllerV2.java | 4 +- .../web/impl/MetadataControllerImpl.java | 11 +-- .../web/impl/MetadataControllerImplV2.java | 8 +- 6 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java index 34c4867f..99342bef 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/AclRecord.java @@ -34,9 +34,13 @@ @JsonIgnoreProperties(ignoreUnknown = true) @Data public class AclRecord implements Serializable { - + /** + * Resource type for elastic search indexing. + */ public static final String RESOURCE_TYPE = "application/vnd.datamanager.acl+json"; - + /** + * Media type for elastic search indexing. + */ public static final MediaType ACL_RECORD_MEDIA_TYPE = MediaType.valueOf(RESOURCE_TYPE); @NotNull(message = "A list of access control entries with at least access for READ.") diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java new file mode 100644 index 00000000..854e930f --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java @@ -0,0 +1,91 @@ +/* + * Copyright 2023 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.domain; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.Date.DATE_TYPE; +import jakarta.persistence.OneToMany; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.Date; +import lombok.Data; +import java.util.Set; +import java.util.HashSet; +import org.springframework.data.elasticsearch.annotations.DateFormat; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; +import org.springframework.http.MediaType; + +/** + * Record holding metadata document + list of SIDs allowed to at least read the + * document. Structure is similar to elastic content of base-repo. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@Data +public class ElasticWrapper { + + /** + * Resource type for elastic search indexing. + */ + public static final String RESOURCE_TYPE = "application/vnd.datamanager.acl+json"; + /** + * Media type for elastic search indexing. + */ + public static final MediaType ACL_RECORD_MEDIA_TYPE = MediaType.valueOf(RESOURCE_TYPE); + + private String id; + + private String pid; + + @Field(type = FieldType.Date, format = DateFormat.basic_date_time) + private Date created; + + @Field(type = FieldType.Date, format = DateFormat.basic_date_time) + private Date lastUpdate; + + @NotNull(message = "A list of access control entries with at least access for READ.") + @OneToMany(cascade = jakarta.persistence.CascadeType.ALL, orphanRemoval = true) + private final Set<String> read; + + @NotBlank(message = "The metadata record.") + private DataResource metadata; + + @NotBlank(message = "The metadata document.") + private Object metadataDocument; + + + public ElasticWrapper(DataResource resource) { + id = resource.getId(); + pid = (resource.getIdentifier() != null) ? resource.getIdentifier().getValue() : null; + metadata = resource; + read = new HashSet<String>(); + resource.getAcls().forEach(entry -> { + String sid = entry.getSid(); + if (entry.getPermission().atLeast(PERMISSION.READ)) { + read.add(sid); + } + }); + + resource.getDates().stream().filter(d -> (DATE_TYPE.CREATED.equals(d.getType()))).forEachOrdered(d -> { + created = Date.from(d.getValue()); + }); + + lastUpdate = Date.from(resource.getLastUpdate()); + } + +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java index 4bc3ba2b..3c4172f0 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataController.java @@ -15,7 +15,7 @@ */ package edu.kit.datamanager.metastore2.web; -import edu.kit.datamanager.metastore2.domain.AclRecord; +import edu.kit.datamanager.metastore2.domain.ElasticWrapper; import edu.kit.datamanager.metastore2.domain.MetadataRecord; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -94,7 +94,7 @@ ResponseEntity<MetadataRecord> getRecordById(@Parameter(description = "The recor @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.acl+json"}) - ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + ResponseEntity<ElasticWrapper> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, WebRequest wr, HttpServletResponse hsr); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index fcdf797f..e8919e9e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -15,7 +15,7 @@ */ package edu.kit.datamanager.metastore2.web; -import edu.kit.datamanager.metastore2.domain.AclRecord; +import edu.kit.datamanager.metastore2.domain.ElasticWrapper; import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; import io.swagger.v3.oas.annotations.Operation; @@ -106,7 +106,7 @@ ResponseEntity<ContentInformation> getContentInformationById(@Parameter(descript @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/vnd.datamanager.acl+json"}) - ResponseEntity<AclRecord> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, + ResponseEntity<ElasticWrapper> getAclById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, WebRequest wr, HttpServletResponse hsr); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index f3452c8b..14ecd911 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -238,8 +238,9 @@ public ResponseEntity<MetadataRecord> getRecordById( return ResponseEntity.ok().eTag("\"" + etag + "\"").body(metadataRecord); } + @Override - public ResponseEntity<AclRecord> getAclById( + public ResponseEntity<ElasticWrapper> getAclById( @PathVariable(value = "id") String id, @RequestParam(value = "version", required = false) Long version, WebRequest wr, @@ -250,11 +251,9 @@ public ResponseEntity<AclRecord> getAclById( throw new AccessForbiddenException("Only for services!"); } - MetadataRecord metadataRecord = MetadataRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version, true); - MetadataRecordUtil.fixMetadataDocumentUri(metadataRecord); - AclRecord aclRecord = new AclRecord(); - aclRecord.setAcl(metadataRecord.getAcl()); - aclRecord.setMetadataRecord(metadataRecord); + DataResource metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + DataResourceRecordUtil.fixSchemaUrl(metadataRecord); + ElasticWrapper aclRecord = new ElasticWrapper(metadataRecord); return ResponseEntity.ok().body(aclRecord); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index b03f81af..30f162be 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -27,7 +27,6 @@ import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import edu.kit.datamanager.metastore2.domain.AclRecord; import edu.kit.datamanager.metastore2.domain.SchemaRecord; import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; @@ -85,6 +84,7 @@ import java.util.function.UnaryOperator; import static edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE.INTERNAL; +import edu.kit.datamanager.metastore2.domain.ElasticWrapper; /** * Controller for metadata documents. @@ -300,7 +300,7 @@ public ResponseEntity<ContentInformation> getContentInformationById( } @Override - public ResponseEntity<AclRecord> getAclById( + public ResponseEntity<ElasticWrapper> getAclById( @PathVariable(value = "id") String id, @RequestParam(value = "version", required = false) Long version, WebRequest wr, @@ -313,9 +313,7 @@ public ResponseEntity<AclRecord> getAclById( DataResource metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); DataResourceRecordUtil.fixSchemaUrl(metadataRecord); - AclRecord aclRecord = new AclRecord(); - aclRecord.setAcl(metadataRecord.getAcls()); -// aclRecord.setDataResource(metadataRecord); + ElasticWrapper aclRecord = new ElasticWrapper(metadataRecord); return ResponseEntity.ok().body(aclRecord); } From b194144e0beccc7db1923f83f1a1e5c6305ea8a6 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 24 Sep 2024 16:55:13 +0200 Subject: [PATCH 077/181] Add hostname to containers. --- docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 832d5025..ffa448ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,6 +21,7 @@ services: elasticsearch: image: elasticsearch:${ELASTICSEARCH_VERSION} container_name: elastic4indexing + hostname: elastic.docker environment: - discovery.type=single-node - xpack.security.enabled=false @@ -42,6 +43,7 @@ services: depends_on: - elasticsearch container_name: rabbitmq4indexing + hostname: rabbitmq.docker environment: - HOSTNAMES=rabbitmq.docker - RABBITMQ_DEFAULT_USER=${RABBIT_MQ_USER} @@ -72,6 +74,7 @@ services: metastore: image: ${PREFIX4DOCKER}/metastore2:${METASTORE_VERSION} container_name: metastore.docker + hostname: metastore.docker environment: - HOSTNAMES=metastore.docker - REPO_SEARCH_ENABLED=true From 6290d5b4867d5cfaf68c98aa6420fa3c2009cf0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:03:09 +0000 Subject: [PATCH 078/181] Bump org.mockito:mockito-core from 5.13.0 to 5.14.1 Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.13.0 to 5.14.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.13.0...v5.14.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 422064f2..afb343ec 100644 --- a/build.gradle +++ b/build.gradle @@ -127,7 +127,7 @@ dependencies { testImplementation "org.springframework.security:spring-security-test" //Java 11 Support - testImplementation "org.mockito:mockito-core:5.13.0" + testImplementation "org.mockito:mockito-core:5.14.1" testImplementation "junit:junit:4.13.2" testImplementation "com.github.stefanbirkner:system-lambda:1.2.1" From 8851c9fc22260baa7ba4eb1286336582efdb9b6b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 2 Oct 2024 14:40:53 +0200 Subject: [PATCH 079/181] Even if old API is used. URLs point to the new API. --- .../metastore2/runner/ElasticIndexerRunner.java | 8 ++++---- .../metastore2/util/DataResourceRecordUtil.java | 2 +- .../datamanager/metastore2/util/MetadataRecordUtil.java | 5 ++--- .../metastore2/util/MetadataSchemaRecordUtil.java | 4 ++-- .../metastore2/test/JsonSchemaRegistryControllerTest.java | 6 ++---- .../metastore2/test/MetadataControllerFilterTest.java | 2 +- .../metastore2/test/MetadataControllerTest.java | 4 ++-- .../MetadataControllerTestWithAuthenticationEnabled.java | 2 +- .../MetadataControllerTestWithInternalSchemaRegistry.java | 4 ++-- .../metastore2/test/SchemaRegistryControllerTest.java | 7 ++----- 10 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 3f01e33f..79f9f583 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -28,8 +28,8 @@ import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.METADATA_SUFFIX; import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.SCHEMA_SUFFIX; import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.queryDataResources; -import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; -import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImpl; +import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; +import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.domain.DataResource; @@ -280,7 +280,7 @@ private void indexAlternativeSchemaIds(String index, String baseUrl) { * @return MetadataRecord of metadata document. */ private MetadataRecord toMetadataRecord(DataRecord dataRecord, String baseUrl) { - String metadataIdWithVersion = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImpl.class).getMetadataDocumentById(dataRecord.getMetadataId(), dataRecord.getVersion(), null, null)).toUri(); + String metadataIdWithVersion = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(dataRecord.getMetadataId(), dataRecord.getVersion(), null, null)).toUri(); MetadataRecord returnValue = new MetadataRecord(); returnValue.setId(dataRecord.getMetadataId()); returnValue.setSchemaVersion(dataRecord.getSchemaVersion()); @@ -300,7 +300,7 @@ private MetadataRecord toMetadataRecord(DataRecord dataRecord, String baseUrl) { */ private String toSchemaUrl(DataRecord dataRecord, String baseUrl) { String schemaUrl; - schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImpl.class).getSchemaDocumentById(dataRecord.getSchemaId(), dataRecord.getVersion(), null, null)).toUri(); + schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(dataRecord.getSchemaId(), dataRecord.getVersion(), null, null)).toUri(); return schemaUrl; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index eb6cc371..92322b1e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -1176,7 +1176,7 @@ public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean curren public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { String metadataDocumentUri = metadataRecord.getMetadataDocumentUri(); metadataRecord - .setMetadataDocumentUri(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImpl.class + .setMetadataDocumentUri(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class ).getMetadataDocumentById(metadataRecord.getId(), metadataRecord.getRecordVersion(), null, null)).toUri().toString()); LOG.trace("Fix metadata document Uri '{}' -> '{}'", metadataDocumentUri, metadataRecord.getMetadataDocumentUri()); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 298ebd91..6093e255 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -25,7 +25,7 @@ import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; -import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; +import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; import edu.kit.datamanager.repo.domain.Date; import edu.kit.datamanager.repo.domain.*; @@ -54,7 +54,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -1041,7 +1040,7 @@ public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean curren public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { String metadataDocumentUri = metadataRecord.getMetadataDocumentUri(); metadataRecord - .setMetadataDocumentUri(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImpl.class + .setMetadataDocumentUri(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class ).getMetadataDocumentById(metadataRecord.getId(), metadataRecord.getRecordVersion(), null, null)).toUri().toString()); LOG.trace("Fix metadata document Uri '{}' -> '{}'", metadataDocumentUri, metadataRecord.getMetadataDocumentUri()); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 55f66904..648314af 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -31,7 +31,6 @@ import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; import edu.kit.datamanager.metastore2.validation.IValidator; -import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImpl; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; import edu.kit.datamanager.repo.domain.Date; import edu.kit.datamanager.repo.domain.*; @@ -67,6 +66,7 @@ import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeAcl; import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeEntry; +import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; /** * Utility class for handling json documents @@ -1291,7 +1291,7 @@ public static final void fixSchemaDocumentUri(MetadataSchemaRecord schemaRecord, * @return URI for accessing schema document. */ public static final URI getSchemaDocumentUri(MetadataSchemaRecord schemaRecord) { - return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImpl.class).getSchemaDocumentById(schemaRecord.getSchemaId(), schemaRecord.getSchemaVersion(), null, null)).toUri(); + return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(schemaRecord.getSchemaId(), schemaRecord.getSchemaVersion(), null, null)).toUri(); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java index 6a464bb9..8d6c9cdc 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java @@ -298,10 +298,8 @@ public void testCreateSchemaRecordWithLocationUri() throws Exception { String locationUri = result.getResponse().getHeader("Location"); String content = result.getResponse().getContentAsString(); - MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); - String content2 = result2.getResponse().getContentAsString(); - - Assert.assertEquals(content, content2); + // URL should point to API v2. Therefor accept header is not allowed. + this.mockMvc.perform(get(locationUri).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isNotAcceptable()); } @Test diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java index 9b8ab78f..8e457ec9 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java @@ -200,7 +200,7 @@ public void testFindRecordsBySchemaId() throws Exception { Assert.assertEquals(ResourceIdentifier.IdentifierType.URL, item.getSchema().getIdentifierType()); String schemaUrl = item.getSchema().getIdentifier(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); - Assert.assertTrue(schemaUrl.contains("/api/v1/schemas/")); + Assert.assertTrue(schemaUrl.contains("/api/v2/schemas/")); Assert.assertTrue(schemaUrl.contains(schemaId)); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index 2f4225f7..d2ebde97 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -884,7 +884,7 @@ public void testGetRecordById() throws Exception { Assert.assertEquals(IdentifierType.URL, result.getSchema().getIdentifierType()); String schemaUrl = result.getSchema().getIdentifier(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); - Assert.assertTrue(schemaUrl.contains("/api/v1/schemas/")); + Assert.assertTrue(schemaUrl.contains("/api/v2/schemas/")); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); @@ -901,7 +901,7 @@ public void testGetRecordByIdWithVersion() throws Exception { Assert.assertEquals(IdentifierType.URL, result.getSchema().getIdentifierType()); String schemaUrl = result.getSchema().getIdentifier(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); - Assert.assertTrue(schemaUrl.contains("/api/v1/schemas/")); + Assert.assertTrue(schemaUrl.contains("/api/v2/schemas/")); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java index 4c8aedb4..563a7e5e 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java @@ -898,7 +898,7 @@ public void testGetRecordByIdWithVersion() throws Exception { Assert.assertEquals(ResourceIdentifier.IdentifierType.URL, result.getSchema().getIdentifierType()); String schemaUrl = result.getSchema().getIdentifier(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); - Assert.assertTrue(schemaUrl.contains("/api/v1/schemas/")); + Assert.assertTrue(schemaUrl.contains("/api/v2/schemas/")); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java index 48e20072..464f464e 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java @@ -686,7 +686,7 @@ public void testGetRecordById() throws Exception { Assert.assertEquals(ResourceIdentifier.IdentifierType.URL, result.getSchema().getIdentifierType()); String schemaUrl = result.getSchema().getIdentifier(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); - Assert.assertTrue(schemaUrl.contains("/api/v1/schemas/")); + Assert.assertTrue(schemaUrl.contains("/api/v2/schemas/")); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); //Schema URI must not be the actual file URI but the link to the REST endpoint for downloading the schema Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); @@ -703,7 +703,7 @@ public void testGetRecordByIdWithVersion() throws Exception { Assert.assertEquals(ResourceIdentifier.IdentifierType.URL, result.getSchema().getIdentifierType()); String schemaUrl = result.getSchema().getIdentifier(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); - Assert.assertTrue(schemaUrl.contains("/api/v1/schemas/")); + Assert.assertTrue(schemaUrl.contains("/api/v2/schemas/")); Assert.assertTrue(schemaUrl.contains(SCHEMA_ID)); Assert.assertNotEquals("file:///tmp/dc.xml", result.getMetadataDocumentUri()); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index 0ce178f8..f341bac9 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -318,11 +318,8 @@ public void testCreateSchemaRecordWithLocationUri() throws Exception { file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/" + record.getSchemaId() + "?version=1")).andReturn(); String locationUri = result.getResponse().getHeader("Location"); String content = result.getResponse().getContentAsString(); - - MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); - String content2 = result2.getResponse().getContentAsString(); - - Assert.assertEquals(content, content2); + // Location Uri should point to API v2. Therefor accept header is not valid. + this.mockMvc.perform(get(locationUri).header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isNotAcceptable()); } @Test From 822fe18f8b71105a42503a13809cd2d017b84fd1 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 2 Oct 2024 15:46:12 +0200 Subject: [PATCH 080/181] Set old API to deprecated. --- .../metastore2/web/impl/FrontendControllerImpl.java | 1 - .../metastore2/web/impl/MetadataControllerImpl.java | 3 +-- .../metastore2/web/impl/SchemaRegistryControllerImpl.java | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java index e483898e..b42f93b9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/FrontendControllerImpl.java @@ -28,7 +28,6 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.util.UriComponentsBuilder; -import java.util.Arrays; import java.util.List; /** diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index 14ecd911..dd420c7d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -27,7 +27,6 @@ import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.domain.*; -import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; @@ -72,7 +71,6 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.nio.file.Path; import java.time.Instant; import java.util.*; @@ -85,6 +83,7 @@ @RequestMapping(value = "/api/v1/metadata") @Tag(name = "Metadata Repository") @Schema(description = "Metadata Resource Management") +@Deprecated(since = "2.0.0", forRemoval = true) public class MetadataControllerImpl implements IMetadataController { public static final String POST_FILTER = "post_filter"; diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index 97529a04..4be35771 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -21,7 +21,6 @@ import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import edu.kit.datamanager.metastore2.util.ActuatorUtil; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.util.MetadataSchemaRecordUtil; import edu.kit.datamanager.metastore2.web.ISchemaRegistryController; @@ -59,7 +58,6 @@ import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; -import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -67,7 +65,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.function.BiFunction; import java.util.function.UnaryOperator; @@ -78,6 +75,7 @@ @RequestMapping(value = "/api/v1/schemas") @Tag(name = "Schema Registry") @Schema(description = "Schema Registry") +@Deprecated(since = "2.0.0", forRemoval = true) public class SchemaRegistryControllerImpl implements ISchemaRegistryController { private static final Logger LOG = LoggerFactory.getLogger(SchemaRegistryControllerImpl.class); From 5f3db065a817871b6cd0cd327067954c01125b4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:17:36 +0000 Subject: [PATCH 081/181] Bump com.google.errorprone:error_prone_core from 2.32.0 to 2.33.0 Bumps [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) from 2.32.0 to 2.33.0. - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.32.0...v2.33.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2c4b8836..604326f7 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ ext { springDocVersion = '2.6.0' javersVersion = '7.6.2' keycloakVersion = '19.0.0' - errorproneVersion = '2.32.0' + errorproneVersion = '2.33.0' // directory for generated code snippets during tests snippetsDir = file("build/generated-snippets") } From 737f8541271a5bb42bd575fc5aa335a3b9702422 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:17:43 +0000 Subject: [PATCH 082/181] Bump io.freefair.maven-publish-java from 8.10 to 8.10.2 Bumps [io.freefair.maven-publish-java](https://github.com/freefair/gradle-plugins) from 8.10 to 8.10.2. - [Release notes](https://github.com/freefair/gradle-plugins/releases) - [Commits](https://github.com/freefair/gradle-plugins/compare/8.10...8.10.2) --- updated-dependencies: - dependency-name: io.freefair.maven-publish-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2c4b8836..4e9c55f6 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.10' - id 'io.freefair.maven-publish-java' version '8.10' + id 'io.freefair.maven-publish-java' version '8.10.2' id 'org.owasp.dependencycheck' version '10.0.4' id 'org.asciidoctor.jvm.convert' version '4.0.3' id 'net.ltgt.errorprone' version '4.0.1' From 44f737989cc5e2c210a9903fdd4a6f703e7f6048 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:17:44 +0000 Subject: [PATCH 083/181] Bump io.freefair.lombok from 8.10 to 8.10.2 Bumps [io.freefair.lombok](https://github.com/freefair/gradle-plugins) from 8.10 to 8.10.2. - [Release notes](https://github.com/freefair/gradle-plugins/releases) - [Commits](https://github.com/freefair/gradle-plugins/compare/8.10...8.10.2) --- updated-dependencies: - dependency-name: io.freefair.lombok dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2c4b8836..7643c034 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' - id 'io.freefair.lombok' version '8.10' + id 'io.freefair.lombok' version '8.10.2' id 'io.freefair.maven-publish-java' version '8.10' id 'org.owasp.dependencycheck' version '10.0.4' id 'org.asciidoctor.jvm.convert' version '4.0.3' From e25e3e020ac84d66ae20fd2c14a2fef76b3e0074 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 08:14:07 +0200 Subject: [PATCH 084/181] Avoid unnecessary temporary. --- .../metastore2/web/impl/MetadataControllerImplV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 30f162be..36130f4c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -241,7 +241,7 @@ public ResponseEntity createRecord( // long nano6 = System.nanoTime() / 1000000; URI locationUri; - locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), Long.parseLong(result.getVersion()), null, null)).toUri(); + locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), Long.valueOf(result.getVersion()), null, null)).toUri(); long nano7 = System.nanoTime() / 1000000; // LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); From b4d5b71e2a662a4e37c960235601d256d14c79be Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 14:41:55 +0200 Subject: [PATCH 085/181] Fix tests due to redirection of locations to /api/v2. --- .../SchemaRegistryControllerDocumentation4JsonTest.java | 3 +++ .../SchemaRegistryControllerDocumentationTest.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java index e7132672..a41f8de1 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentation4JsonTest.java @@ -433,6 +433,7 @@ public void documentSchemaRegistry4Json() throws Exception { andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). andReturn().getResponse().getHeader("Location"); // Get URL + location = location.replace("/v2/", "/v1/"); String newLocation = location.split("[?]")[0]; // 2. Accessing metadata document @@ -481,6 +482,7 @@ public void documentSchemaRegistry4Json() throws Exception { andReturn(); etag = result.getResponse().getHeader("ETag"); location = result.getResponse().getHeader("Location"); + location = location.replace("/v2/", "/v1/"); // 5. Update metadata record //************************************************************************** // update once more to newest version of schema @@ -506,6 +508,7 @@ public void documentSchemaRegistry4Json() throws Exception { andExpect(status().isOk()). andReturn(); location = result.getResponse().getHeader("Location"); + location = location.replace("/v2/", "/v1/"); this.mockMvc.perform(get(location). contextPath(contextPath)). andDo(document("get-json-metadata-document-v3")). diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java index 503e89ec..d30dd9de 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/SchemaRegistryControllerDocumentationTest.java @@ -416,6 +416,7 @@ public void documentSchemaRegistry() throws Exception { andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). andReturn().getResponse().getHeader("Location"); // Get URL + location = location.replace("/v2/", "/v1/"); String newLocation = location.split("[?]")[0]; // 2. Accessing metadata document @@ -465,6 +466,7 @@ public void documentSchemaRegistry() throws Exception { andReturn(); etag = result.getResponse().getHeader("ETag"); location = result.getResponse().getHeader("Location"); + location = location.replace("/v2/", "/v1/"); // 5. Update metadata record //************************************************************************** // update once more to newest version of schema @@ -490,6 +492,7 @@ public void documentSchemaRegistry() throws Exception { andExpect(status().isOk()). andReturn(); location = result.getResponse().getHeader("Location"); + location = location.replace("/v2/", "/v1/"); this.mockMvc.perform(get(location). contextPath(contextPath)). andDo(document("get-metadata-document-v3")). From 15b15ead90367d6cf7a2bd4a3239a375ac584707 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 14:48:36 +0200 Subject: [PATCH 086/181] Fix attributes sent in message via messaging queue. --- .../messaging/MetadataResourceMessage.java | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java index 8502c53a..6b16d04b 100644 --- a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java +++ b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java @@ -15,19 +15,24 @@ */ package edu.kit.datamanager.entities.messaging; +import edu.kit.datamanager.metastore2.domain.AclRecord; import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.domain.DataResource; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.xerces.util.URI; /** * Handler for creating messages for metadata. */ @Data -@EqualsAndHashCode(callSuper=false) +@EqualsAndHashCode(callSuper = false) public class MetadataResourceMessage extends DataResourceMessage { /** @@ -35,7 +40,7 @@ public class MetadataResourceMessage extends DataResourceMessage { */ public static final String DOCUMENT_TYPE_PROPERTY = "documentType"; public static final String RESOLVING_URL_PROPERTY = "resolvingUrl"; - + /** * Create Message for create event. * @@ -71,7 +76,7 @@ public static MetadataResourceMessage factoryUpdateMetadataMessage(MetadataRecor public static MetadataResourceMessage factoryDeleteMetadataMessage(MetadataRecord metadataRecord, String caller, String sender) { return createMessage(metadataRecord, ACTION.DELETE, SUB_CATEGORY.DATA, caller, sender); } - + /** * Create Message for create event. * @@ -83,11 +88,12 @@ public static MetadataResourceMessage factoryDeleteMetadataMessage(MetadataRecor public static MetadataResourceMessage factoryCreateMetadataMessage(DataResource metadataRecord, String caller, String sender) { return createMessage(metadataRecord, ACTION.CREATE, SUB_CATEGORY.DATA, caller, sender); } + /** * Create Message for create event. * * @param metadataRecord record holding all properties of document - * @param action message was triggered by this action + * @param action message was triggered by this action * @param subCategory the sub category of the message * @param principal who triggered this message * @param sender sender of the event. @@ -102,7 +108,7 @@ public static MetadataResourceMessage createMessage(MetadataRecord metadataRecor msg.setEntityId(metadataRecord.getId()); } if (action != null) { - msg.setAction(action.getValue()); + msg.setAction(action.getValue()); } if (subCategory != null) { msg.setSubCategory(subCategory.getValue()); @@ -141,23 +147,28 @@ public static MetadataResourceMessage factoryDeleteMetadataMessage(DataResource /** * Create Message for create event. * - * @param metadataRecord record holding all properties of document - * @param action message was triggered by this action + * @param dataResource record holding all properties of document + * @param action message was triggered by this action * @param subCategory the sub category of the message * @param principal who triggered this message * @param sender sender of the event. * @return Message for create event. */ - public static MetadataResourceMessage createMessage(DataResource metadataRecord, ACTION action, SUB_CATEGORY subCategory, String principal, String sender) { + public static MetadataResourceMessage createMessage(DataResource dataResource, ACTION action, SUB_CATEGORY subCategory, String principal, String sender) { MetadataResourceMessage msg = new MetadataResourceMessage(); Map<String, String> properties = new HashMap<>(); - if (metadataRecord != null) { - properties.put(RESOLVING_URL_PROPERTY, "To do"); - properties.put(DOCUMENT_TYPE_PROPERTY, "To do"); - msg.setEntityId(metadataRecord.getId()); + if (dataResource != null) { + String metadataDocumentUri = DataResourceRecordUtil.getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()).toString(); + String schemaDocumentUri = DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue(); + String[] split = schemaDocumentUri.split(DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR); + String schemaId = split[split.length - 1].split("\\?")[0]; + + properties.put(RESOLVING_URL_PROPERTY, metadataDocumentUri); + properties.put(DOCUMENT_TYPE_PROPERTY, schemaId); + msg.setEntityId(dataResource.getId()); } if (action != null) { - msg.setAction(action.getValue()); + msg.setAction(action.getValue()); } if (subCategory != null) { msg.setSubCategory(subCategory.getValue()); From 2b5e086d312b8601cb19908fb2a9e75375fa7337 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 14:50:47 +0200 Subject: [PATCH 087/181] Fix updates with empty resource types for metadata documents and add tests. --- .../util/DataResourceRecordUtil.java | 9 ++- ...rollerTestWithAuthenticationEnabledV2.java | 72 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 92322b1e..ffbce70d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -361,6 +361,9 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur if (updatedDataResource.getState() == null) { updatedDataResource.setState(dataResource.getState()); } + if (updatedDataResource.getResourceType() == null) { + updatedDataResource.setResourceType(dataResource.getResourceType()); + } } else { updatedDataResource = DataResourceUtils.copyDataResource(dataResource); } @@ -758,7 +761,7 @@ private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentif private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, MultipartFile document, SchemaRecord schemaRecord) { - LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaRecord, document); + LOG.trace("validateMetadataDocument (schemaRecord) {},{}, {}", metastoreProperties, schemaRecord, document); if (document == null || document.isEmpty()) { String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; LOG.error(message); @@ -1526,7 +1529,7 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp MultipartFile document, String schemaId, Long version) { - LOG.trace("validateMetadataDocument {},SchemaID {}, Version {}, {}", metastoreProperties, schemaId, version, document); + LOG.trace("validateMetadataDocument (schemaId) {},SchemaID {}, Version {}, {}", metastoreProperties, schemaId, version, document); SchemaRecord schemaRecord; DataResource dataResource = DataResourceRecordUtil.getRecordById(metastoreProperties, schemaId); if (dataResource == null) { @@ -1872,7 +1875,7 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, DataResource metadataRecord, MultipartFile document) { - LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, metadataRecord, document); + LOG.trace("validateMetadataDocument (dataresource) {},{}, {}", metastoreProperties, metadataRecord, document); if (document == null || document.isEmpty()) { String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; LOG.error(message); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java index 495bbf6d..c05dadb7 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -397,6 +397,33 @@ public void testCreateRecordWithInvalidUrlSchema() throws Exception { andReturn(); } + @Test + public void testCreateRecordWithoutResourceType() throws Exception { + String id = "testCreateRecordWithoutResourceType"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + // Empty resource type + record.setResourceType(null); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + + ObjectMapper map = new ObjectMapper(); + DataResource result = map.readValue(mvcResult.getResponse().getContentAsString(), DataResource.class); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getResourceType()); + } + @Test public void testCreateRecordWithAnyValidUrl() throws Exception { String id = "testCreateRecordWithAnyValidUrl"; @@ -1718,6 +1745,51 @@ public void testDeleteRecordWithoutAuthenticationButAuthorization() throws Excep andReturn(); } + @Test + public void testUpdateRecordWithoutResourceType() throws Exception { + String id = "testCreateRecordWithoutResourceType"; + String schemaId = SCHEMA_ID; + DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); + // Empty resource type + record.setResourceType(null); + + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", KIT_DOCUMENT.getBytes()); + + MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken)). + andDo(print()). + andExpect(status().isCreated()). + andReturn(); + + String locationUri = mvcResult.getResponse().getHeader("Location"); + String etag = mvcResult.getResponse().getHeader("ETag"); + ObjectMapper map = new ObjectMapper(); + DataResource result = map.readValue(mvcResult.getResponse().getContentAsString(), DataResource.class); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getResourceType()); + + result.setResourceType(null); + + recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(result).getBytes()); + + mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.multipart(locationUri). + file(recordFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = map.readValue(mvcResult.getResponse().getContentAsString(), DataResource.class); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getResourceType()); } + + @Test public void testDeleteRecord() throws Exception { ObjectMapper mapper = new ObjectMapper(); From 1ef286d2ca183eb8276821b9e2c5e67e8bdeb9bd Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 14:54:15 +0200 Subject: [PATCH 088/181] Return new location while creating metadata document resources via API v1 to point to new API v2 '/api/v2'. --- .../datamanager/metastore2/web/impl/MetadataControllerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index dd420c7d..b2431cfa 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -206,7 +206,7 @@ public ResponseEntity createRecord( long nano6 = System.nanoTime() / 1000000; URI locationUri; - locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), result.getRecordVersion(), null, null)).toUri(); + locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getRecordById(result.getId(), result.getRecordVersion(), null, null)).toUri(); long nano7 = System.nanoTime() / 1000000; LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); From 3751d75bccaa39ee640eb7fc10c3f973de3164d3 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 15:02:51 +0200 Subject: [PATCH 089/181] Return metadata document by default if no accept header is given. --- .../kit/datamanager/metastore2/web/IMetadataControllerV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index e8919e9e..a77e8c92 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -130,7 +130,7 @@ ModelAndView getLandingpageById(@Parameter(description = "The record identifier @ApiResponse(responseCode = "200", description = "OK and the metadata document is returned if the record exists and the user has sufficient permission."), @ApiResponse(responseCode = "404", description = "Not found is returned, if no record for the provided id or version was found.")}) - @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}, produces = {"application/xml", "application/json"}) + @RequestMapping(value = {"/{id}"}, method = {RequestMethod.GET}) @ResponseBody ResponseEntity getMetadataDocumentById(@Parameter(description = "The record identifier or related resource identifier.", required = true) @PathVariable(value = "id") String id, @Parameter(description = "The version of the record. This parameter only has an effect if versioning is enabled.", required = false) @RequestParam(value = "version") Long version, From b2e8f0402469de52c486136cfc5a572136a790b3 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 15:04:48 +0200 Subject: [PATCH 090/181] REST endpoint for schema and metadata documents must end with '/'. --- .../kit/datamanager/metastore2/web/IMetadataControllerV2.java | 2 +- .../metastore2/web/ISchemaRegistryControllerV2.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java index a77e8c92..be24feec 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/IMetadataControllerV2.java @@ -145,7 +145,7 @@ ResponseEntity getMetadataDocumentById(@Parameter(description = "The record iden + "If no parameters are provided, all accessible records are listed. If versioning is enabled, only the most recent version is listed (except in case of 'id' is provided).", responses = { @ApiResponse(responseCode = "200", description = "OK and a list of records or an empty list if no record matches.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DataResource.class))))}) - @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) + @RequestMapping(value = {"/"}, method = {RequestMethod.GET}) @PageableAsQueryParam @ResponseBody ResponseEntity<List<DataResource>> getRecords( diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java index 79e01f76..53c520fa 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/ISchemaRegistryControllerV2.java @@ -64,7 +64,7 @@ public interface ISchemaRegistryControllerV2 extends InfoContributor { @ApiResponse(responseCode = "201", description = "Created is returned only if the record has been validated, persisted and the document was successfully validated and stored.", content = @Content(schema = @Schema(implementation = MetadataSchemaRecord.class))), @ApiResponse(responseCode = "400", description = "Bad Request is returned if the provided metadata record is invalid or if the validation of the provided schema failed."), @ApiResponse(responseCode = "409", description = "A Conflict is returned, if there is already a record for the provided schema id.")}) - @RequestMapping(value = {"", "/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) + @RequestMapping(value = {"/"}, method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody ResponseEntity<DataResource> createRecord( @Parameter(description = "Json representation of the schema record.", required = true) @RequestPart(name = "record", required = true) final MultipartFile schemaRecord, @@ -151,7 +151,7 @@ ResponseEntity getSchemaDocumentById(@Parameter(description = "The schema id.", + "If no parameters are provided, all accessible records are listed. With regard to schema versions, only the most recent version of each schema is listed.", responses = { @ApiResponse(responseCode = "200", description = "OK and a list of records or an empty list of no record matches.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = MetadataSchemaRecord.class))))}) - @RequestMapping(value = {"", "/"}, method = {RequestMethod.GET}) + @RequestMapping(value = {"/"}, method = {RequestMethod.GET}) @ResponseBody @PageableAsQueryParam ResponseEntity<List<DataResource>> getRecords( From 87fa0c5f29a82a5a53762c4e227e323c6e95bd4e Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 9 Oct 2024 15:06:50 +0200 Subject: [PATCH 091/181] Set hostname for elastic container also via container name. --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ffa448ea..417de218 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,7 @@ services: - dps elasticsearch: image: elasticsearch:${ELASTICSEARCH_VERSION} - container_name: elastic4indexing + container_name: elastic.docker hostname: elastic.docker environment: - discovery.type=single-node From 6eca7b87a1ac521832d061d4482f4a307d93d5c1 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 11 Oct 2024 09:10:39 +0200 Subject: [PATCH 092/181] Fix tests due to redirect from API v1 to API v2. --- .env | 2 +- .../RestDocumentation4WebpageTest.java | 2 ++ .../metastore2/test/MetadataControllerTest.java | 9 +++++++-- .../metastore2/test/MetadataControllerTestV2.java | 14 ++++---------- ...ataControllerTestWithAuthenticationEnabled.java | 7 +++++++ ...taControllerTestWithInternalSchemaRegistry.java | 6 +++++- .../test/SchemaRegistryControllerTestV2.java | 9 +++------ 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/.env b/.env index 8456926c..e8388448 100644 --- a/.env +++ b/.env @@ -12,7 +12,7 @@ RABBIT_MQ_PASSWORD=rabbitpasswd # Only edit the following lines if you # want to update service versions. ######################################## -METASTORE_VERSION=v1.4.3 +METASTORE_VERSION=migrateIndexing FRONTEND_COLLECTION_VERSION=metastore-v1.0.1 INDEXING_SERVICE_VERSION=v1.0.1 ELASTICSEARCH_VERSION=8.11.1 diff --git a/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java b/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java index dd6320db..9b0a88bf 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/documentation/RestDocumentation4WebpageTest.java @@ -394,6 +394,7 @@ public void documentSchemaRegistry4Json() throws Exception { andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). andReturn().getResponse().getHeader("Location"); // Get URL + location = location.replace("/v2/", "/v1/"); String newLocation = location.split("[?]")[0]; // 2. Accessing metadata document @@ -436,6 +437,7 @@ public void documentSchemaRegistry4Json() throws Exception { andReturn(); etag = result.getResponse().getHeader("ETag"); location = result.getResponse().getHeader("Location"); + location = location.replace("/v2/", "/v1/"); // 5. Update metadata record //************************************************************************** // update once more to newest version of schema diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index d2ebde97..5b12ef8e 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -568,6 +568,9 @@ public void testCreateRecordWithLocationUri() throws Exception { file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); String locationUri = result.getResponse().getHeader("Location"); + // Due to redirect from API v1 to API v2. + locationUri = locationUri.replace("/v2/", "/v1/"); + String content = result.getResponse().getContentAsString(); ObjectMapper map = new ObjectMapper(); @@ -1336,6 +1339,8 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); + // Due to redirect from API v1 to API v2. + locationUri = locationUri.replace("/v2/", "/v1/"); MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); @@ -1676,7 +1681,7 @@ record = mapper.readValue(body, MetadataRecord.class); Assert.assertEquals(record2.getDocumentHash(), record3.getDocumentHash()); Assert.assertEquals(record2.getCreatedAt(), record3.getCreatedAt()); Assert.assertEquals(record2.getSchema().getIdentifier(), record3.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), (long)record3.getRecordVersion());// version should be the same + Assert.assertEquals((long) record2.getRecordVersion(), (long) record3.getRecordVersion());// version should be the same if (record.getAcl() != null) { Assert.assertTrue(record2.getAcl().containsAll(record3.getAcl())); } @@ -1710,7 +1715,7 @@ record = mapper.readValue(body, MetadataRecord.class); Assert.assertEquals(record2.getDocumentHash(), record4.getDocumentHash()); Assert.assertEquals(record2.getCreatedAt(), record4.getCreatedAt()); Assert.assertEquals(record2.getSchema().getIdentifier(), record4.getSchema().getIdentifier()); - Assert.assertEquals((long) record2.getRecordVersion(), (long)record4.getRecordVersion());// version should be the same + Assert.assertEquals((long) record2.getRecordVersion(), (long) record4.getRecordVersion());// version should be the same if (record.getAcl() != null) { Assert.assertTrue(record2.getAcl().containsAll(record4.getAcl())); } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 2d0d27a2..ad5c71d0 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -1515,7 +1515,6 @@ public void testUpdateRecordWithWrongVersion() throws Exception { result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). file(recordFile). file(metadataFile).header("If-Match", etag).with(putMultipart())).andDo(print()).andExpect(status().isOk()).andReturn(); - String locationUri2 = result.getResponse().getHeader("Location"); // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); @@ -1545,8 +1544,10 @@ public void testUpdateRecordWithWrongVersion() throws Exception { Assert.assertEquals(dcMetadata, content); // Check for old location URI. - result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); - String locationUri = result.getResponse().getHeader("Location"); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1")). + andDo(print()). + andExpect(status().isOk()). + andReturn(); // Check for old metadata document result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId + "?version=1"). accept(MediaType.APPLICATION_XML)). @@ -1558,8 +1559,6 @@ public void testUpdateRecordWithWrongVersion() throws Exception { dcMetadata = DC_DOCUMENT; Assert.assertEquals(dcMetadata, content); - - Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); } @Test @@ -2036,7 +2035,6 @@ public void testUpdateRecordWithLicense() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - String locationUri2 = result.getResponse().getHeader("Location"); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). accept(MediaType.APPLICATION_XML)). andDo(print()). @@ -2048,7 +2046,6 @@ public void testUpdateRecordWithLicense() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). @@ -2118,7 +2115,6 @@ public void testUpdateRecordWithLicenseNull() throws Exception { // result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); body = result.getResponse().getContentAsString(); - String locationUri = result.getResponse().getHeader("Location"); record2 = mapper.readValue(body, DataResource.class); // Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); @@ -2137,7 +2133,6 @@ public void testUpdateRecordWithLicenseNull() throws Exception { andDo(print()). andExpect(status().isOk()). andReturn(); - String locationUri2 = result.getResponse().getHeader("Location"); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). accept(MediaType.APPLICATION_XML)). andDo(print()). @@ -2149,7 +2144,6 @@ public void testUpdateRecordWithLicenseNull() throws Exception { Assert.assertEquals(dcMetadata, content); - Assert.assertEquals(locationUri.replace("version=1", "version=2"), locationUri2); result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). andDo(print()). diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java index 563a7e5e..95f31740 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabled.java @@ -475,6 +475,9 @@ public void testCreateRecordWithLocationUri() throws Exception { andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")). andReturn(); String locationUri = result.getResponse().getHeader("Location"); + // Due to redirect from API v1 to API v2. + locationUri = locationUri.replace("/v2/", "/v1/"); + String content = result.getResponse().getContentAsString(); ObjectMapper map = new ObjectMapper(); @@ -854,6 +857,8 @@ public void testGetRecord() throws Exception { String body = result.getResponse().getContentAsString(); MetadataRecord mr = mapper.readValue(body, MetadataRecord.class); String locationUri = result.getResponse().getHeader("Location"); + // Due to redirect from API v1 to API v2. + locationUri = locationUri.replace("/v2/", "/v1/"); String recordId = mr.getId(); result = this.mockMvc.perform(get(locationUri). @@ -1307,6 +1312,8 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); + // Due to redirect from API v1 to API v2. + locationUri = locationUri.replace("/v2/", "/v1/"); MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java index 464f464e..5ff770db 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithInternalSchemaRegistry.java @@ -392,7 +392,9 @@ public void testCreateRecordWithLocationUri() throws Exception { file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); String locationUri = result.getResponse().getHeader("Location"); - String content = result.getResponse().getContentAsString(); + // Due to redirect from API v1 to API v2. + locationUri = locationUri.replace("/v2/", "/v1/"); + String content = result.getResponse().getContentAsString(); ObjectMapper map = new ObjectMapper(); MvcResult result2 = this.mockMvc.perform(get(locationUri).header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); @@ -886,6 +888,8 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); String locationUri = result.getResponse().getHeader("Location"); + // Due to redirect from API v1 to API v2. + locationUri = locationUri.replace("/v2/", "/v1/"); MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); MockMultipartFile recordFile2 = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 307c10ac..2b9b43a8 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -234,7 +234,7 @@ public void testCreateSchemaRecordWithAlternateEndpoint() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.multipart(ALTERNATE_API_SCHEMA_PATH). file(recordFile). - file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + file(schemaFile)).andDo(print()).andExpect(status().isNotFound()).andReturn(); } @Test @@ -627,11 +627,8 @@ public void testGetSchemaRecordByIdWithInvalidVersion() throws Exception { public void testFindRecordsBySchemaIdWithAlternateEndpoint() throws Exception { String schemaId = "testFindRecordsBySchemaIdWithAlternateEndpoint".toLowerCase(Locale.getDefault()); ingestXmlDataResource(schemaId); - MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isOk()).andReturn(); - ObjectMapper map = new ObjectMapper(); - DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); - - Assert.assertTrue(result.length > 0); + // alternate endpoint is no longer available! + MvcResult res = this.mockMvc.perform(get(ALTERNATE_API_SCHEMA_PATH).param("schemaId", schemaId).header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)).andDo(print()).andExpect(status().isNotFound()).andReturn(); } @Test From 534ac47328c6bec4da14b0b39d5378601c34a39b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 11 Oct 2024 09:28:49 +0200 Subject: [PATCH 093/181] Switch relation type for schemas from 'isDerivedFrom' to 'hasMetadata'. --- .../metastore2/util/DataResourceRecordUtil.java | 12 ++++++------ .../metastore2/util/MetadataRecordUtil.java | 6 +++--- .../web/impl/MetadataControllerImplV2.java | 2 +- .../metastore2/test/CreateSchemaUtil.java | 2 +- .../metastore2/test/MetadataControllerTestV2.java | 10 +++++----- .../test/SchemaRegistryControllerTestV2.java | 6 +++--- .../metastore2/util/MetadataRecordUtilTest.java | 4 ++-- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index ffbce70d..5492bf01 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -567,7 +567,7 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app LOG.trace("Set relation to '{}'", resourceIdentifier); metadataRecord.setRelatedResource(resourceIdentifier); } - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); metadataRecord.setSchema(resourceIdentifier); if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { @@ -741,7 +741,7 @@ public static MetadataSchemaRecord getInternalSchemaRecord(MetastoreConfiguratio */ private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentifier relatedIdentifier, MetadataRecord metadataRecord) { if (relatedIdentifier == null) { - relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, null, null, null); + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, null, null, null); } ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(schemaIdentifier.getIdentifierType().name())); @@ -1271,7 +1271,7 @@ public static void validateRelatedResources4MetadataDocuments(DataResource dataR if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR)) { noOfRelatedData++; } - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { noOfRelatedSchemas++; } } @@ -1285,10 +1285,10 @@ public static void validateRelatedResources4MetadataDocuments(DataResource dataR errorMessage = "Mandatory attribute relatedIdentifier of type 'isMetadataFor' was provided more than once in record. \n"; } if (noOfRelatedSchemas == 0) { - errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'isDerivedFrom' was not found in record. \n"; + errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'hasMetadata' was not found in record. \n"; } if (noOfRelatedSchemas > 1) { - errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'isDerivedFrom' was provided more than once in record. \n"; + errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'hasMetadata' was provided more than once in record. \n"; } errorMessage = errorMessage + "Returning HTTP BAD_REQUEST."; LOG.error(message); @@ -1304,7 +1304,7 @@ public static void validateRelatedResources4MetadataDocuments(DataResource dataR */ public static RelatedIdentifier getSchemaIdentifier(DataResource dataResourceRecord) { LOG.trace("Get schema identifier for '{}'.", dataResourceRecord.getId()); - RelatedIdentifier relatedIdentifier = getRelatedIdentifier(dataResourceRecord, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + RelatedIdentifier relatedIdentifier = getRelatedIdentifier(dataResourceRecord, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); return relatedIdentifier; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 6093e255..3f492bf9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -397,7 +397,7 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati relatedIds.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(metadataRecord.getRelatedResource().getIdentifierType().name())); relationFound = true; } - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { updateRelatedIdentifierForSchema(relatedIds, metadataRecord); schemaIdFound = true; } @@ -493,7 +493,7 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli LOG.trace("Set relation to '{}'", resourceIdentifier); metadataRecord.setRelatedResource(resourceIdentifier); } - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); metadataRecord.setSchema(resourceIdentifier); if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { @@ -692,7 +692,7 @@ public static MetadataSchemaRecord getInternalSchemaRecord(MetastoreConfiguratio */ private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentifier relatedIdentifier, MetadataRecord metadataRecord) { if (relatedIdentifier == null) { - relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, null, null, null); + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, null, null, null); } ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(schemaIdentifier.getIdentifierType().name())); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 36130f4c..ac041a0d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -217,7 +217,7 @@ public ResponseEntity createRecord( //Can't test this // boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId( // getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue(), -// getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM).getValue()); +// getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.HAS_METADATA).getValue()); // long nano3 = System.nanoTime() / 1000000; // // if (recordAlreadyExists) { diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java index 56efce4a..ec19486a 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java @@ -560,7 +560,7 @@ public static MvcResult ingestOrUpdateXmlMetadataDocumentV2(MockMvc mockMvc, Str String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); record = mapper.readValue(body, DataResource.class); - relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); if ((schemaId != null) && schemaId.startsWith("http")) { relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } else { diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index ad5c71d0..219e0dce 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -388,7 +388,7 @@ public void testCreateRecordWithUrlSchemaNull() throws Exception { String schemaId = null; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { item.setValue(null); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -413,7 +413,7 @@ public void testCreateRecordWithInvalidUrl() throws Exception { String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { item.setValue(invalidSchemaUrl); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -438,7 +438,7 @@ public void testCreateRecordWithInvalidUrlSchema() throws Exception { String urlWithInvalidSchema = getSchemaUrl(SCHEMA_ID).replace(SCHEMA_ID, INVALID_SCHEMA); DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { item.setValue(urlWithInvalidSchema); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -473,7 +473,7 @@ public void testCreateRecordWithAnyValidUrl() throws Exception { String schemaUrl = "http://anyurl.example.org/shouldNotExist"; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { item.setValue(schemaUrl); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -859,7 +859,7 @@ public void testCreateRecordWithBadRecord2() throws Exception { String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); //remove related resource - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); record.getRelatedIdentifiers().remove(relatedIdentifier); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 2b9b43a8..e064561f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1952,7 +1952,7 @@ public static DataResource createDataResource4Document(String id, String schemaI RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE_STRING, null, null); relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); record.getRelatedIdentifiers().add(relatedResource); - relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaId, null, null); + relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, schemaId, null, null); if ((schemaId != null) && schemaId.startsWith("http")) { relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } else { @@ -1986,11 +1986,11 @@ public static void setRelatedResource(DataResource dataResource, String relatedR } public static void setRelatedSchema(DataResource dataResource, String relatedSchema) { - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); if (relatedIdentifier != null) { relatedIdentifier.setValue(relatedSchema); } else { - relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, relatedSchema, null, null); + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, relatedSchema, null, null); dataResource.getRelatedIdentifiers().add(relatedIdentifier); } if (relatedSchema.startsWith("http")) { diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java index 93962331..b3f7e718 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java @@ -725,7 +725,7 @@ public void testMigrateToMetadataRecord() { //@ToDo Make this working again // dataResource.getTitles().add(Title.factoryTitle(SCHEMA_ID)); // dataResource.setResourceType(ResourceType.createResourceType(SCHEMA_ID)); -// RelatedIdentifier relId = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, SCHEMA_ID, null, null); +// RelatedIdentifier relId = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, SCHEMA_ID, null, null); // relId.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); // dataResource.getRelatedIdentifiers().add(relId); // // dataResourceService adds ACL @@ -815,7 +815,7 @@ public void testSetToken() { } private void setSchema(DataResource dataResource) { - RelatedIdentifier factoryRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, "anySchema", null, null); + RelatedIdentifier factoryRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "anySchema", null, null); factoryRelatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); dataResource.getRelatedIdentifiers().add(factoryRelatedIdentifier); } From 79cbfbed8c80ec3cf26ac6fe6342cf5a4ebdd869 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 11 Oct 2024 13:32:01 +0200 Subject: [PATCH 094/181] Fix missing metadata record when interacting with indexing service. --- .../edu/kit/datamanager/metastore2/domain/ElasticWrapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java index 854e930f..5485bdaf 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java @@ -63,7 +63,7 @@ public class ElasticWrapper { private final Set<String> read; @NotBlank(message = "The metadata record.") - private DataResource metadata; + private DataResource metadataRecord; @NotBlank(message = "The metadata document.") private Object metadataDocument; @@ -72,7 +72,7 @@ public class ElasticWrapper { public ElasticWrapper(DataResource resource) { id = resource.getId(); pid = (resource.getIdentifier() != null) ? resource.getIdentifier().getValue() : null; - metadata = resource; + metadataRecord = resource; read = new HashSet<String>(); resource.getAcls().forEach(entry -> { String sid = entry.getSid(); From 724cfabb3e0e5fe85867798ef47ddb1635503e85 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 06:35:37 +0000 Subject: [PATCH 095/181] Update dependency jacoco to v0.8.12 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f97952a9..ea8354bc 100644 --- a/build.gradle +++ b/build.gradle @@ -165,7 +165,7 @@ tasks.withType(Test) { } jacoco { - toolVersion = "0.8.10" + toolVersion = "0.8.12" } import java.text.SimpleDateFormat From 57459a50226feedf017f788373983f023dc90205 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 06:35:43 +0000 Subject: [PATCH 096/181] Update dependency org.springframework.cloud:spring-cloud-starter-config to v4.1.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f97952a9..7603eb8e 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ dependencies { // cloud support - implementation "org.springframework.cloud:spring-cloud-starter-config:4.1.2" + implementation "org.springframework.cloud:spring-cloud-starter-config:4.1.3" implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.2" // springdoc From 4bf4aa64aa367f83db5a82901fbbad7ad9723de2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 06:35:48 +0000 Subject: [PATCH 097/181] Update javersVersion to v7.6.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f97952a9..a8c9755d 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ group = 'edu.kit.datamanager' ext { // versions of dependencies springDocVersion = '2.6.0' - javersVersion = '7.6.2' + javersVersion = '7.6.3' keycloakVersion = '19.0.0' errorproneVersion = '2.33.0' // directory for generated code snippets during tests From 693076fd7599ad20d743e498e18d3df99143ca30 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 06:35:53 +0000 Subject: [PATCH 098/181] Update dependency ajv to v8.17.1 --- src/main/resources/templates/metadata-landing-page.html | 4 ++-- src/main/resources/templates/metadata-management.html | 4 ++-- src/main/resources/templates/schema-landing-page.html | 4 ++-- src/main/resources/templates/schema-management.html | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/resources/templates/metadata-landing-page.html b/src/main/resources/templates/metadata-landing-page.html index ac0df88e..32c5c791 100644 --- a/src/main/resources/templates/metadata-landing-page.html +++ b/src/main/resources/templates/metadata-landing-page.html @@ -41,8 +41,8 @@ <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/dep/moment.js}"></script> <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/js/tabulator.min.js}"></script> <script type="text/javascript" th:src="@{/editor/lib/js/metadataeditor.js}"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.11.0/ajv7.min.js" - integrity="sha512-NGcX0dMSiOACZ7t7BKTSSoL2vnVKAp5mG+M8gK1vZQGwJtzbLQba1eniSXdez+WfDtcrxBUEo143DV297oAnbw==" + <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.17.1/ajv7.min.js" + integrity="sha512-OIkF2zyWS+sUanhT2i75RexNDQ2EKHayyeOxBx4Z4ct0D+dlPzZPhHTi7xZ5eU7aznNLMX7pCZyZVSi25+EGMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script> diff --git a/src/main/resources/templates/metadata-management.html b/src/main/resources/templates/metadata-management.html index 8f53d4b1..80e84dd6 100644 --- a/src/main/resources/templates/metadata-management.html +++ b/src/main/resources/templates/metadata-management.html @@ -80,8 +80,8 @@ <h4>Metadata Record Form</h4> <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/dep/moment.js}"></script> <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/js/tabulator.min.js}"></script> <script type="text/javascript" th:src="@{/editor/lib/js/metadataeditor.js}"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.11.0/ajv7.min.js" - integrity="sha512-NGcX0dMSiOACZ7t7BKTSSoL2vnVKAp5mG+M8gK1vZQGwJtzbLQba1eniSXdez+WfDtcrxBUEo143DV297oAnbw==" + <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.17.1/ajv7.min.js" + integrity="sha512-OIkF2zyWS+sUanhT2i75RexNDQ2EKHayyeOxBx4Z4ct0D+dlPzZPhHTi7xZ5eU7aznNLMX7pCZyZVSi25+EGMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script> diff --git a/src/main/resources/templates/schema-landing-page.html b/src/main/resources/templates/schema-landing-page.html index 3fa579fa..f89f036f 100644 --- a/src/main/resources/templates/schema-landing-page.html +++ b/src/main/resources/templates/schema-landing-page.html @@ -41,8 +41,8 @@ <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/dep/moment.js}"></script> <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/js/tabulator.min.js}"></script> <script type="text/javascript" th:src="@{/editor/lib/js/metadataeditor.js}"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.11.0/ajv7.min.js" - integrity="sha512-NGcX0dMSiOACZ7t7BKTSSoL2vnVKAp5mG+M8gK1vZQGwJtzbLQba1eniSXdez+WfDtcrxBUEo143DV297oAnbw==" + <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.17.1/ajv7.min.js" + integrity="sha512-OIkF2zyWS+sUanhT2i75RexNDQ2EKHayyeOxBx4Z4ct0D+dlPzZPhHTi7xZ5eU7aznNLMX7pCZyZVSi25+EGMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script> diff --git a/src/main/resources/templates/schema-management.html b/src/main/resources/templates/schema-management.html index 5a0453ea..c1046c7b 100644 --- a/src/main/resources/templates/schema-management.html +++ b/src/main/resources/templates/schema-management.html @@ -79,8 +79,8 @@ <h4>Schema Record Form</h4> <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/dep/moment.js}"></script> <script type="text/javascript" th:src="@{/editor/dependencies/tabulator/js/tabulator.min.js}"></script> <script type="text/javascript" th:src="@{/editor/lib/js/metadataeditor.js}"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.11.0/ajv7.min.js" - integrity="sha512-NGcX0dMSiOACZ7t7BKTSSoL2vnVKAp5mG+M8gK1vZQGwJtzbLQba1eniSXdez+WfDtcrxBUEo143DV297oAnbw==" + <script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/8.17.1/ajv7.min.js" + integrity="sha512-OIkF2zyWS+sUanhT2i75RexNDQ2EKHayyeOxBx4Z4ct0D+dlPzZPhHTi7xZ5eU7aznNLMX7pCZyZVSi25+EGMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script> From 34f17a99bb2ad5b240b147c838b57f04246a26cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 06:37:07 +0000 Subject: [PATCH 099/181] Update dependency gradle to v8.10.2 --- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 7 +++++-- gradlew.bat | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 12612 zcmY+pRa6|n(lttO3GVLh?(Xh3xVuAe26uONcL=V5;I6?T_zdn2`Oi5I_gl9gx~lft zRjVKRp?B~8Wyrx5$mS3|py!Njy<sLXg+!=ps!Sqt9`r=XUo;c>{0Wt4i%@s8v88pK z6fPNA45)|*9+*w5kcg$o)}2g}%JfXe6l9ig4T8ia3Hlw#3f^fAKW63%<~GZJd-0YA z9YjleCs~#<d(XS?yMY1sGw-OP3R2VM#+6%5eeUN|({(-V6UM^$zy?GeX}?<gItrOm zQJggnc1nCXJayu8df`R~JL0@RfB+hJnbLoGL#l^iy=-*Z&q*8TLRRt!XC4~4+kbXp z`>Y?V+`#nr+49hhsr<d|m&Ui$iv(&fVQj@GbFZR8g+r$Lrh36TXDhPW>$<mQiJ9^r z1RGjXCb)6uN=OM>K$k!lg}AZDw@><pK@-*0CSIU(XHdICOS#gI%A73Q_qMmL%!`J- z?Z6n4$%!M&R@&xyuB|~%u8zGNbfx;nfnd9_b$bs(#VRX}9HD+cYsIOFV0b-8b|wPt zFZ(zdK$4|q%Z%8kRQ9T~`!ZL?l&!$>2j=f7t~5IW6#K|lAX7|^N}lJ)I!km`nrwx> z))1Es16__aXGVzQM0EC8xH+O!nqTFBg9Ci{NwRK*CP<6s`Gq(~#lqb(zOlh6ZDBK* zr$|NDj^s6VanrKa+<kvW<wXwCTnkHW++j-@$u30!2_)I9fEr(yJUC&Gldq(ho&Q#$ z|AMDkA<DG~tgox{?yAXzWLQ-rcC>QC;5>twePaexqRI%RO~OY075y?NN90I|f^(P# zF=b>fZ73b5JzD`#GC3lTQ_B3lMeBWgQUGYnFw*HQC<e(^dgNH{E0P^la%aArhWzPn zv1Iyb9qLnN46c;1*Qg1}Wo~y~)bQgttke-N4*8=GloD(Uzp6z3LvV4tt?eC?38-G6 z;S)0gPKmi%n)?Ate%xd%y&vYn*J=>}^z{$6G4j(n4y-pRxPT(d2Wgb%vCH(?+t&Pj z)QM`zc`U`+<~D+9E{4Uj2kc#*6eZMU<mKPV${*8}%<xuS7Po4aP&Q%C9d5f?1{u3~ z?JiV-&R4i7s7+=*6Vh$tFxhdpb-E?a>$4Oj6QMfA^K!rbl`iBix=2sPrs7j@aqIrE zTaZJ2M09>rp$mgyUZ!r2$UK{+DGqgl`n;*qFF~M(r#eh`T{MO?2&j?xgr8FU$u3-` zhRDc_I23LL4)K&xg$^&l-W=!Jp-P(_Ie07q>Je;QLxi8LaEc%;WIacJD_T69egF?7 z;I_Sg_!+qrur8$Hq4grigaiVF>U7uWJ@Hkd&%kmFnQN-P^fq0gB1|uRt!U#X;DnlV zo?yHWTw7g5B;#xxY`adhi4yZn@f(7-Xa(J6S=#d@&rlFw!qfvholE>MEb|VWn^g}G zMSrK&zQ^vDId&ojL!{%{o7?s{7;{+u%L{|tar(gp?Uxq3p?xAysB>0E$eG#$tvkk9 z2Q2gEP17{U6@UD*v({5MP-CTZfvWMItVjb4c;i~WLq&{?Q1(koX&vt7+$z}10{^Id z{KDjGi0JpD7@;~odF__0m|p;5rIrHidOP9^mwKe#-&JX-X@acc)06G{LO1Wu)#gvZ za~y9(fhA%UwkDOV<as6ipaoRRnHw1p%94@QgVh`2nbeXYOCzoCwcD~TnB?0tcjYGP zO4cK2#unc-i1mZJ1ljZarC`c&&fjO!^81ggDmbm4BofP1$ZrveY%n4>U1LBJ`0ROE z4&)dJKK%mG@+CIm?+wt9f~@xIMr8}UH<Tq-YKF_sep;@YlCtUqOB+|7yZwxxPSigi zdBJYqKAA0zDfOu?6VsT!k<!&lyXFVEU37x8)i%<UroVgxatdX5rhhVil(NGh%s&)C zvyK`DL=|d-@!wdgU*{EdPalCBYMoLgr^QQp9JQqD+fb}wcbnS}fqvqkGq~ZbtxAnk zRG0~1i9B~|!YkL@iq@3Jm%g(}X%<XpQ?Xd~Hlnq%v5E8(Ai9TaihsL?Iuqv>*K1j| z0pppo{7gv3v{URwx<r)g)sD%uqf&$7Z&1aOL#bz`bi5^Of~{Yg$X{UPnzW=B<tOSg zJ*KqX)YyT#IuMv}5C-v*px_ewXN3Okn#$g`xco&kIi!5BN)#UBU))Cud%ZI6r#csh z%Tkdk(*wL*9L!@6c-}K>VMeg>Ps!L<SE2azjqY?G5Q^4q1}8^^6Zk9ndT}oz<LeuK zf@M_7HPW`Hq3w$Zyf~0kSpdePji$^wQ%cp1bSu_C<EQ7;#e_FYneysLC|k$R%~=jc z2Eh$#EJW!de~w*tdcyd2+zT1)zjjKb0{lcdHe}@CG+0LgA^kBn-99u~xCE!>5IKxm zjac2egjgb0vH5i75$s|sY_RYec#>faqJk|AGgV;v=^%BM(^p{p;(^SVt-88G9f!q; z>p}9E4^f0=01S2pQBE4}9YqE%TV)*hlU^8k9{&=K76+*Ax^r=AkBb%OCP^P2nm0Ri z;D-|Zk?gGeU<12ti2CnPVNA(Pb)02+r|&yTWW-OJ<DJE&w)lB;v}{5ZQeA;Xv<G`P z&c6nzQV)IYIvl{+cD=_Ev^R_m9~xNPXZ;4*f?@Ft-I!oOVN`7Hhc;F_ONas0XS>O7 zNLb0pps6aN?A~NJp5kj{{IOlf!5KWMleV@-hYLift)D>-7K+tgs=7Ake}oBnIy-y1 z(Hn@Hjw=_(x>dO5ysQsrnE%A*bk0K<-j{1Yqz@#n#jOL^AzCr#wR|WYzqk6i7v)Lf zkXdKxzuu20aP{Tbg$(+9&oh7cd(Uoqqf<#ujb$<VoSo++MmCyq=9A}zz~%<Lt68mh zQy2*DtlJ*NEv061v!4K6Vn<z|RoFz09<yY4-#N3tl86xvqd!NNul>q4sZ~gxFbQfS zS)kNklyL*{2AELgjZ(LBu*>S(oH5AaJ<cOMU&oY;;aF85$a3egZKPXQP+uKpBZ0DQ zPBO*phxl-K+p1@7mss>;YiB@;l@=O%F6B?oanzoYRM^fQ9-<~^=3$H0g^<S2;<1yN zHMgqd4)RD=e0A=j?7YnPS*e??i)R>JPMLQo@SZ@QuNvy)tyJ)LSj`+()<O%!_X82J z^kb<`up_CAZ5G#7by^Osam8Ime2M)t0=`HHyMi;$ZK^1kAfxGTQZahWm{izCK|5d! zjCmFILIzaIJ_g{`81J8_V6hEaZ`Kb?%@#GsLRT`%I1h7CSGAM$Gh$ohijKC=(1G(_ ztbrw_-?f)HWGrkv3=9U;{~F9XV2zPkIk@o4?yC+##ku_6eDnmIuNs*5_t4oz)mvH- zP}4haWU3U@D=b5Z7VQga4HU2=@%MBzAk}N>#fy?{aV4Yg^7dlQ7AQM^3GLCR2dAFR zJjtfKiVqF`l-H_fz0HD|9g>)pOxn}k!vdZ=DO!7Sikm{Z%P6BrRkBS6W?ZB5W&7rT z@uYpf@M@a!z7H&o@-yrcCL^Ff3e7p3T`R9p?@o-acXmbTSa0>ZANzCSgovsd%;i$| zVus`not!oL#(W`L-!9w0jdaECaG4hk{V7IOs676ZquZH~0<X2+NL?a5;`G~RFAgaF zOG_ipK-bZYvK9BN)};W0`0XE#=zgeXP#(Cg2FiI~BZu9iTm?^Qv`~kE>TX5hDq|)x z6T497l|E?f4)LA>j=S8}b$0LS=I4h|hUFJYJODT8Li@#6kF$k0)@*l{RnM1HQ%?VT ze-Pqlc!~t(oumVC*?5fwR;P6u{t<A}ZFWR4J3TkSQOKSLJ|!I`>HaZ~*LlD;B)4f? z?lpWfa2P@)g57flVl83Ej<RqTlxbzc-GuW>%P`2)gGyaPjhvD(%i~{`2b>#3!+y&` z!2nuwHMFA-zUY}f1^0B8<`N)Gr=A4TS@b1qykmd0Pq{?r)+1^^+D(=xasb^Tf!oK9 zBLL+*p6M_#ufgLzgq1zcSwZsZnQWFLC3`Yxdg-2=*tT`J9nrfYt)RF)YryBf8_gW{ zvKbB+oZLehfT)S#<|y1)E0hW^?+AnqPXq9Hu;v3dsMGdr{SVyF63;K<8VcgI#~}1i zLYSBL0K;RTT(;>2x=*!1Di9w0mwr;`CN}kM65|Ay{~z}_^JKOsRaN<~#9O^iiW<5P zYN7r~HV!#Nz~IZU`P>1Xe%4f~K}KcF#X&5kO*G}-)74S*tQ8CietdPcA1Yl;S=Mr# z`#MYY!{s^uo=jn7;k6O%(}fN+*0cWMpt~#n9DR<3NyU?+3D^AgI}S)Cu-Tljg`VY} zX1=fq$?8$DtOeGxE6f8lbS_6Q3C4+LDTO$}_IpM$Xv<|QSC%+Oll^q$y`7o@jD{dp zNDl|&X)r7wETa-#h*d`KXntxI(Y{vLha{$0i7@G8xx^m=c<{lJ9?p-i!^W{%j7-oo z0W^SzZ^(Wkyz*We{lEn%Yhu-ycUOHtrRiVJL4~&S91*D0MrLu}Q>v-Mc?GcWfpyz% zX|UvcN@krFO#@v|CtYM}g|=L3%aMo$E5<@CM%c*;?u>L<P8&c#HzO27hFVr?q|`t_ z6<R8yEwjdp$!l!%nRBO)zH{Y_v3jGk#jTDtlZh#bwP0X04!9I|j)kC^hRKfu6A0Nv z{h%4THrz(lF4tg7Qtm|u`x>OTz00@+dt1{yg1y=$h+{|D17U}$*^fE^H&8b431EUE z<9tv0V_#%#&1N#j7AKCj!tTK@J%oFW*ESW<(#Gl#Xs%v<@Ai<L$=3WNj*B-F_(z*6 z{CcF`>tI?s92nLzm<)w3Wkkom1f$gcdUi%g_*jofy&}N#luL<$GVIe{iQkQ)sIHVy zBgItnPBFamrv6Kb{eE($Q(f`ZPeW!Hm%Y@F*OF1sKB{Yy|C>WEv_mfvv-N-jh)B-5 z4a!1WcT@9a+hGaBrc~sz=>G?Q!*Zp^JFRUvBMyNR1;`)j$RhH$6gEyVKhd$&K-CFT zXaWC-Y=fyOnqT84iMn9o5oLEOI(_3fk!W^8-74|q1QhQ|CmT0i=b;6Z3u?E{p7V{? z;f#Q-33!L+4&QQcZ~GAqu$NS{M;u%`+#9=7^Oa5PKvCCCWNG_~l(CidS!+xr-*gg{ z$UQ`_1tLT_9jB=Hckkwu>G{s0b0F4bnR7GibmHo?>TR&<3?D;5Fb#gd8*wYa$$~ar z7epl1qM)L{kwiNjQk}?)CFpNTd?0wAOUZ|gC{Ub|c-7h~+Rm(JbdoRe!RNVBQi!M8 z+~U6E2X&KSA*T6KJvsqwqZl#1&==Dm(#b^&VAKQ>7ygv*Fyr;)q9*^F@dCTg2g!w~ z%hg)UXAUyIpIbLXJv1nZX+a_C)BOH2hUim|>=JHCRf(!dtTidb&*~I!JrfRe+PO>w z@ox$G2a3i9d_N9J=|2$y2m-P&#PTNwe!oLBZFs;z|F5kXvBDn<)WwE0E3$ow=zg3R zK(9;sf0t;VEV3@gAg7jRtnj%-6O@!Hvg*;XcUAw}!=2*aErvB(eQIm(-UGmq^J=XN zTqJo$Y|WKo^HlBF3BXJrA#}7ZLg=r*w`I*~Ix`o&2k8^(0mt8Rp=A>F`&gehhp@Jy z^e^#B2!~$LvNCKugg)8)-G%&THdk~kfextilegP9?#C#()F59U<HY%O5uXp3Ci+<2 z4s8Yrf{h@z-IbH4Fk^o{vP45e5_G=<@D{2^f?l<tRDm&{spNt>$&eo(h|5>ceo*Em z{PEE79T$YP|Kr7K`WBHbtQwyxFkCl6xX&+oUf90B5xoi3_5KHHCyEE*oPbOQkfMz& z6^hT8_NXd2iWk{q9IKae1{<zAdf|Kc=sW?c3;w~J8Hyxq%U>_7hMPH8I7_BMtVOM4 z6jm?E0QJOn$qrgsJ`9w##GB9?G})-GXSQo6(tYS(Q0-Ct$co?Zzl0?NHsDRron?;_ zZZgQg)%XW>P?8_&zoG<D?!7`qfDJIeNxpm%UEo=>uF(>Och2kEJXsu1_X&~w87x!b z>~h!a>e7{`p@+#hXF88wI*JeWRZ;J4ev4<}HWf|Z;(7$E!S5l9wzBHFe>^I{2<?b^ zm?e^O?I@1T|8`|@6X<UrcmG+;bWufgLXH+{KPy!1M=ZWZwhhgQ8iNf^19FS&{;Kma z3e^xp6PVe<tDvTkL<>`a;a)QnAwa2xv1e(bq$<}!8o^ofGvYpk7dBR+`*%iE;hUY5 zaHF}OjGO9r*{%lmcK^uFiTHg<p~5l~l|;WMlQ!}!+tNaOHE|o)xx5tIOso9+P}W-E zHsNO4EX1I9{I@7h$+#%<U*P8jowqBR*;5^25cB^28mc`b5m9>oUD`^9Nx@~;Bg!V* zuuJ&ti{DQiq7RyJAR94wem{}cPK1J(Yxnn_{=>?USqz-~&QXRStS^s-7TksZ$AEI! z#og36s3JGtGU{CnDHRFt<n~%P(rpi;eT*^#AtW4awY?HeyPa~W4e0uw%+=3Bn-R5@ zv^cXVds#%;{->ipFqvrE*gw7_K@NN0h+ItTq@4fqN!HeQU1y7*X?9+IfZT4Vxebpt z%#VzgdDK~-&+=Z*#>=n#XUhNvBZp3=Cr41jMqwJkHLf3L7Vm~V#GgJ(Jpii~PmJ#s zA7Ft!{xD@z>9DUb4JbiUBdNEcU4BO$651iN*mp*f)HbRRM`Cx5cR?5IfEcU{IZWwf zz(M6CDv)>xa3x}K6%tP^i15P1&&DOLK=k~+jNR$UK3frSl+|<jyqPpHFR%XI{F(h4 z@aHSmM}RTkqW~^CUol^C_7u@(A)wK%k+bML-)T-nNOErW1n7}>PjSC-dBItvD~LL! z>_g(YYdO4k(5EbPOw+v+;G7~jYm>F@Ai|o`gs%F)F8tDz$dl7Q%aCe|v|$UkAul_R zNlA-beBX^IJU<Kax}wTZQvov`Rr=c!>?kgS`E$it7nF4DaI!SJAGq)2P&Few(-|tp z?K+%D3e4{pfkayrcbm0ftu6Ol2ZzdKM+4i!hNP3NRL`EvvZJ3yvNr2MV%i<vwjnz* zNS7SUt@FjG8`E|zJE}qc%-UP?mDEqzS{5$lfcFTj=IcrU|M0U*IIz+?u3b%Fa^}W$ z#m|eTg^ed~5YfAT8xiF*;<y9=48OoIo`dDtwrD@DVlA+iQVcsT0yxAgz9%ZGY)|yB zS=1L-p2J?b=t}_PcWTz%TLEaBsvhr<+r)zkH;;7f%075JBT|UpQYW5(YKn01-1Q%u zq}UJ;3-oD=*hFbfPW-?;fB!t;&BmRj{vPMBMyE|-y^UeK_(b`N^cLszX0vW(=jC4N zZt7?29WgX)baZI+6{iWs^3v*HBLJO+kmWBmS46sKlp%7&>gZ4kj``Qrdb_OI$7jWP z;l0DYf&0(-*QcP5zrP`HVznW+SbH63Qx$7_9~NjRNg7eKqI!UJ=XH`g^=t8GiFTu( z?2L{JKEu%jJx&XjNzU(*!ZNmL1@RlJA<n|~gpaHJ=s>0G$2_LrAb_7lmjil(GSlSM zwTes`m+3R;3#N~Xg#9owh3ycXV8@ZlaY_16kpPFA={721b~URO4HD3sp%fmkZM}k) zZB0#)kP=RkNB~R-MCk8aljG_bagt4vIb~8)BV%(b8_;)&Kf9GX+%O_cNG|(D$!3&D zL(I8}*LqN5NntipFlN13=`D>6!{D@CFMBH0kW3=HccJV+xW~|$qeFR5i-2{X+iWMu zI2$gepQ)H_B%ip_BlWOQ*|pErXs|4ir{IHccgaIJ84irE{?+$KDABXr&f`jB^V-c% z$$u`uU1YB^{<+UN2cNg#7&0bz@yF?5>j|;)5&IV3wIQp58X#OE-M^$HdyvL|Um5t? zhZlAG!Mz%XkUe3t471JM*Yur}o30vzu6RN7gJyNcf!IItsDO730mcJ*O!~V``y5=3 zNJGp<f<92SpATi+Av=yBWRj9%i+HYkIn|oWkqT?YzS`muF~(upVoVQdX<9y;6q%_$ zg<_UHItu24JBn2(z(oc-MlU#`-U*$5&(ukyMNiDD{(*s2#S+F#kAh9`8X<9sLK4?B zjE_}0k0TY*in?R!SK;;Oq~6@ms^sp<S&cru6cV6-vMD5gZ|QVLsr<NJgP^c+y^_Co zkJxdFro<2}NaCy{*-TSrt|~u%g@`-ss|Z(iLx@%~58syV2lGpLB&TcTuiNo1`5}Ev zC%=7sop?xB9$HI6outs1r-lxT5v=->34DZ}wd1H6V`Uuy%es>BiO_aE-S8jzir#$& zyk)@2a5tP$@g%jW^b^JGdo)X@Q%sE`^lDQmY9m%uDFpPX`w9%=yQ+nneMm#OaXcD` z9}{tn5A2b2z9783vL2_jSao?uxJhWJoq%47*RafM4o0@gY(p)F>qT4^XM5GLzV#6j zC+HoGhAne7o_w{WUo(B++z7lU3Y0k1rYv9|TSv0vR-Du(5=VakbbelgZTeDn+a_Wv zq_j-^+Qz1WAl;Zg>ahX|CER<n^lxjmKe*C%;j3vQsky%A<K{!uFI?xnT>b<aQ?XJK zSqInwGQW@Gp`3+@NIZz}(GCnA`2z~qjKulbHgRBdmwdAm#m(C2Vi;ufw^H$G1sj3G zS+Td$hRP_!B29k@6cB5hoW^j*<C;u;*&9c_pcCErZ?tDlLI~*+)fq9^<rY^0ysNcU zQacFUG|^$c&W|1yR7e%-YF%*{YPAB=%*xZuY<h#Qqf@cAl~TwvjJJoU2O|vV55VE+ z?ah`-h_3a{<HP}=J(`=jSz6YC->X1V%B!hTKN?M}fGoA07M(WU&NfT&TmN`P@56U2 z^)vLDs|Ln~0iTtn-?KTeQl@T&bskJFuTUS!m+$CS9vnd}8(UMO|Kv6TCfGN9NUu&4 zL{)GTxPq>fwsJ~aU=4Qhuq8*RzDsP(LZh$BHezq&9gK$IS<|DYbm})$QTGCS6T;Dr zEkLct!b+#<1r9OKG@P!f1wm8>=Nz!7OzJm!g<+`?N3;Ya<Cu^U=YJaSSk-UK#V#j? ziST8aq*Wb!u>A3(P@EL=(sTaRMDD!c8=<FSI9JTg$DQ(7_TGAE5XJ&{5<WunLUvxL zyVo75%l2!#j<vivC$ts|Ks7*$g_8WhkW>-XN^4BXp(eVkj$NmEMYPP>YJ4bJ3yUud z<3BeJAJ$6z^TuywnfH5lv#$lgwraNw{IV=tIznPH1DT`v-5yS=!)J<}xxl}<mV7x& z`=f+J5l}Cb8;%>uZf9azA2A97Haf!;<3y01hlw?dWNEv@TLi1s-mO4vmIT%O_42nS z$VRWrs9NngqRRkWAnWkn%`Rw@?wH|)7XL`EL5EZu$qy<XFP)LHNu2br3Bm68;AFoD zCrQVfBlf=%Id#bkkahkqKl?_PSz*7@oh~}N5lK6=S5}dePdIveBWod#liK2_q4X3Z zEIMcTR^b9y4~55tUP&N!M7RMhi*Y?-1ks^;&3HqX{L_>JW31&CB^T_)qwIv!{;E_6 zo-9XAryQRlk-O0>o#-SZO>|6OYq;}<*>Wu1AsVRiXY4f8qb;+sItv3AyS!4Ry+q}) zA!pAB|BmC;=RIOk^^vlsEH(!Q!7_1FK~ZB2err*o!+b(r=m1b?$6d!%zmN+69LXnT z&gRmM+n_R-F@sT*IYv0<HFA(9QiUk=HL8dj`>_mGPvur!u`iWbQO7SqiGFLeY&yga zf`lM&B74FA2C?N@8_z652fjhBEoDUKbP8hL{0{HAF%qDo7)o3=3rg#6)T7%%5^wl% z9R0*S*<~>nzYOdQk2l`9h#t+gJy_xujw6xjV(8S<_DbVg61&pT%Hi42l%D73G?adn znB%UdNM0p}lEF-P2%TAMam2zpQev71e>a$$%i+r~b+D9G<PnkKvqAQ{g_(V5t+X!O zZ)3(k=fH5_mk3vkSb0#lROf36-?M;FcZpmwP}1;=kq0WU@<OlxOd{nU4vggunJ=Hw z3e*X=n}>9pF|oY_*(-u*89oKsXLY+UIbqq)MQ%(GYS{(*n_S_*RN$*~`zUtab%0<n zJ^c)!j8Suk2ijw5LD4Wmh4KSyL`9e$Pw*6$%1-2{7d@AYB-CY1v56I1gtnwDav^uH zN2rtkUm(~!si~Kgn94E`(GPJyansZls-Y5Fa}$=0SxsyUG6E_a{kzYZCJ2lt##G#u zr=_ZyVxpisz3Q}Z_BjxW#d3#T`PWhQ7L5g^`qS3ztJPF2Fx8VdV7v<D@Xl0>aKwhx znc)Yo?{xq1sJCgQD)TeTci1ucvbez9q=A72H(-SB18Kl&6^vHV8^i!p@>iF!DIw17 z+8Q)TNisB7>pwyww4y)yJx*wX6SJO78eLBC-ar1+k$Z9fy;wBD|3kzI{<+l*>PSY^ z_?nLOZaeWbU@C3hfK?X;Di*8CHCPkx2qco6(ZyJdqSzp^TJ_5Lpa0UP{Gy+!b0Lr% z@xYxS<i5kjzicG5NN_Dx4>jUKoY6L#>$qx~KD$-0=|OF7zhVP~ntMgEALYPIfhj@+ z!;JJ7te>CcovruwHsJH6Lta$nm|%^C@=V-rmhU{+I~0(|XHQ9jt@L7pb{gx#{4r!) zg($FyFTslcgu(~6lYr$nW?)%*l#VJ=R-jxK(x=t1bWlu(nL66T#qj%3aZ@uVhy}Co zDU_q61DD5FqqJ*#c|(M5tV)XBN?Ac^12*q)VN4yKPJ|#==S_<?;sU=&!VM26-zdzS zM%sgvc|DAvs7h%Du^Ohg7#d32xoAmckwqb<+cUGN?a_kblR<yM?LmE?6yuS85hy24 zrt--7cs;~qBF>`_QD9|0ls!`2)SwuHDRA_OfXQDq3%qW&MZB}Z!=k-9xqev8jHz(H z{^D@cIB~QiK>~wa)A&^Ll^Wi6Qg<a2SeL&Ek%28%9@ux{Y^VeA->CzU;iv-BHsLBs zH7=<YxOLJzi85F#Gt4u~SIuk6fwXblsbf-2KHB*dEEu}-e(ha4OXs5~@-8@%sUlQQ z@;$0TtSAr%(zmHcsL`F-J&kTVAQD;~IRI6zk)UCxLLvQqXZKaQkmkv|5mc(p)JO5T z_#~%QP}Vv6f5I308s>jN%|>0S`SjP%M&AF1PNVDp_FZ?2Bm@7`DC&v(pYrw!!yD#4 z6+<=HS0Ln6MhoKxF<%~H`y20{vf#pxh=;j{zY381gvAFekgG|>G1zo8$&az{V=;JR zy_puF4$L$?EMhT?;TpQoR*j16ll`#AS4e96C}yp_aGKkBe?1H|k_;gG-~Xorc<;lI zkB}fB{$c-D2mGA&{rm<*@F5)c3X+6??g~XoEwuzSuch0D@W~P5(2I8v8F$c2$Vw51 zP#YLSBDqtWW^EYBl^QYHF+MA7am6f4DOhwnJM=W9$uvMOsZ%_~?)2C#wb?CkI$7{K zEi)=#|5pFvg^){zK5kpBLjB2kZ+$ZB|L=W|aNwyyb(gC2l7bcpx{E-H@)q6@D6N<W zSyyr|=FWAD0hV=wwkPZw&2n!0G8WmL&-sl!Je3mdRD^^ptLFJ4`j&Gi?D=Jr+7pw; z?l*n5{|Y~(>^xh`{1E%ItF2$eeB_SjI@b2WgTpS1thwg&n`jiIzw^TtXUyB{0<?}j z53(^RLQF<0|84+;5o{2(J@OPkXYz0JSoy=j-+!OqkdSo#<Fpvu1z<U)Z7SBTYX1qa zQQ?#oL`}^9v)ov82D5rvzo@>0(<Y*C5vZYZg^o3$cRG__VFm3}PrYtZ7zq;i3=q(D zjsL09ROkut&i~A<nbf>$GIq>vbj|}bav}}Q<!6Y5A$bE)n7WDb*7j_&BYSlh%J#QF z{szbn$|OtMEhOWM_muohE}}FOn9ia&<=AZs<f7b&oA>_~wp3>k8!E@hVC;OMUTu|= zAy<BxctgvVSbemkT&*0{&Kh(&V{-xSL#?8&<P~fPbZAL+uH@HHdgF72FFW=7y0@R$ zl{PxddbWUB%S5tj9$o$Rto&WHa^6cujxgG4&2M_5?Zf16zQ5gw?V4pn+j2IsHl?27 z?Ndr&)+W53^t*{}V806nAtKo?+#g;^VVpyAYpbNE3PnOuVrf?wn4`^_g`@sPNZ5bE zyuT(cM>#vXH*GrUHu7^cNZWe1>y;2(51js9wbu+R3Aa*(wzH9+X0dIsf&gc_x|_LP z>~CF^?(~U}+l~ehe|i>?4eo!xkq&Lk+RR-1d<ZTcCrzc%<wR>uNP#o~>@1x)s&i&u zRaYL@+D&_M|JLI6fHbEr_`U;HgPTh#E3?sB)A$*gqyBgg*ql|a-m*TX5rACbWKCE6 zdeQ`v8m6>g^ugv`p|HY^#1QZrGGUj0^HVDc@{?Q0yhalbBEV{+|HzC^-{&e{5K%z9 z6Bxtnfu1!@Mp+Q&*&~;FOg&*Vm<@4b;{FG0-!UUXX!|)1w}op!B_|7_s~d(+=9Gba zKp8`LaB4D(H=cGcspJ_TjYaOwMb=sGn^gtUVhK!UI~2KKYEE-<cPX)PyMEvmc`7Un zbjqNSHf;o~>NC}F>+BEY7IVvy%KRvm00tg!Q`y=er}wpEetX}K@;}(}{s9AzV#q2@ zBy7}->|N?13POrs`;U?(qAG(I$~Gt+Rgw%aNZ_0fs_utVvRJT-7z4!@x36v@=N<xK za1B6!T~%M}sMo2i8*6&YC7%dlHeMHej8t3`QwWn6DOSnFickrZ0%<PnG0t41g3oLM zqNB^XWoU;x=#gdq>BX=IqkK{#Kg0w48de@?#Yb4M(Svj5=T+<<ZQd(pyzcKd5!c!v z3aAP<6?3#2Y3umwFtFjd*=K;=ECq064)y|d0hQt5^FcCyN}adMLj284V1oUF63@Ik zyK67fb?;!+O1gQ{A3Uy?^^6e9dRaoP6BMi~62^4^z&6s2a;SYfd?3&KWV1=|#!<tE z0dsIViUx)wM-xm%D2oCpPHkWspnv$u5Haeydd)3}`z3m>ONr8-oh7l?Cji@+erqur zFhZ=<!%|sZIdg8!U@=tWa`-w)5HHW2okch1)z%%OZ~jg3h!p&@X#J#b{ipf9R<N@B zWpR~xH7;tL3wqd<d{COWlbOkrR!iy-x=5QM&L@XSVCtTXF{2K4)fKIehZ7S+JTj{~ z7kPEc3^8o-aB6zB-su?qxDjaNnte>9|Lk=$`c}v4u`)-!!UI=!9Jo@h&7p4RlS#u! zZ7-prn75JkV?VjptX;@$#`U`{vB!=Z?V`T*FBF>J?vsML7e6@2GbUteMFfX-TUu{2 zLNIG*;dV)8GV8gAgEf#)X3A>p3^CRka1v?~8x^anBhQ=L=LsOl=&pcOYHo98m##ye z34MtGCDK!`ptl?taG<e3t`-Q!58X-zA%G6u9&M5m{w$wK2Vl*Z{Q4^>Mr5q{!zVc? zG00e){TV?`YA9eB;(lA3lXI?RrB4BYQGk?vOmTIUJED=(`_*gtn2DB-t4WW54as*W zb2kD-lWX>lb$+W!VFakki>B^Vc+u$?NLF>)!U%b@Y}gYJ>m2H=^x0=nsE0TF^Yu0h ztgH8-o1%+jCk(+&`|)tTfEVHq0cMeFa{Uz)X$;fCq%Y=SOWML6bYfeP8j5hktL`KK z(18`XrUn&WN9PtFxh&dX`y~YBsmdhi7Kw%tKzM%^VEhdD<_XkulW-x=JN6OPbFI4@ zzDDRN+f=@{0h*MswwOqG6gJ?{NuHx(y-|FUGsxyZ*x0~$MW(eY>vqq4Fh#t7uzw=- zKB?|!0N~!h^AMdLa)oR!Ca#HZ9&Zf)ghuO<^RN)4twRlygHnQG(BE{cDc<WLLlYSh z>5E}OF4;xss6gYyV~EcJvJkX)xNWb=@yw!uq0v-sf^rvkp-;?DPWK@*SEw|V;IH=7 zfQqEV_>DjOPT~8X*J|H8=&RnzK4~S7ML~nLX^%s-Vqc^aWy7N$y57qciZGcqy#=zU zs8hcHiI=D$+RB{|62{ohCTiaML6FI4Uhzo5D{Jik@poCs0w7F)*w}F4r0sJ~#u-72 z5bK=ANt=<yg1?d4a`up8tthJDrI}2UU9;+E>M$Dh5NKnxGsg9NRR?WD-x|FhTwBjd zD<-K>44DB~i%frJOfnzh1R>PRY34kw!6~p3M$JLaD1r@`=h)~Ngks-(gdXh^Q?BTP zZ^Zj5w1AwtuR2$~E7s9iZdF}z%pv1em^V2rM{1tLUY@-+Sc0(9jA|iZWml1;v13=U zHf?y@#mb--7z6$ue>`qjhE~brk$AY-RG90~5wcBbDReXR2)pKg{L>;H(DI`U!MLNQ zY9rFJP@ZQ}jlcMh%WSCo%vf+nd0Gmd*F%KMIe>slCUh)8Ma|;M_I+v#;|ueg9oLg; zq2HtZX%&#F7vdpNlkX?}(C7dGC^y#NB#m4%69RzTNrk%4ol~hSI%>2r6B|*ZkW(*P z;u#s;+faHo{tfy+1L^RzWDi*<XvRUHm&YQh2ae3hNsw?Iw!3o$(00f@UzF~tg*fWu zAJFR<*qdT<Av;%xrQ#FS4O8Xv9e35rXwZe!|FD>^JR0iY(zJDB36y_QJ+|E-2x+cY z!V8uLNkt<fMjx7T7>H~q>WQZuY!Ap66WP|E!0PA1jK~)^8oJVGbspJs6QL!!-5Qm7 zHYI|_`Actg?vDzdg5{86w@GS$G6ANzff7->6i5pB$T4O}`fZ_;{217Om0gN5zTr12 z5mW{hCzCE-QubjxN$TAE-XgI-8dTY@OZmq`y+y_>dk*(qXF<XB#O|R(Y&Y&lAeEPU zY_9Xd*~$V|yHw-yCddnMv~l^Z4nqXFErcf%Cnl=7oaD0Awp`Aelyxa{x?D*!0BD;^ z5{+YLU=Px+E$Vwrm_Xi4QBYaz=Y96;>0{nam|q@~i}Utp*k{yurq(DW54hkDT4bbg z=_etM?Nf5W^o-HEu9_?&xEqPg^P^mTxLH8n%u<M*z`86dM-@kezcH@CAvC7qA-U~S z8b4P%cN~7H)MCK5Q&3){RC~gR0G8c-^p^30s2E@9-jb?Pn--=(%GVw%XpfFXR82nZ zZ_z2zvP@Y8(PFf32-$McoG%H#b|0H>$!mWvFG|{&)jtnU&6|5-`~eaNz0%D1BDo`{ zS1N5(KW5v^2eLdd_%`uaRndF@h0Uo6=M|8?b~KbOLZk{HXEnGmtgZXf2inI*1r%n! zQ3&%RI4r{f&dwW~HwH0Ked9b!<o3cIaa)373!gk+0Rgvp^O~}Kqn%|b&PLgl&*d0; zA7x$Ft;yc2e<=F@@pX(Zz$KYH43V2e--={uz{NgbW)*92X_qs)KHbZU5>k6{>_19H z_Ai>5IChDMY(FfMyG%;30?SQ{iV9KyGru62+Y)~qSQ91}b~}w<&*}R&1c#$O`H@~c z5)2S_eXx}M#N{MuGeQS9@#UJB@;W_j50b}jIhxMPloEFQZdvwx<aiZgHL4H0auK_m zQndP8s>iU^RYycTzgK)-vl3LT&$L8~@68$C8~5_U{cR$E#w*x65(qw&eoL@>%ZHvj zWnEMlSh*(o&oy|J7eJ5OD`ssy%F?*Vp?`Cq;FShyl{ZoKCG5g{y}>usznni#8ki(i zO{w@n{iAj1_ooX@+s*!uW60WcH~*bNOT6z%0jVML5};wVrQp<xteNHLy==UJvLLcS z11@$%U6$V{EZ1yi0j9|4ZzQHrZ$b9Uc6>~`Uss_{cO2oud_nNA8^B$?07fJ6?iI)Q zuo9G)O-z)DqstrBqf>B%S05hf-wep0@$BFHKSrkZ{za3D)yVzRz)2{wf8(Wp+xyAM z$rtyx$gi3A=V~V!`Q3;BM0$>*VVtxEM|xDL^gew7ydy3Q6YzD&THRz*q33Ms_D;M- zbCx1Ft#UNB)V3bf`~{ImI72OTp^|bF8?G8#FRj+Biy8ET5#rA3sd|0FR@U(LAJ%w8 zS1%n8Z=Amhw)92rIsof=YVWF4jw&F*j1LG@-`+cR0-<nuGLoApm6AondsRuBs0;bz zkeHO!BeX#m0Yx^57kX}M_AT0d==N@AWa+zP>~2LqXRH8(Ccne{y#MCPncF64U`0uO zWmi$dlii~1D0rLR{qc|_2M!C$t8^=G7xQY)9!#Y331A|>N)EhmyVdLWL9I3YLJ`7? zZmpqUJB>Ni9oiL)^1<Ii{^5j^G+assy|~V#vd_uV1ms&{;-FTPtsDU<J<c_$1@8xu zA04y$94AbCkFZTwi9<J=cw&FyQ9ZRPGPo+Lq8+t<=-vtb>IK1UoMyhWE{$9M2M6Xi zPKk7GpMsA6vjZbU7~i+u|J6Nk|Ci!Y3UMUT2|`M;JsNQACdJ%ooo9Yt{?A+0hMpxi znEa~~sxC>rKrU6bd=WRb;%wsH>A#j4{({&1GYSNR57Gama(3)2A;SM>qop}l>Jk2* zn1+C$fIxuwzg3mCU#SOq<eL^!iJhia#Yyyk!yq8Y{(sa~|NoJi{c+Noi_p?Gs|X+= z;{FFU#bT$CRWm@QB>b-wOCb6mBcYlA5+mt<&_J~sBxc(GQtBFINUO~Mr7<-uu($>P HJ<R_D`<Plw delta 12460 zcmY+KQ+VIc6YpcYv2ELFY_qX#`_nYGoyNAErqMSx8{2ln)8GH%oagM#yk{13v3oJ| zyta1%qGukWK1c`PoQnaCnJ_4ki~ZQ8l!#->4oML2Lo<@i8BwbL^1~GkG`E7C$SEa_ zF^}Ea+#Je`Xy6;#D0FPnSrR%Y!QGA~NA^{oWmW8C<3dr{x6wWQ{4+bzemqV5W$i5~ z=J0jXZ>uZb>DT@0Ks?4QJ{`z?8JWl3$y;2pj#$XP*pv$>$g(z43{YH9KmmR6<#sIn zA<JQDEaX6mBxGjn_H`Pn2vX|anL2%9A>`#=0#sgycaBQ^&}Xba!|KaZ8~b30v~nLt z9%#gz_*=~KD{3t^X~l>480*}PhKN=??g`RV|4Ud{Gyyl187MJ}r(#e+H$GEdI+p1s zq_25h;fV)$EPK%Dw-(G=f`yHB-_tttsC!?k7*<CZRdB0Yj6aTr7h2qEZ+g<D$kH4c zqNHh<<`k(H5p38>#!|4a>`Ahj8nm?&n>NRs%jkZW^3-0P_yMP5&*6a26{MRj1&TPF zyE#|c)5uUHzMWx=rMKpuPih*V=S;W3MzIZTw2uTbr}8`p2bm+Z6Sa%vvWA<TscAzG zIW)i9uf*trLQJD1)TLTol)Z36u#=RFpxud+t#0GQjyU+54s$Q#Y^;gC>WSf4H)p(+ zSQ8;EvUa#wqWV+9vmIio(%7wukK2SwjUS8Yl%Rq%=~PU)2$Tvm6`1!r3H@U#_|bB0 zmlT1PS3wPB(b&^+@YY7Y$n4l3mV3-X0$>z|gZp6O*Lhzn&?Gad2ZCF;+#95-Y?#y+ z?*l@Yf=a4w{Px=o!N|3~_XKfk&G;f<J*cgy9;;DHK4V09AG7Pdjf<YmdDG|i*hhKq z8J-ka+Rm0o>N>Ps&dp2FpA~qD=0~=!NOS@B#XAKKkND>Y{4>rqxrViKD7;?>j8`R` z&G)3FN|dfsxnaI^!d1G%=>AbTTxZWo;n-DLrQ!sj=f~VAOe5zhGS(dgx|!ls62fbX zV@<7Ck^!}R=`Swr?(7w1rY6Nmq~<ZrV(7SB#`9}+v%>sfXJ?TiKJLn=&SQdEt9$@0 zA+h1Wbwbri0s-stc8yVq;mRa6@kEf8^KXUz&jcic!+avDvvJFa>k0ioWug=T3oPw; zyj4it&0@>_*uI@2=^+T7sL1_!^aJW@Xfo8aC#3^Wt<U_aFz6}E2fI>QC7fET8b9C} z*u^ue6Oj<atK36uS|zE|V`=aQZjgY+EjGACGxK-u{N8PJ@xZ3(JR;V&wYu|r*qE>n z7@(eskJ2+cNnH9~VyfIh<-|7!je~vGy*odz(sk-u$~SrYF3glruZ*W`{sqnS+9=;Z zh{D@MSG91%lr&ua8%$sJF%y1I<|e;EdfJykY8#D$Hc_81n5`$7;1N|b0tvvPLzSg& zn7!5x?T*@rQUKcUhTIjV(rw*5oQYlm5Db<kwylN;ncU&P^#xuaGkApPo;lXB46wR| z9vfH@eszp>EO?60#mohHfbR$3_x#+PZoYi@Vd4`#YgKyTd^!4n{fN~WZDY61sAOm6 zl!d^i*a01QxpWM9Pcl?&{RgO}uq%ErOk5WpECvnfEh!*YP&1Sl)uTN4hg??Vqs~i5 zYsfufz3?{TtwuBN=`0~Qg<P_3=!xd#D;FviS0Bvzs$CPK21H$can7@>1PlWH#OGG$ zLLWU17$v``)CE1cds_7kj8mJ{-+l8{DS|zAQ&3|qpOY=!J|kXUhXue9|H>4gqk|n) z-i34GmxLFj8asb3D#D&=ya*a5`C<=o?G;Ev^LV%;l#nH#O=7Nh@z1Do>j6Q;I5S2P zhg|AZbC&|c7}uSJt57s2IK#rSWuararn-02dkptTjo*R{c5o(bWV}_k3BBnKcE|6l zrHl&ezUyw^DmaMdDFVn<8ZY=7_{u{uW&*F<7Al6};lD(u;SB=RpIwI)PTyL=e25h* zGi{lRT}snjbMK~IUx|EGonH+w;iC2Ws)x>=5_{5$m?K<KylkVbB;JMu@5Q?Y-{C4c zzoQG%q6Mm*qcR0%MQ2=`G{d#$+1_)g@-q}#(nm{iHVxq3&HyYD;vNF;Nj7~Ns=BT> z5(*1jMn%u0V1Y%m@`YS3kskt~`1p(rA4uk;Cs!w^KL$w>MH)+cP6|XKr4FfHIATJH z!EGAK4N>1yFR`-zW|w%ByRe#=&kA&#W<T}z$wD+qdj1|GVci!w20~IlaM-gdn44yB zypLmMt|8HW90I!TQHR#bLxPNvxx3(=DXqm?CmJi?j`cy=l@-t?rdG+}SXx}34SKYg zQNQl>yUldDGpt!wf-8SFWiSi!5QZL+l7*CE?u!NW1T$<1rdLJ9y3u{_zvHaM?#Rm4 zFk}^1!ffcrB|XK3gsO-s=wr*sUe&^$yN|KxrA)uW00Gu60%pw_+DcUjW`oW<35OC8 zq2{j8SgC}W$?10pvFU83(SL$%C?Kctu3*cs0aa%q!fjn1%xD*Jrm!F3HGR9-C{b?- zHp(cL;ezXMpL@0-1v0DMWddSDNZ5h?q50cOZyVi#bU3&PWE=(hpVn|M4<T6B7sG{8 zeYyl0*ubCa7gf0TQGIfP@aWfzr6SRT1Q3DbZv&O@QJGSpJoY9q5!2^~g#=`T5o-4X z4wMPM1lZPsE9C6Mo%Q^FTnX&f7AKp{FPj0@c3y6J?e8<WxsR9PYo4C1e(iWQL{5ik zH=qtt&M9QAy$OfMm~ZvdX!>_KYG5h9LffKNRsfhr^=S<s0ehrIsndw?0d7*u^s)ZG z$x`^6qs@#e{9%)<zSmf|tnJx0ns`GiH$pp>YiKg?#r&HNMi2@cd4aYL9lw(5_IvQJ zcB*DD()hUSAD^PdA0y|QrVnqwgI@pUXZXjHq3lG2OU&7sPOxxU$Y3&ytj6Qb=2#cC z;{d-{k|xI*bu+Vy&N+}{i(+1me!M;nshY_*&ZQLTGG*xNw#{RpI<F(UPjEz!k+zr+ z%4hg0>`3^eGfHck+*38NRgiGahkFethtVY=czJs#)VVc{T65rhU#3Vf?X)<B;26<e z68kn~lVd)iXa{jn-0IL=f#euo(`^PplnVfcc>8f0)X{w!J3J{<Y(oJqB&1`mjmxgB zJY>z|Sq|%?)nA+zo?$>L9@o`Kc|*7sJo4UjIqu0Ir~S5k^vEH};6K?-dZ0h*m%-1L zf!VC%YbM1~sZOG5zu&Sh>R;(md*_)kGHP)<;OA44W?y53PI%{&@M<kUvRi#u7ytrD zg^R(C`>EN}9TOiqu+1a3AGetBr$c)Ao3OX>iGxmA;^^_alwS818r4Pn&uYe^;z6dh z)68T|AN=hjNdGpF7n>y+R<mwcWB<Ya*NYEqR3JixasLgDoQ?-q+;zB{cx8(`YEJsE z8N|ijP-7T(V15JD=2qeuz7`5D44^i_KRA#FMFjRqlLkSAEo|?S2YR|KlLYSDpFMi5 zlhBUbXtGNsr}OEBXxUpwGt^TIHU!$*+Ag*J?qkZFQAbJJkEnh%Bq3>TAZc9&opTXf zqWfK_dUv=mW{p_vN>|(cIkd(+Jy}qnK{IW%X*3!l`^H~FbAHwof+vLZ0C2ZXN1$v7 zgN&R9c8IO`fkR{6U%ERq8FN<1DQYbAN0-pH7EfcA{A&nhT!Be>jj>J!bNRw4NF|}! z1c70_#fkk!VQ!q1h2ff@`yDyrI1`np>*e#D4-Z~*!T^8#o*$V~!8bWQaie?P@KGBb z8rXc!YDL!$3ZgZZ%;-%~0Kn<+d+{xJ$stQbtN8GWV?MCJvzPU|(E(1z;rFw{&6vy) z3*@y%7Tx8rH-p$boS>bLyod?OKRE8v`QSBvGfY6f}_{Zo1q85xoyOF16n~yHx2W ziydUoYLkJmzq|n&2S(O!ZmLdP1(o1Jsq88cX)x3V-BK5eF&0e_0G!5?U7&3KN0`mc zH&Lt)q8!d_VgzxyL^(@xrbp2y)Hmr^V48));RSfE=*Ly0uh9!$3dv-vMZ<B8EQmZK z%yD#x*qN}E`5Xi0pIP3Xk|Ir?z$U^PBPxb6WpP3IBjjt)&my>r2URf@l5zdwLjGZB zugY>7_fd_vbV*Qv1?H~>Z%RD%nEeFSI$n$$Lrpc6g>i4+XdBB!%zM$Bhrz5Swzyg? z$~I~n@~-wTBY3-T&pr+|gC+OHDoR?I(eLWa{Z#Rsh>lc~%u0!&R|s0pA*w<7QZ}{i z*AF<qI6{PywEd%Xs3*lags5_&aPJa@hO--F1$O%#bdgp8y={wv{rO_(IUi!%HsB|V z`5&VfI&&C%hT{^dEDbt??+77Tq(3HFHx|xl{jgw}Q*DjE+TcNrMojZN*PH#p@SH}R z@Xu`)>r~0F3y~f$MGh_HDL7J_1?SxKL}fWIk!$G}`^{)xh*dZ5kK>xGL9>V`WZZg_ z)^Vm)EQK`y<U9-&%#WaCB)GfbjlGts=K5=`9)D@ejpc?W?3C)%DMGMu82u?hmqH$S z#c;ELe#eS217a87pGY63GD~k3T`iYAD?#gKpf{bRFrbGhRO>fh5KiR(vb&aHvhich z_5o+{d~0+4BEBqYJXyXBIEb1UgVDs;a!N2$9WA>CbfrWryqT25)S4E4)QXBd*3jN} z?phkAt`1rKW?xoLzEm!*IfkH|P>BtECVr0l8-IGk_`UjE#IWkUGqvyS+dMrCnF<a? z8twD07q0Z{9v(3D;a@0c!0Rg}k1GY6kBGDp_?m^@f7MUu@fa;06718Q0f{cLLCdCw zsvb}PbiUAlc)P^lTa;p&m!5-zA6X5SqV&T{aH;#<*>l<7RCgSMX^qn|Ld_4iYRldO zY&cHhv)GDo8nKvKwAbfyLR%t?9gG?R7~PSD#4D-;?F&!kV59O}neYut5AGbKwy-(U zqyBi=&Mgj|VIo>$u!DHM`R7O?W8-idbePuxiJMH``6c_5L-chKd}=rGC5Gfrc{f!* zWFEBm?l@_b7kzY7%1RQQbG5V<4=ZlkZ%sF74Q|mKOc7Ak7dP2#quiGcZ0_J%7Q?j{ zv9{WFw;n5<HMMg)rLC27Ya2hqhOLeEe+Si~^V8;0Zv2H?lq8R<w2s%exZkHU)bvUM zCU#FfbS5Jfdxla8wc?^uWfsxEsy3aI90^OZhmS8369lE&B!18ww+e3XiCqyD?3vvM z$&BFLk5b(Cg&&(Kkfl#QpcRY?O_c>G-Mn%r#0R;{jLt{yy}9J6rQ(>X9pJ`7Xy?Zv z=lNit#qXaq?CnElK^zF~sG}U5oCpR0T>FH=ZX}Prju$);?;VOhFH8L3I><9P_A|C+ z{;>~dk%9rrq(snjsEm}oUz2FQ21MCG*e?g)?{!&|eg7PX@I+Q0!hL6C7ZVY|g2E>i zr!Ri2@OfEu$)d52+>+cpgh6Z;cLYCZ&EMR0i<^~4&wEu_bdo;y^6}+U2GIQgW$|Od z_jg{O=pU>0-H$P-EOlWyQy#W0r@@_uT}Lg+!d5NxMii7aT1=|qm6BRaWOf{Pws54v zTu=}LR!V(JzI07>QR;;px0+zq=(s+XH-0~rVbmGp8<)7G+Jf)UYs<$Dd>-K+4}CsD zS}KYLmkbRvjwBO3PB%2@j(vOpm)!JABH_E7X^f#V-bzifSaKtE)|QrczC1$sC<<*Y z$<SQy$IUPLG}!oY>hY*3E10fYk`2W09gM_U<2>+r^+ro$Bqh-O7uSa)cfPE_<#^O) zF+5V;-8LaCLKdIh3UB@idQZL`0Vx8`OE#6*1<;8(zi&E7MWB1S%~HAm%axyIHN2vd zA(pJ<hG*T~b|HRJd=PkpKc>Gm_P<gXBs84zOQ@^zzggq%RNYmRcnPwBW@U;4W6~w1 zc)9!EX`Y>raB0Aat3~?obWBs?iSc*NhM!{-l_WNCx4@F7I?)5&oI|z{o@JKd1H<!` zv-|4PohI)b5<SK?`Wvi(SN+A-snCmVe?J%HV1+T($=eRX#VCtRV<>Z}zf*#}JjK3$ z-;3V*WJZvUcKvSOBH4c7C{fl8oRw8-vfgKQjNiR|KhQ%k6hWNEke(k8w<kfQ>-Ro| z7Y3)FsY-?7%;VT64vRM)l0%&HI~BXkSAOV#F3Bf#|3QLZM%6C{paqLTb3MU-_)`{R zRdfVQ)uX90VCa3ja$8m;cdtxQ*(tNjIfVb%#TCJWe<dwdt_>H?o4RY#LWpyZBJHR| z6G-!4W5O^Z8U}e5GfZ!_M{B``ve{r0Z#CXV0x@~<aO*GPh97Ye0w0FxSFdI5(c%#e zB-49&ls#|r*$)qZ5(=I00o|&)&d)3Vr;TQZ7=XCp$65Nr_O`aqN6mpG5ali;DUWuT ze}nL$H8;-_e`_~Td~r=@Fd#_sw*nC)boV>X#Pc;}{{ClY_uw^=wWurj0RKnoFzeY` z;gS!PCLCo*c}-hLc?C&wv&>P1hH75=p#;D3{Q8UZ0ctX!b)_@Ur=WCMEuz>pTs$@s z#7bIutL9Pm2FDb~d+H}uBI#pu6R}T{nzpz9U0XLb9lu@=9bTY&PEyFwhHHtXFX~6C zrcg|qqTk(|MIM%KQ<@j=DOjt|V)+8K26wE_CBNnZTg+Z+s}AU|jp6CFoIptG1{J*# z7Ne~l;ba*=bSwAMQ|Vq#fW~+je4PXA91YFzBubNF?ovIOw-$C-8=Ehed{lGD0}(Id zRe4sh8L>&T%{>8o))he}eE;<zd8{dkIeyHjId^)5C8Dfqu4N{b!nMYoOJ};J_F>5_ zxoXk3wX?MyNl-xF<jg13uJ=K3pS}7Bxi&jx{H7Zi^~i<>!q1d$G?=wp^`@09(jU&X zOqZIBI#dN`2PJNdATR3ivtub|nO$dulSaP|e4)WXF1YAGN1pDQIbIjXFG!oC85Mt; zW$eteoL{y^5t4TMRwP$jNPjZFpGsWnGe=jMMqKtcZm9Y9PFZLi*1p@qoKKub^T@2+ zk$@*KYdQ?Z<WhVLF|0Rw<qgZ^;h$_v2ozZ~2B(c9)Fd3ugt}tYq(EXOoK<KU%2(|_ zXwdMe_xNK$9|SLuN>`}<%4ALwk*Yc{(WTf@#u;as(fvE^9{Gk)lWbJ<Fjdmr*Ad0d zAB^ehSfDt?YH-?}mX(9oIpGt+?DZ?0Ti{Bt$WoiCrF}`Q>P*SjttWofV0s?AB({~l zZI1hZVWFT~W-T?nfMMcnCS4-#6H-MU7H$KxD;yaM46K4Kc@~Q>xzB+QnD_I`b_l3m zo9pRx46b!p?a^&zCDwygqqV3epjs<lr*6C0NLqJwYjW(CkdVNHhH8OSRjD|}%rMZU zW&OHg{JpSTFsB=;54Nzp{CoD1ziemo_~FZI$x-gr>(s0NQI6ARA1n!Yy-qduipxQ& zUAlqRpNjBS+y-ZheD(!R;F}&^V_}b_gqH%tVZ5%%ziO7k^w=es+wZ<zWWg+?b@0oq zw?)2u{9x?r*rp?(arKZrF>tK^i*vmrWNLMs{oWu_CIov|s1raZiS)>38>pYu;i+-t zI_DiNe6aA4KTZ2P09qPj(0~K4nUq^0+f(2$g`229zkG4jLzRvJUWE0oF1XHL4t3UN zDH466G56sy9hTZoAJB!C3;@F;ONxEk5u6M<?Jse76{z1DRQg+EGt{>v%zdo}Rq`=* zw1n7MOhfNSV48TS989ArIcj`C%Gk8~93~u>)!Yt2b4ZriKj9x2d<EF$FgbEw9_+Fs z%QNB=7nqv_OyuJM1V-yo6B$1QZVM8s7yq*1m}xy3&=I+u-t>`H2HQNJ=I>hkDlcZn zqRj>!;<h+M=IcUSD(LI{2}I=3H?BoQr_=GHAt;6Yx)VbMF?3)O(^;F%fX>oRMTIOu zx|Zfsu~v76T{z7AC(jxj^c@tnJHZtGPsq$DE!8kqvkDx5W?KUJPL+!Ffpwfa+|5z5 zKPCiOPqZZrAG;2%OH0T$W|`C@C*!Z`@Wkop{CTjB&Tk`+{XPnt`ND`Haz;xV`H^RS zyXYtw@WlqTvToi;=mq1<-|IQ(gcOpU%)b#_46|IuWL#4$oYLbqwuk6=Q@xZaJSKVF zZcHs~ZBl;&l<b}sR_jregl4gq_udWs78`gF2hz&$&?H5O72#r1Iqa@v2j*UjY#hg* z-&~M(eQZ_+L&N-+W16^fLfxR01eM64kCIv|f~~%(y8iabNvP4`C{<{7h!>F3=+nK; zF`4gSCeZXlwmC_t4I`#PUNQ*)Uv&oGxMALip|sxv^lyVV73tKI7)+QY5=tEM<r0Gn zeki86_jIEbM5&}Pr2f180`Y-i7q(C~1?Uwr27YxDB-L9L+-!2+f4M=FHTslTNbWZm zr&AZr`sv?-WXH_4TUv<&T*S@v$WMo`Tdg^c(`*f}aV!SlAK)gYGHWw22DAo^U9Npc z5zGLBn>as{vTD-BaTJ^*Y6gq~PU;F5X!sxqiq$iFCo+Uv7m%1w((=e}Vf*=dtds|6 zbX}91!G?C*KG03eHoN}RZS9DJxa&8YwNCT8?JxMXyZqZr13NA|GB{+vG`08C{V(yy zf*Lw$+tYSU_+dI`3n{bMrPdDb`A=Mkg!O=k>1|*3<B<U9yt3l_k5yZHx9(>MC8j~- zXL79J4E=U^H=iBLTeHE_OKzE&dws8RNynsSJ!d;`zK?P92U{f)xvD7VQVosrXZrL+ z6lMVdD1YgL;%(1cq{#bS6yXmp|DS@nax#AqqlZhtUQd<QheSWAv98YjZ+F+$k-34r zO9f43tEeAWDw<x^sSb*hlbMCj2DS1}+kX41S?CkB7gaUCbOz@J6i>h<^2vr5`Ep<W z2E2WL=t^(oL+@HRci1@un(c|XV&JZZnW^7QDs75Sk1kVkWp%5f#|er|Gtk!Z>AO<p zBo1c%Bo9lyZ%Cyw#>LGYq)sa(w9^3-f}NHy=GR4v%t2YZly3m1G@5y`xBh_HGrD%f z>;|Ty?<WoZqcIB=i)M*4;-tBuU?1qbFBu|NWiV=fOntrdY1>9FiJAc&UVD(StT4I` zfVQwxhE9bXE6r2mKO8Ag7{L^jCyqQb0QqKDPE=RAgqn8q1O^>(z7h5kE(6va%QqRZ zkIOmp(})rLSS(2{=C12e&@!W2=Jel-^_R``0xHO^+t!(oXbcv5yhD4g*$t_F)_5Dl zSVCgesW%;DtYPCFs{G;GX_o?1J3;QQP<?t1D19oXhb<$F4AlGG^2?z)-uwdnfaInO z;td=iUTdK=f(FP!39E)qivWJ5`M)NijXwJt$92!yVO}@Hnaft7ek~*YA(0Vv&Px7H zydrjfp|#dh;tfX*W?9?@l~do|c!2G_chv5uB_oj=773h!fP!S)u8E2J>Pv)rWw;>} zJ&KwnUqwNXloNXlK_+pNDfI~hON#SokVJb&ilg8d7^NWo2ZQymCqQMnjfi>ePibjr z-Z@q!?RGN$Mj}Nk){X_vaj6?Mj$>ACR*z|6MsXy3VZ^PFn@yHkPo(>m(iWepn8SC@ z>D2;R4m+gDRZ=SIX!b+CP(qE=JDIUkn=D$aUu+Ihn9-+k1LS3PreQg0N5e<M8f?-0 zqUq~cI*AlmJj4&1F@S}_sb&P<pq@tKcMCJu*h@IP>WIG@x${nC3v<uk_*&$;l`u84 zctsiWsYUOQ6*VIbhZ$_kBW7%CzCP@bWANl4m}R5O@%ke&9P3I>^7caS>1!PKNAY9J z#}E}Q9w#SP>(GY7Hbj&z4$Li6o5taBO|4+F`yS9zq*<HN@c^xvnF!_~42^oKk~l6D z@Ubc8Se7~W75T$ic(wVCq)(?B4U81x{4w08Cjl8$6s)B=<_^^(S+7~s<E!~JtJMRT zlX#!FJJlRz>LJ<38wy4I>HA9(&GYrk4dLajKGww))BWli6Ln1A^Lda@N~p+snkb9C z@OthI+<##vp8!HVQT4Wk(=@zQ{OvZ$EKWS73+JHb)eYLGD-cqi6^|vd$<+IHuc?Nq zW7JertT~3))4?J|28n$I@nAD0c1%9C&IVhEZX~mUsf{efyS(XNG%ch;!N~d7S(Ri7 zb&=BuON95aVA&kLn6&MVU|x}xPMp7xwWxNU1wS+F6#y}1@^wQZB*(&ecT?RnQcI}Y z2*z!^!D?gDUhc@;M^OpLs4mq>C&p{}OWVv<)S9KMars@0JQ{c_ScGsFo3BJ)Irg++ zAWwypJdTO-_<vOW0C#}P^Dl0g**IppOT*d>{Uh8m(Z!3KL7K{ZZzKHj;{M8I$mV>k znTM?sa0);^=X^cglL`uC+^J)M7nEa$w=VwFULg~%DJllw+7dJAj3{qnP5i3@wr7%y zjXp?Wl2%Th=my&3u?Q$RV6N5tzKMSPTsc#J+-cDDp~qFB6bL2C8AS7Y3PKtVhdhl) zIaLqH5+OnWPWSt(lQCgkN8lczc-V%_iZ{>#1%Z$N*>lu#S;0MZ$T2Y8Kg!U;hAZj> z6S#%$DQ_`Ic%Zr@?}GgjRXg@qTj^17n`65oJ@Wj0u1X8&+UVd|Xs?J+i_^GZ94m6= zUc96~Q`OJvlKB_Lr15*Yw_PUPEr?f?H&00b^-W%26mD)(n(rGGNfK9~2h=C>p-7BZ zFd&*&Msdu{w~(eyFOglwCPH^Rb}O(N7LtS+nnEwDx*pGD?|&9Si~M43a+*L(b0$5A zv`T`(G3xO;I_sx;FwTP21ZlfDpz<RKqD|R`Vi}uqNFQN6XbBv1wl3$}L{U85hl%P> zOo?}Vlgf~fo{YWm@n_JyD*frOg{XsvBA~|Tn4V6hu>Gd>89-rblfVJUaGvj6X%NZ} z$tFF9sx=4_$*c~G`9iPLGh@=sV+O{D2-t*K@J7H=`V+oVt}8?04WwU3h1BgS!f%1P zFak-T#7`TtLcR=Yz>g0R!ZQrH!YiZOQN=_V-UyncN1Rc18?KY?#O`v#JK+pq0K$~H z3D@v9DZF42R)b9#BBX{^$DOMlJ!g<ESs_=^skPbqZ8AAzdu^RBR8t8zYX(pMc>)Gc za{o-1e%F6NvgKq9tC8pV+9S$;9*zNv{J*)n&dmf~anP1)4~<J$42k=HE1fID6gBX8 ziU<X}a>N%~h#c(=B#3*KgzhCKhFdgDoWi2IDog{RVyzK|Y`rCUs3T~pJMmdZJy4?b z&s5G=zhf**(t7Y^oC_mcTsE-{^}wiaoUu&?kojLKs>SJPxjcP>{a5CbXCx92AcBE) zHtqP}LjZ{W>PH?Tu(E0X=%{PB<iwGt&IwfGhv=*sAq(7;Y}oOpaGIoK%X2LOqMge< zPYBM9GmaE%tBz-@rKr{fjrP$^Oc!JSV2kcb)o7Z>MW@F_?#7b&#!^q`<-5$ur+-q6 z{dn=(^UZw6*3-XM_(<RgAkH&-pDL?EpQWB+0}Ocm9=BU*bi?i^WCzAt-P_E=^j7uQ zJp(5uPI7JZM!SD<v0mZwiF+Y4#+^E{bJ&_Ib%u7Y%?VW>=@<1_*i&XM4=0t5u!gm6 z{UlmNGPKgO_;e;q9|#esq~Sq`<}%d{+sRmhvsA{5i*91=tub>OZZ%)xUA#4q$dDyy z1`w4%?OPLg3JeZb#cqSMO?*Xn%|-FCcuH2i2fn_{IFusub6;NQdN|7TD1N?%E8*g? z$apAt@cEe!I%jB=*q$p_3=t_5R0ph%{qaq+QDg!c99Y!Xa!&oDZOeis_ot)gNXr{l zdY$|So2Qed2Y7KMNBrS^E169kG%h<+z{Z_p_;shB!uY)>yAVcK=&!bg`lVg)4T1|7 z0}7FpfydVH4F87K@c!nEG+WGKm{Ouo)Slpl;#qcEIQ0zdMfLA#;dBxYw;p;KoVv6| z3_D5&7rJdG12CnDSvZUW?$UC6^UVSW^|vw|o-<z-&aJzK4m~VZX;`tg;&2U(03|Pj z(o!A0R7S223Wc_LBGXV>_4bz)(w5(3AiVhpeT(|=f#x_}E?s#qHZF#xA6AF_ujl$G z-jHD%q(d2}v2PhXx&6YWps~m(^+RXl91Q#xRRJBhjKl$FG4bk);|ag;ieUZ&!Ii3$ z(iGz1+0<uJhdJw}cBg`qOLih!z(iorWL0g>m7#g5>ASldBbNZL=ZHh=tmmJt$!71; zIML2<I_u7^19Fp9*&4(X!_N4V2H2F*qHnBxhbp4XL<U=@wegk?1F^cTIFl=BW!YL9 zZJ<7_xlzvbxZ_I>GhEz1<yO++iVOr?Q^$BLJj5HklRTT~g}HbI`nO&Qz{_@&$=c{y z{mJ8s{#U{pmw^s_lU2nhyXI(OW8F_7a$1>pg@1rQN(M^_691wAGkJ@Pga_05WuQ6! zG5RkGY2^`@(H~pp7&Ga+Pwh3L!Njj!-rc<pwUYhr5h&60NnoyrDiNF1yg|PlxMF7Q z)`%Ath9AWmM3ub~u$CN<fH~FJIQYX=%hkcTyCi%;SynFRxAGkK9GmHE3YoZNqK;F} zv_dY=onQR(t|_Q)-rY$bFJmo{HuA_R-5p=RU;AMFlCoRvk<kg;<r><hO1CpMy^jow zS<Jf)4kO4!*?;^F?!eNmMq1%n;%zz)Uu8GOPgb(#l%vVi(y&{00%EZQBUtbK6K<1; zEa{E<vuhG1%~H?5x8SX*B)mO~!N3j1JtANV(Od7&(MhPAFjo1^@I{n-1fqL|fh3cM z`*KJB)S{POCBJOXB^yuE2autCtK?C*%J)LJdeUyL?R0VKo3JRMf6JFTf}%y{;d@cl z)fjpv%b02+;hai&1SUk!-G6_;^~e{IlzPe2NpEe(hMh>;^bTIfo5<yx?gUnu1-N;o zV+AEV%_&DPUzy2GT^0Cv27dJvj%DBdgJqCDWLx<{_ELWxC{YPoLp+Y@cNb+pXMQQi zMH5Pa%?|8N$;mU(g*d-Q>hP@H##1X8xUZJckrx>id`bAd3QUx9GuomqBYZ!uN1-&o zvTxC?;p8vL67&fW<gd(QFBA_mkrWb&pOGTwbMP%9s?o}GK+aYYK8-yk4vaZ|Ih_nH z;)gJ)PcR}Oa<jL;2NgwtT==R|7?TdnXH^ZQ8>8fw(YOqt>L@bdLrEF*3OgYe$4n4{ zEB40LiU#6-0@5jdN`0w}N0q<o#F?=-Gy}2+lxhW5Pg`|Uz2i&IlVauO=Y1OIiae{< zA=qYfV_@?pzQN5Z7nF6KOa?=bbKJpn5}7IN!<<-IV$I;e17bx-C(xL;Nq(v!b|I$& zaM?MwuvukxZwklVGI?mj0K0;Sv`Afn!3*Y(dSf9+fV}mp{F|TSVGv#qoTt06eO&5G z`Z&Hp;yD^8qw*=7y1A4<;~M=wvUDaYzrtL~buXv5A(nhXEku(y*m(<)nb*{aI6djd zHyadT@ny7`G#3y9%LGc&3BvvOJwyLT)yTSA^CmLaaPa-v3~oUKDm0Y>i@c0~oT2FP z)LNk&a82my?jv(tQpiMi$TK_L@lub#lsM$R{Dk?Ya@%%%hu<UD%S2NYT{)g7kve1m zlwo!miS<|T7PPSnGNQNiZfja>Zkct~tSWM714c!45k}-ZLVA-bVM`>|_ZBbW_m-7| z3U%xrAhi}n?T(2F{_n4EZ10inkIFl#y09?7$uwBoJgqY8vylwev)fDOn;>0R!aEnV zBz%j0Mqpx~EZU3q@%+oV7;}|vt7$~ou@faEIq{p?FY$XXg&6*K)b_LP=}gi9`Bij3 zN`<X7?-!aEEAzErZ)Z7;@M9x}1I53`Gv#J^OW{zPpD07d*{-6<^8kXzNAO2gC*dVf zhp_(%F5)XANQ=zE!Ea>zEo|B6*|-;>S`rNa^BKRDbDAk>X#MsR`EvL>6bqU@SaDDs z8>bu@3YdRaWs*Te@G-UHjU%F~kTHw5(0PVJ+pwh#ha2u;DB+UMo@A5UYIl#5rtBV- zGX_hIpw}3C@H*Us(Cc-d#-gNrG#w$(9+S=GxO>3SR`SE2fHZ2KrDc#_C^$jI>Y}#; zMwY=R6@+dWi~0RXw(c@3GZ&%~9K(q&ee0Zw;pwL`E_tZak-#8^_b)Dpyi73^he?xV zXJ08&wh5-M&}qy4f7!D&=E)puDD(Nmg1d<e^#=qZ=;LyazINR2jIuh(^Hs3|&IH3; zaV`+0L2vV|ZntPrQ3hWP_di%^I*N$-Jqoq9b8ctn2|qHL&AJnLB4Z{K3Q}g9EaF(5 z=$Od3Y9b~H?z$DdTL&^JPLWWblu~tRH-oSdqMs(b?Z}#uz;~6vNG2<P|Kj8l$V|7~ z8Etcp2R*s4aA(xsd&a5bivL9h?wHNxs?Sjp-t^=`l4mGlc;U2sOz@X-MA9eXB0Rcc zOnMUT==UI-TKC=pHz_;`GyXnBf7axaUP(??HB)<^ctf{2Vd8LU9Ccg8dz*=n%y^UZ zVrANQKJnIcsoovd2#5)m&l222xRXmjhd#rxXBOp3g`cyQm9*AbG(#c-iyfj|B4ola z3#odYm*v{79-P>_(j`4LvxM5x_huNg-pGG%9rYqO6mImyJ@}*3Y>^3OvcnTG%EV1) zq_Ap?Z!Iw__7#D=pOWnQN$gB!Mr0!9yx|g<4icJh{cFOu3B8}&RiYm+Mb;VEK``LK zL(NcpcTiGieOIssSjr?ob}^``nNf&UcJhXyncO9m{6gD$kqSD`S69(aF8dkWz5>!9 zBLe4Sib7Hs2x_L2Ls6Ish$MGVK<yUu_`>rGt5+_2zCyP1byaCg3upo+-I}R4&$m)8 zQ7|jc1Z^VWggpuQj*cP;>Zo9LS!VSzrqmZczaf;u`d<Xc_S^t;zRuh97qVr%!m@re z#{=6C5N|kDe_)R4V}}A*W)q>0J(f%Z9r%An@s!e>n9%y=n!<a0{EVz}SHSd#+8ds$ zNVk#Q&HDUSsr<~Xn2={wA#7?rg&w^?I)zIq4E=>IZ_tVGu{Jmsbp}Fk%HJIU<jYe1 zhGa=&{Jy-M?(~n7^ZeFU#v4nE)eZN!S;tv5BKs@>?a+-~bjfLTuH|JExA8EROowzr zqW9{YyZhR0a4clRK>1I4Ncx&WER~{iE;F^$T7K%X@3PGOA%6#Z%p3TS^&M;Dnjw@i z^o!$9nhcsmcHcY4?4j9+ofL_CWsZ4Hcch(rjsGfGD(nsH>w}^ERqGnz%iGj0j{g}h z7wMkJ-2Z2~<cDd>eS>2!i}0~B63i;>SyFJU2+>VC<Gnxr<KI7La?xk?UpZ=uncDx@ zrdSm?Ja4c&)D3OJ{WiP`?lBzl>S^AxaDOx%g6-t0eM^P<3+*z`ztvOqrG3)&#$K?& z_Y0wbWID47@cU`E1A6A&!`aZk0ZE@z-h#l1NqX2#`$Uev2gepW`rf8*!=rD5&;Jb{ zl08rU>dPo=K%-1Ao1~G-@4ve~y5#9E8x;TE0k5d^TC(=Zc>mwjW^c=+U-<9}b0k<N zb{iispNv32N}9&o!|GtH6ZO0WO;6nC7oJT%v|-sgz{rVZ&d4w+?<_(E`DV3>u~}gj z3sbW>R2M6DR!g#NUP;nxo>)@7*=RP{U18SDop6b2&PHce^&h97@xx3t+VK+!keE#} z;(Uf&89as9k8{$nkLbuB!-d7TP`_VJpL^Xs8OKB~ri$YUbW8fch64}7|0EW<IJg1^ zQS&u`Vt}rRI-chv+w}eFB=w9pdOMHb+-X#6OEjeX3l3O*QpdBapJ0uc52>oT(TRj{ z*GT<7Y<7DsrCi79ZsM)z#c(!nNOGySOCkY1fAuQOq12&iUVC!a`#O;dBLf=d?&4*B zI~LgAO7E0qxK(uRTM;IgJ}+z^gD+bi-6I!3x{r9`l~%8T<mDv_=iv5RkK`|}s@`7= z8e1ij5Els0CKBa5LjjV%J|To!_4SBtQA9kd8H&Aj)Q5-J14)fO?}zES_3a)-9`2!a z6ZM&srL>RP%UE0V8E*Sz>Nl1NVG<<7(wDHZ+HcOkQm$O&k+vyx)y)x{Pz!U8hS$*m z<b5)vKA9x8yKOr!SSw_xVHl>Byc0h6BUI*BOpuL==P+H|Hx%`>7!W+1H!l9vi&)`V zyn2o9{z=lc+VX*!Vh~SF=)L}Z40XeG>LF6cP^b+R$NxSe<xZ71sNm%l(0CJp#I()c zP<#zzL&u(>UqbK^Q*UTalKz<ni8|Goe?g0v?*D?zCj_>P8X%{9@RSCXm_NhF>{=S2 zi}ezam_^P`S!!-cyEW9y7DBbK93roz@Raccy*v}?mKXScU9E_4g;hBU7}zSofAFda zKYEe?{{I54<m*x$!vEX%{zF{kVDMo7Z=^aTla&)De=ie&Xz=-;B=jXrt}j6&_<yH@ zfsy`ydw^upaxI9!sDI0`l4Hw7AW&ofC-=&wAQIyKC%;yxKqMypPnK0ELhPmfPa;+_ L!=&c?m;C<#d67$> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4413138..df97d72b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 7101f8e4..9b42019c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## From c69e6f832e3c0bc54d34b49d044912a6fc071cf4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 06:37:12 +0000 Subject: [PATCH 100/181] Update rabbitmq Docker tag to v4 --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 832d5025..3d4f9e70 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,7 +38,7 @@ services: networks: - dps rabbitmq: - image: rabbitmq:3-management + image: rabbitmq:4-management depends_on: - elasticsearch container_name: rabbitmq4indexing From e8e41e3d3cac0aad98c9514c7126e2b7beae8142 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:44:36 +0000 Subject: [PATCH 101/181] Update dependency org.springframework.cloud:spring-cloud-starter-netflix-eureka-client to v4.1.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 538f4b30..e885266b 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { // cloud support implementation "org.springframework.cloud:spring-cloud-starter-config:4.1.3" - implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.2" + implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.3" // springdoc implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springDocVersion}" From e151b4be843713a899ef4f3e197e1c8989cfb053 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 14 Oct 2024 10:59:37 +0200 Subject: [PATCH 102/181] Add Google core library for tests. --- build.gradle | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index e885266b..b6cec39d 100644 --- a/build.gradle +++ b/build.gradle @@ -49,8 +49,8 @@ if (System.getProperty('profile') == 'minimal') { dependencies { // Spring - implementation 'org.springframework:spring-messaging:6.1.1' - implementation 'org.springframework.cloud:spring-cloud-gateway-mvc:4.1.5' + implementation "org.springframework:spring-messaging:6.1.1" + implementation "org.springframework.cloud:spring-cloud-gateway-mvc:4.1.5" // Spring Boot implementation "org.springframework.boot:spring-boot-starter-data-rest" @@ -65,7 +65,7 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-mail" // JSON Parser for Metadata Editor - implementation 'com.googlecode.json-simple:json-simple:1.1.1' + implementation "com.googlecode.json-simple:json-simple:1.1.1" // cloud support @@ -106,7 +106,7 @@ dependencies { implementation "com.networknt:json-schema-validator:1.5.2" // XML validator // https://mvnrepository.com/artifact/xerces/xercesImpl - implementation 'xerces:xercesImpl:2.12.2' + implementation "xerces:xercesImpl:2.12.2" // datamanager implementation "edu.kit.datamanager:repo-core:1.2.2" @@ -121,6 +121,7 @@ dependencies { runtimeOnly "org.apache.httpcomponents:httpclient:4.5.14" // Additional libraries for tests + testImplementation "com.google.guava:guava:33.3.1-jre" testImplementation "org.springframework.restdocs:spring-restdocs-mockmvc:3.0.0" testImplementation "org.springframework.boot:spring-boot-starter-test" testImplementation "org.springframework:spring-test" From e51eb0b6a52ef13e7f540d47022a4688bc7b1108 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 14 Oct 2024 11:45:07 +0200 Subject: [PATCH 103/181] Add test for create date. The create date should not be deleted or changed while updating record. --- .../util/DataResourceRecordUtil.java | 55 ++++++++++------ .../test/MetadataControllerTest.java | 54 ++++++++++++++++ .../test/MetadataControllerTestV2.java | 62 +++++++++++++++++++ 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 5492bf01..09600963 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -30,7 +30,6 @@ import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; import edu.kit.datamanager.metastore2.validation.IValidator; -import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImpl; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; @@ -78,6 +77,7 @@ import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; +import edu.kit.datamanager.repo.domain.Date; /** * Utility class for handling json documents @@ -338,34 +338,51 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur MultipartFile recordDocument, MultipartFile document, UnaryOperator<String> supplier) { - DataResource metadataRecord = null; - metadataRecord = checkParameters(recordDocument, document, false); + DataResource givenDataResource = null; + givenDataResource = checkParameters(recordDocument, document, false); DataResource updatedDataResource; LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); - DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); + DataResource oldDataResource = applicationProperties.getDataResourceService().findById(resourceId); LOG.trace("Checking provided ETag."); - ControllerUtils.checkEtag(eTag, dataResource); - LOG.trace("ETag: '{}'", dataResource.getEtag()); - if (metadataRecord != null) { - LOG.trace("metadataRecord: '{}'", metadataRecord); - metadataRecord.setVersion(dataResource.getVersion()); - metadataRecord.setId(dataResource.getId()); - updatedDataResource = metadataRecord; + ControllerUtils.checkEtag(eTag, oldDataResource); + LOG.trace("ETag: '{}'", oldDataResource.getEtag()); + if (givenDataResource != null) { + LOG.trace("metadataRecord: '{}'", givenDataResource); + givenDataResource.setVersion(oldDataResource.getVersion()); + givenDataResource.setId(oldDataResource.getId()); + updatedDataResource = givenDataResource; if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { - updatedDataResource.setAcls(dataResource.getAcls()); + updatedDataResource.setAcls(oldDataResource.getAcls()); } if (updatedDataResource.getRights() == null) { updatedDataResource.setRights(new HashSet<>()); } if (updatedDataResource.getState() == null) { - updatedDataResource.setState(dataResource.getState()); + updatedDataResource.setState(oldDataResource.getState()); } if (updatedDataResource.getResourceType() == null) { - updatedDataResource.setResourceType(dataResource.getResourceType()); + updatedDataResource.setResourceType(oldDataResource.getResourceType()); } + // Set create date + Date createDate = null; + for (Date date : oldDataResource.getDates()) { + if (date.getType().equals(Date.DATE_TYPE.CREATED)) { + createDate = date; + } + } + Date newCreateDate = null; + for (Date date : updatedDataResource.getDates()) { + if (date.getType().equals(Date.DATE_TYPE.CREATED)) { + newCreateDate = date; + } + } + if (newCreateDate != null) { + updatedDataResource.getDates().remove(newCreateDate); + } + updatedDataResource.getDates().add(createDate); } else { - updatedDataResource = DataResourceUtils.copyDataResource(dataResource); + updatedDataResource = DataResourceUtils.copyDataResource(oldDataResource); } boolean noChanges = false; @@ -402,7 +419,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur if (!noChanges) { // Everything seems to be fine update document and increment version LOG.trace("Updating schema document (and increment version)..."); - String version = dataResource.getVersion(); + String version = oldDataResource.getVersion(); if (version == null) { version = "0"; } @@ -433,14 +450,14 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur } if (noChanges) { - Optional<DataRecord> dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(dataResource.getId()); + Optional<DataRecord> dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(oldDataResource.getId()); if (dataRecord.isPresent()) { dataRecordDao.delete(dataRecord.get()); } } - dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, updatedDataResource, eTag, supplier); + oldDataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, updatedDataResource, eTag, supplier); - return dataResource; + return oldDataResource; } /** diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index 5b12ef8e..9a11c01e 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -1724,6 +1724,60 @@ record = mapper.readValue(body, MetadataRecord.class); } + + @Test + public void testUpdateRecordWithoutCreateDate() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId). + header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + MetadataRecord record = mapper.readValue(body, MetadataRecord.class); + Instant createDate = record.getCreatedAt(); + record.setCreatedAt(null); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/metadata/" + record.getId()). + file(recordFile). + file(metadataFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put("/api/v1/metadata/dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record2 = mapper.readValue(body, MetadataRecord.class); + Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + Assert.assertEquals(createDate, record2.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record2.getSchema().getIdentifier()); + Assert.assertEquals((long) record.getRecordVersion(), record2.getRecordVersion() - 1L);// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record2.getAcl())); + } + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + // Check for new metadata document. + result = this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + + Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + } + @Test public void testDeleteRecordWithoutAuthentication() throws Exception { String metadataRecordId = createDCMetadataRecord(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 219e0dce..7b28980c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -69,6 +69,7 @@ import java.util.stream.Stream; import static edu.kit.datamanager.metastore2.test.CreateSchemaUtil.*; +import edu.kit.datamanager.repo.domain.Date; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -2081,6 +2082,67 @@ record = mapper.readValue(body, DataResource.class); } + @Test + public void testUpdateRecordWithoutCreateDate() throws Exception { + String metadataRecordId = createDCMetadataRecord(); + MvcResult result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String etag = result.getResponse().getHeader("ETag"); + String body = result.getResponse().getContentAsString(); + + ObjectMapper mapper = new ObjectMapper(); + DataResource record = mapper.readValue(body, DataResource.class); + DataResource record2 = mapper.readValue(body, DataResource.class); + Assert.assertTrue(record.getRights().isEmpty()); + Date createDate = null; + for (Date date : record2.getDates()) { + if (date.getType().equals(Date.DATE_TYPE.CREATED)) { + createDate = date; + } + } + record2.getDates().remove(createDate); + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); + MockMultipartFile metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", DC_DOCUMENT_VERSION_2.getBytes()); + + result = this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + file(metadataFile). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + +// result = this.mockMvc.perform(put(API_METADATA_PATH + "dc").header("If-Match", etag).contentType(DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + record2 = mapper.readValue(body, DataResource.class); +// Assert.assertNotEquals(record.getDocumentHash(), record2.getDocumentHash()); + SchemaRegistryControllerTestV2.validateCreateDates(record.getDates(), record2.getDates()); + Assert.assertEquals(DataResourceRecordUtil.getSchemaIdentifier(record), DataResourceRecordUtil.getSchemaIdentifier(record2)); + Assert.assertEquals(Long.parseLong(record.getVersion()), Long.parseLong(record2.getVersion()) - 1L);// version should be 1 higher + SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); + Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); + + // Check for new metadata document. + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + result = this.mockMvc.perform(get(API_METADATA_PATH + metadataRecordId). + accept(MediaType.APPLICATION_XML)). + andDo(print()). + andExpect(status().isOk()). + andReturn(); + String content = result.getResponse().getContentAsString(); + + String dcMetadata = DC_DOCUMENT_VERSION_2; + + Assert.assertEquals(dcMetadata, content); + } + @Test public void testUpdateRecordWithLicenseNull() throws Exception { String metadataRecordId = createDCMetadataRecord(); From f70685ed667cf5b8eb71b709286435373a3d0049 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:33:08 +0000 Subject: [PATCH 104/181] Update dependency org.mockito:mockito-core to v5.14.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 538f4b30..09bda42c 100644 --- a/build.gradle +++ b/build.gradle @@ -127,7 +127,7 @@ dependencies { testImplementation "org.springframework.security:spring-security-test" //Java 11 Support - testImplementation "org.mockito:mockito-core:5.14.1" + testImplementation "org.mockito:mockito-core:5.14.2" testImplementation "junit:junit:4.13.2" testImplementation "com.github.stefanbirkner:system-lambda:1.2.1" From 73c12d72a4837e2df2db68adc84240b1e44ac7c5 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 16 Oct 2024 09:01:27 +0200 Subject: [PATCH 105/181] Fix some bugs regarding transformation between old and new data structure. APIv1 and APIv2 should now be useable in parallel. --- .../util/DataResourceRecordUtil.java | 123 ++++++++---------- .../metastore2/util/MetadataRecordUtil.java | 89 ++----------- .../util/MetadataSchemaRecordUtil.java | 78 ++++++----- .../web/impl/MetadataControllerImpl.java | 2 +- .../test/MetadataControllerTest.java | 11 ++ 5 files changed, 114 insertions(+), 189 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 09600963..b7cdfbee 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -226,7 +226,7 @@ public static DataResource createDataResourceRecord4Metadata(MetastoreConfigurat // validate schema document / determine or correct resource type validateMetadataDocument(applicationProperties, metadataRecord, document); - metadataRecord.setVersion(Long.toString(1)); + metadataRecord.setVersion(getSchemaRecordFromDataResource(metadataRecord).getVersion().toString()); // create record. DataResource dataResource = metadataRecord; DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); @@ -352,6 +352,15 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur givenDataResource.setVersion(oldDataResource.getVersion()); givenDataResource.setId(oldDataResource.getId()); updatedDataResource = givenDataResource; + if ((updatedDataResource.getCreators() == null) || updatedDataResource.getCreators().isEmpty()) { + updatedDataResource.setCreators(oldDataResource.getCreators()); + } + if (updatedDataResource.getPublicationYear() == null) { + updatedDataResource.setPublicationYear(oldDataResource.getPublicationYear()); + } + if (updatedDataResource.getPublisher() == null) { + updatedDataResource.setPublisher(oldDataResource.getPublisher()); + } if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { updatedDataResource.setAcls(oldDataResource.getAcls()); } @@ -555,22 +564,13 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app metadataRecord.setLastUpdate(dataResource.getLastUpdate()); } - for (Identifier identifier : dataResource.getAlternateIdentifiers()) { - if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { - if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().name())); - LOG.trace("Set PID to '{}' of type '{}'", resourceIdentifier.getIdentifier(), resourceIdentifier.getIdentifierType()); - metadataRecord.setPid(resourceIdentifier); - break; - } else { - LOG.debug("'INTERNAL' identifier shouldn't be used! Migrate them to 'URL' if possible."); - } - } + PrimaryIdentifier pid = dataResource.getIdentifier(); + if ((pid != null) && pid.hasDoi()) { + metadataRecord.setPid(ResourceIdentifier.factoryResourceIdentifier(pid.getValue(), IdentifierType.valueOf(pid.getIdentifierType()))); } - Long recordVersion = 1L; if (dataResource.getVersion() != null) { - recordVersion = Long.parseLong(dataResource.getVersion()); + recordVersion = Long.valueOf(dataResource.getVersion()); } metadataRecord.setRecordVersion(recordVersion); @@ -591,10 +591,13 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app //Try to fetch version from URL (only works with URLs including the version as query parameter. Matcher matcher = Pattern.compile(".*[&?]version=(\\d*).*").matcher(resourceIdentifier.getIdentifier()); while (matcher.find()) { - metadataRecord.setSchemaVersion(Long.parseLong(matcher.group(1))); + metadataRecord.setSchemaVersion(Long.valueOf(matcher.group(1))); } } else { - metadataRecord.setSchemaVersion(1L); + // set to current version of schema + SchemaRecord currentSchema = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(resourceIdentifier.getIdentifier()); + + metadataRecord.setSchemaVersion(currentSchema.getVersion()); } LOG.trace("Set schema to '{}'", resourceIdentifier); } @@ -604,15 +607,12 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app LOG.error(message); throw new BadArgumentException(message); } - DataRecord dataRecord = null; LOG.trace("Get document URI from ContentInformation."); ContentInformation info; info = getContentInformationOfResource(applicationProperties, dataResource); if (info != null) { metadataRecord.setDocumentHash(info.getHash()); metadataRecord.setMetadataDocumentUri(info.getContentUri()); - MetadataSchemaRecord currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(schemaConfig, metadataRecord.getSchema()); - metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); } // Only one license allowed. So don't worry about size of set. if (!dataResource.getRights().isEmpty()) { @@ -1701,25 +1701,27 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataResource) { SchemaRecord schemaRecord = null; RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataResource); - String schemaId = schemaIdentifier.getValue(); - LOG.trace("getSchemaRecordFromDataResource: related identifier: '{}'", schemaIdentifier); - LOG.trace("getSchemaRecordFromDataResource: '{}'", schemaId); - switch (schemaIdentifier.getIdentifierType()) { - case URL: - schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); - break; - case INTERNAL: - String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); - if (split.length == 1) { - schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); - } else { - schemaRecord = schemaRecordDao.findBySchemaId(schemaId); - } - break; - default: - String message = "Unsupported identifier type: '" + schemaIdentifier.getIdentifierType() + "'!"; - LOG.error(message); - throw new ResourceNotFoundException(message); + if ((schemaIdentifier != null) && (schemaIdentifier.getValue() != null)) { + String schemaId = schemaIdentifier.getValue(); + LOG.trace("getSchemaRecordFromDataResource: related identifier: '{}'", schemaIdentifier); + LOG.trace("getSchemaRecordFromDataResource: '{}'", schemaId); + switch (schemaIdentifier.getIdentifierType()) { + case URL: + schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); + break; + case INTERNAL: + String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); + if (split.length == 1) { + schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); + } else { + schemaRecord = schemaRecordDao.findBySchemaId(schemaId); + } + break; + default: + String message = "Unsupported identifier type: '" + schemaIdentifier.getIdentifierType() + "'!"; + LOG.error(message); + throw new ResourceNotFoundException(message); + } } return schemaRecord; } @@ -1900,37 +1902,22 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro } boolean validationSuccess = false; StringBuilder errorMessage = new StringBuilder(); - RelatedIdentifier schemaIdentifier = getSchemaIdentifier(metadataRecord); - SchemaRecord findByAlternateId; - if ((schemaIdentifier != null) && (schemaIdentifier.getValue() != null)) { - if (schemaIdentifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { - findByAlternateId = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); - } else { - String schemaId = schemaIdentifier.getValue(); - String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); - - if (split.length > 1) { - findByAlternateId = schemaRecordDao.findBySchemaId(schemaId); - } else { - findByAlternateId = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(split[0] + SCHEMA_VERSION_SEPARATOR); - } - } - if (findByAlternateId != null) { - try { - validateMetadataDocument(metastoreProperties, document, findByAlternateId); - validationSuccess = true; - // After successful validation set type for metadata document resource. - MetadataSchemaRecord.SCHEMA_TYPE type = findByAlternateId.getType(); - metadataRecord.setResourceType(ResourceType.createResourceType(type + METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); - // - } catch (Exception ex) { - String message = "Error validating document!"; - LOG.error(message, ex); - errorMessage.append(ex.getMessage()).append("\n"); - } - } else { - errorMessage.append("No matching schema found for '" + schemaIdentifier.getValue() + "'!"); + SchemaRecord findByAlternateId = getSchemaRecordFromDataResource(metadataRecord); + if (findByAlternateId != null) { + try { + validateMetadataDocument(metastoreProperties, document, findByAlternateId); + validationSuccess = true; + // After successful validation set type for metadata document resource. + MetadataSchemaRecord.SCHEMA_TYPE type = findByAlternateId.getType(); + metadataRecord.setResourceType(ResourceType.createResourceType(type + METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + // + } catch (Exception ex) { + String message = "Error validating document!"; + LOG.error(message, ex); + errorMessage.append(ex.getMessage()).append("\n"); } + } else { + errorMessage.append("No matching schema found for '" + findByAlternateId.getSchemaId() + "'!"); } if (!validationSuccess) { LOG.error(errorMessage.toString()); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 3f492bf9..5258c7ad 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -373,20 +373,14 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati dataResource.getDates().add(Date.factoryDate(metadataRecord.getCreatedAt(), Date.DATE_TYPE.CREATED)); } } - Set<Identifier> identifiers = dataResource.getAlternateIdentifiers(); if (metadataRecord.getPid() != null) { - ResourceIdentifier identifier = metadataRecord.getPid(); - MetadataSchemaRecordUtil.checkAlternateIdentifier(identifiers, identifier.getIdentifier(), Identifier.IDENTIFIER_TYPE.valueOf(identifier.getIdentifierType().name())); + PrimaryIdentifier pid = PrimaryIdentifier.factoryPrimaryIdentifier(); + pid.setIdentifierType(metadataRecord.getPid().getIdentifierType().value()); + pid.setValue(metadataRecord.getPid().getIdentifier()); + dataResource.setIdentifier(pid); } else { - LOG.trace("Remove existing identifiers (others than URL)..."); - Set<Identifier> removeItems = new HashSet<>(); - for (Identifier item : identifiers) { - if (item.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { - LOG.trace("... {}, {}", item.getValue(), item.getIdentifierType()); - removeItems.add(item); - } - } - identifiers.removeAll(removeItems); + LOG.trace("Remove existing identifier"); + dataResource.setIdentifier(null); } boolean relationFound = false; boolean schemaIdFound = false; @@ -445,77 +439,12 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli DataResource dataResource, boolean provideETag) { long nano1 = System.nanoTime() / 1000000; - MetadataRecord metadataRecord = new MetadataRecord(); - if (dataResource != null) { - metadataRecord.setId(dataResource.getId()); - if (provideETag) { + MetadataRecord metadataRecord = DataResourceRecordUtil.migrateToMetadataRecordV2(applicationProperties, dataResource); + if ((metadataRecord != null) && provideETag) { metadataRecord.setETag(dataResource.getEtag()); - } - metadataRecord.setAcl(dataResource.getAcls()); - - for (edu.kit.datamanager.repo.domain.Date d : dataResource.getDates()) { - if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { - LOG.trace("Creation date entry found."); - metadataRecord.setCreatedAt(d.getValue()); - break; - } - } - if (dataResource.getLastUpdate() != null) { - metadataRecord.setLastUpdate(dataResource.getLastUpdate()); - } - - for (Identifier identifier : dataResource.getAlternateIdentifiers()) { - if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { - if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().name())); - LOG.trace("Set PID to '{}' of type '{}'", resourceIdentifier.getIdentifier(), resourceIdentifier.getIdentifierType()); - metadataRecord.setPid(resourceIdentifier); - break; - } else { - LOG.debug("'INTERNAL' identifier shouldn't be used! Migrate them to 'URL' if possible."); - } - } - } - - Long recordVersion = 1L; - if (dataResource.getVersion() != null) { - recordVersion = Long.parseLong(dataResource.getVersion()); - } - metadataRecord.setRecordVersion(recordVersion); - - for (RelatedIdentifier relatedIds : dataResource.getRelatedIdentifiers()) { - LOG.trace("Found related Identifier: '{}'", relatedIds); - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(relatedIds.getValue()); - if (relatedIds.getIdentifierType() != null) { - resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); - } - LOG.trace("Set relation to '{}'", resourceIdentifier); - metadataRecord.setRelatedResource(resourceIdentifier); - } - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); - metadataRecord.setSchema(resourceIdentifier); - if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { - //Try to fetch version from URL (only works with URLs including the version as query parameter. - Matcher matcher = Pattern.compile(".*[&?]version=(\\d*).*").matcher(resourceIdentifier.getIdentifier()); - while (matcher.find()) { - metadataRecord.setSchemaVersion(Long.parseLong(matcher.group(1))); - } - } else { - metadataRecord.setSchemaVersion(1L); - } - LOG.trace("Set schema to '{}'", resourceIdentifier); - } - } - if (metadataRecord.getSchema() == null) { - String message = "Missing schema identifier for metadata document. Not a valid metadata document ID. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } DataRecord dataRecord = null; long nano2 = System.nanoTime() / 1000000; - Optional<DataRecord> dataRecordResult = dataRecordDao.findByMetadataIdAndVersion(dataResource.getId(), recordVersion); + Optional<DataRecord> dataRecordResult = dataRecordDao.findByMetadataIdAndVersion(dataResource.getId(), metadataRecord.getRecordVersion()); long nano3 = System.nanoTime() / 1000000; long nano4 = nano3; boolean isAvailable = false; diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 648314af..e958b6d6 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -28,6 +28,8 @@ import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE; +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; import edu.kit.datamanager.metastore2.validation.IValidator; @@ -67,6 +69,7 @@ import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeAcl; import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeEntry; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; +import org.springframework.util.MimeType; /** * Utility class for handling json documents @@ -388,17 +391,14 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati dataResource.getDates().add(Date.factoryDate(metadataSchemaRecord.getCreatedAt(), Date.DATE_TYPE.CREATED)); } } - Set<Identifier> identifiers = dataResource.getAlternateIdentifiers(); if (metadataSchemaRecord.getPid() != null) { - ResourceIdentifier identifier = metadataSchemaRecord.getPid(); - checkAlternateIdentifier(identifiers, identifier.getIdentifier(), Identifier.IDENTIFIER_TYPE.valueOf(identifier.getIdentifierType().name())); + PrimaryIdentifier pid = PrimaryIdentifier.factoryPrimaryIdentifier(); + pid.setIdentifierType(metadataSchemaRecord.getPid().getIdentifierType().value()); + pid.setValue(metadataSchemaRecord.getPid().getIdentifier()); + dataResource.setIdentifier(pid); } else { - LOG.trace("Remove existing identifiers (others than INTERNAL)..."); - for (Identifier item : identifiers) { - if (item.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { - LOG.trace("... {}, {}", item.getValue(), item.getIdentifierType()); - } - } + LOG.trace("Remove existing identifier"); + dataResource.setIdentifier(null); } String defaultTitle = metadataSchemaRecord.getMimeType(); boolean titleExists = false; @@ -527,32 +527,40 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecord(RepoBaseConfigu MetadataSchemaRecord.SCHEMA_TYPE schemaType = MetadataSchemaRecord.SCHEMA_TYPE.valueOf(type); metadataSchemaRecord.setType(schemaType); } catch (Exception ex) { - message = "Format '" + type + "' is not a valid schema type. Returning HTTP BAD_REQUEST."; + message = "Format '" + type + "' is not a valid schema type. Returning HTTP BAD_REQUEST."; // Test for new schema version ResourceType resourceType = dataResource.getResourceType(); - if (resourceType.getTypeGeneral().equals(ResourceType.TYPE_GENERAL.MODEL) && - resourceType.getValue().endsWith(DataResourceRecordUtil.SCHEMA_SUFFIX)) { + if (resourceType.getTypeGeneral().equals(ResourceType.TYPE_GENERAL.MODEL) + && resourceType.getValue().endsWith(DataResourceRecordUtil.SCHEMA_SUFFIX)) { type = resourceType.getValue().replace(DataResourceRecordUtil.SCHEMA_SUFFIX, ""); try { - metadataSchemaRecord.setType(SCHEMA_TYPE.valueOf(type)); - // new - message = null; + metadataSchemaRecord.setType(SCHEMA_TYPE.valueOf(type)); + // new + message = null; } catch (Exception ex2) { - message = "Format '" + type + "' is not a valid schema type. Returning HTTP BAD_REQUEST."; + message = "Format '" + type + "' is not a valid schema type. Returning HTTP BAD_REQUEST."; } - - } - + + } + } finally { - if (message != null) { - LOG.error(message); - throw new BadArgumentException(message); - + if (message != null) { + LOG.error(message); + throw new BadArgumentException(message); + } + } + SCHEMA_TYPE schemaType = metadataSchemaRecord.getType(); + switch (schemaType) { + case JSON: + metadataSchemaRecord.setMimeType(MediaType.APPLICATION_JSON_VALUE); + break; + case XML: + metadataSchemaRecord.setMimeType(MediaType.APPLICATION_XML_VALUE); + break; + default: + throw new BadArgumentException("Schema type '" + schemaType + "is not implemented yet!"); + } - } - nano3 = System.nanoTime() / 1000000; - metadataSchemaRecord.setMimeType(dataResource.getTitles().iterator().next().getValue()); - nano4 = System.nanoTime() / 1000000; metadataSchemaRecord.setAcl(dataResource.getAcls()); nano5 = System.nanoTime() / 1000000; for (edu.kit.datamanager.repo.domain.Date d : dataResource.getDates()) { @@ -566,19 +574,9 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecord(RepoBaseConfigu if (dataResource.getLastUpdate() != null) { metadataSchemaRecord.setLastUpdate(dataResource.getLastUpdate()); } - Iterator<Identifier> iterator = dataResource.getAlternateIdentifiers().iterator(); - while (iterator.hasNext()) { - Identifier identifier = iterator.next(); - if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.URL) { - if (identifier.getIdentifierType() != Identifier.IDENTIFIER_TYPE.INTERNAL) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(identifier.getValue(), ResourceIdentifier.IdentifierType.valueOf(identifier.getIdentifierType().name())); - LOG.trace("Set PID to '{}' of type '{}'", resourceIdentifier.getIdentifier(), resourceIdentifier.getIdentifierType()); - metadataSchemaRecord.setPid(resourceIdentifier); - break; - } else { - LOG.debug("'INTERNAL' identifier shouldn't be used! Migrate them to 'URL' if possible."); - } - } + PrimaryIdentifier pid = dataResource.getIdentifier(); + if ((pid != null) && pid.hasDoi()) { + metadataSchemaRecord.setPid(ResourceIdentifier.factoryResourceIdentifier(pid.getValue(), IdentifierType.valueOf(pid.getIdentifierType()))); } Long schemaVersion = 1L; diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index b2431cfa..349c1b90 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -449,7 +449,7 @@ public ResponseEntity updateRecord( LOG.trace("Metadata record successfully persisted. Updating document URI and returning result."); String etag = updateMetadataRecord.getEtag(); - MetadataRecordUtil.fixMetadataDocumentUri(updateMetadataRecord); + DataResourceRecordUtil.fixMetadataDocumentUri(updateMetadataRecord); URI locationUri; locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(updateMetadataRecord.getId(), updateMetadataRecord.getRecordVersion(), null, null)).toUri(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index 9a11c01e..d69cf26e 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -1602,6 +1602,17 @@ public void testUpdateRecordWithInvalidSetting4Json() throws Exception { CreateSchemaUtil.ingestOrUpdateXmlMetadataDocument(mockMvc, SCHEMA_ID, 1L, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); } + + @Test + public void testUpdateRecordAndDocument2NewSchemaVersion() throws Exception { + String alternativeSchemaId = "testupdatealsoschema"; + CreateSchemaUtil.ingestXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V1, schemaConfig.getJwtSecret()); + CreateSchemaUtil.ingestOrUpdateXmlSchemaRecord(mockMvc, alternativeSchemaId, CreateSchemaUtil.XML_SCHEMA_V2, schemaConfig.getJwtSecret(), true, status().isOk()); + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, alternativeSchemaId, 1L, "document", CreateSchemaUtil.XML_DOCUMENT_V1, schemaConfig.getJwtSecret()); + // Change version of schema to a higher version whith additional fields. + CreateSchemaUtil.ingestXmlMetadataDocument(mockMvc, alternativeSchemaId, 2L, "document", CreateSchemaUtil.XML_DOCUMENT_V2, schemaConfig.getJwtSecret()); + } + @Test public void testUpdateRecordWithLicense() throws Exception { String metadataRecordId = createDCMetadataRecord(); From cb56c1b3cec50bb83c2744cae4898209c223f66f Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 16 Oct 2024 09:23:07 +0200 Subject: [PATCH 106/181] Fix tests as mimetype of schema is now determined from schema. --- .../test/JsonSchemaRegistryControllerTest.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java index 8d6c9cdc..80be77f6 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/JsonSchemaRegistryControllerTest.java @@ -720,7 +720,8 @@ public void testUpdateRecord() throws Exception { body = result.getResponse().getContentAsString(); MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type was not changed (as it is linked to schema) + Assert.assertNotEquals(record.getMimeType(), record2.getMimeType());//mime type was not changed (as it is linked to schema) Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); // Version shouldn't be updated Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); @@ -748,6 +749,7 @@ public void testUpdateRecordWithoutChanges() throws Exception { ObjectMapper mapper = new ObjectMapper(); MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); + String mimeTypeBefore = record.getMimeType(); record.setMimeType(MediaType.APPLICATION_XML.toString()); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -756,7 +758,8 @@ public void testUpdateRecordWithoutChanges() throws Exception { body = result.getResponse().getContentAsString(); MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertEquals(record.getMimeType(), record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type was not changed (as it is linked to schema) + Assert.assertNotEquals(record.getMimeType(), record2.getMimeType());//mime type was not changed (as it is linked to schema) Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); // Version shouldn't be updated Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); @@ -829,7 +832,8 @@ public void testUpdateRecordAndDocument() throws Exception { body = result.getResponse().getContentAsString(); MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type was not changed (as it is linked to schema) + Assert.assertNotEquals(record.getMimeType(), record2.getMimeType());//mime type was not changed (as it is linked to schema) Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); @@ -912,7 +916,8 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { body = result.getResponse().getContentAsString(); MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type was not changed (as it is linked to schema) + Assert.assertNotEquals(record1.getMimeType(), record2.getMimeType());//mime type was not changed (as it is linked to schema) Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); // Version shouldn't be updated Assert.assertEquals(record1.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); From e41fa9cc9e0fa28ec3a4e8301f0c922edcb25279 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 16 Oct 2024 15:55:20 +0200 Subject: [PATCH 107/181] Fix some tests. --- .../util/DataResourceRecordUtil.java | 10 ++ .../metastore2/util/MetadataRecordUtil.java | 145 ++++++------------ .../web/impl/MetadataControllerImpl.java | 2 +- .../test/MetadataControllerTest.java | 29 ++++ 4 files changed, 83 insertions(+), 103 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index b7cdfbee..6465aa88 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -340,6 +340,16 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur UnaryOperator<String> supplier) { DataResource givenDataResource = null; givenDataResource = checkParameters(recordDocument, document, false); + + return updateDataResource4MetadataDocument(applicationProperties, resourceId, eTag, givenDataResource, document, supplier); + } + + public static DataResource updateDataResource4MetadataDocument(MetastoreConfiguration applicationProperties, + String resourceId, + String eTag, + DataResource givenDataResource, + MultipartFile document, + UnaryOperator<String> supplier) { DataResource updatedDataResource; LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 5258c7ad..8df44b1c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -226,88 +226,49 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica throw new BadArgumentException(message); } } - - LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); - DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); - LOG.trace("Checking provided ETag."); - ControllerUtils.checkEtag(eTag, dataResource); + DataResource givenRecord = null; if (metadataRecord != null) { - existingRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - existingRecord = mergeRecords(existingRecord, metadataRecord); - dataResource = migrateToDataResource(applicationProperties, existingRecord); - } else { - dataResource = DataResourceUtils.copyDataResource(dataResource); - } - - boolean noChanges = false; - if (document != null) { - metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - validateMetadataDocument(applicationProperties, metadataRecord, document); - - ContentInformation info; - String fileName = document.getOriginalFilename(); - info = getContentInformationOfResource(applicationProperties, dataResource); - if (info != null) { - fileName = info.getRelativePath(); - noChanges = true; - // Check for changes... - try { - byte[] currentFileContent; - File file = new File(URI.create(info.getContentUri())); - if (document.getSize() == Files.size(file.toPath())) { - currentFileContent = FileUtils.readFileToByteArray(file); - byte[] newFileContent = document.getBytes(); - for (int index = 0; index < currentFileContent.length; index++) { - if (currentFileContent[index] != newFileContent[index]) { - noChanges = false; - break; - } - } - } else { - noChanges = false; - } - } catch (IOException ex) { - LOG.error("Error reading current file!", ex); - } - } - if (!noChanges) { - // Everything seems to be fine update document and increment version - LOG.trace("Updating schema document (and increment version)..."); - String version = dataResource.getVersion(); - if (version != null) { - dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); - } - ContentDataUtils.addFile(applicationProperties, dataResource, document, fileName, null, true, supplier); - } - - } else { - // validate if document is still valid due to changed record settings. - metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); - - Path metadataDocumentPath = Paths.get(metadataDocumentUri); - if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { - LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); - throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); - } - - try { - InputStream inputStream = Files.newInputStream(metadataDocumentPath); - SchemaRecord schemaRecord = MetadataSchemaRecordUtil.getSchemaRecord(metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); - MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); - } catch (IOException ex) { - LOG.error("Error validating file!", ex); - } - - } - if (noChanges) { - Optional<DataRecord> dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(dataResource.getId()); - if (dataRecord.isPresent()) { - dataRecordDao.delete(dataRecord.get()); - } - } - dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); - + givenRecord = migrateToDataResource(applicationProperties, metadataRecord); + } + DataResource dataResource = DataResourceRecordUtil.updateDataResource4MetadataDocument(applicationProperties, resourceId, eTag, givenRecord, document, supplier); + +// if (!noChanges) { +// // Everything seems to be fine update document and increment version +// LOG.trace("Updating schema document (and increment version)..."); +// String version = dataResource.getVersion(); +// if (version != null) { +// dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); +// } +// ContentDataUtils.addFile(applicationProperties, dataResource, document, fileName, null, true, supplier); +// } +// +// } else { +// // validate if document is still valid due to changed record settings. +// metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); +// URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); +// +// Path metadataDocumentPath = Paths.get(metadataDocumentUri); +// if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { +// LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); +// throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); +// } +// +// try { +// InputStream inputStream = Files.newInputStream(metadataDocumentPath); +// SchemaRecord schemaRecord = MetadataSchemaRecordUtil.getSchemaRecord(metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); +// MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); +// } catch (IOException ex) { +// LOG.error("Error validating file!", ex); +// } +// +// } +// if (noChanges) { +// Optional<DataRecord> dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(dataResource.getId()); +// if (dataRecord.isPresent()) { +// dataRecordDao.delete(dataRecord.get()); +// } +// } +// dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); return migrateToMetadataRecord(applicationProperties, dataResource, true); } @@ -441,7 +402,7 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli long nano1 = System.nanoTime() / 1000000; MetadataRecord metadataRecord = DataResourceRecordUtil.migrateToMetadataRecordV2(applicationProperties, dataResource); if ((metadataRecord != null) && provideETag) { - metadataRecord.setETag(dataResource.getEtag()); + metadataRecord.setETag(dataResource.getEtag()); DataRecord dataRecord = null; long nano2 = System.nanoTime() / 1000000; Optional<DataRecord> dataRecordResult = dataRecordDao.findByMetadataIdAndVersion(dataResource.getId(), metadataRecord.getRecordVersion()); @@ -737,26 +698,6 @@ public static MetadataRecord getRecordByIdAndVersion(MetastoreConfiguration meta return result; } - public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, - String recordId) throws ResourceNotFoundException { - return getMetadataDocumentByIdAndVersion(metastoreProperties, recordId, null); - } - - public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, - String recordId, Long version) throws ResourceNotFoundException { - LOG.trace("Obtaining metadata record with id {} and version {}.", recordId, version); - MetadataRecord metadataRecord = getRecordByIdAndVersion(metastoreProperties, recordId, version); - - URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); - - Path metadataDocumentPath = Paths.get(metadataDocumentUri); - if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { - LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); - throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); - } - return metadataDocumentPath; - } - /** * Merge new metadata record in the existing one. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index 349c1b90..d0341268 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -266,7 +266,7 @@ public ResponseEntity getMetadataDocumentById( ) { LOG.trace("Performing getMetadataDocumentById({}, {}).", id, version); - Path metadataDocumentPath = MetadataRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); + Path metadataDocumentPath = DataResourceRecordUtil.getMetadataDocumentByIdAndVersion(metadataConfig, id, version); return ResponseEntity. ok(). diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index d69cf26e..fa90dd94 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -13,6 +13,7 @@ import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.domain.MetadataRecord; + import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; @@ -1224,6 +1225,34 @@ public void testUpdateRecord() throws Exception { Assert.assertEquals(dcMetadata, content); Assert.assertEquals(record.getMetadataDocumentUri().replace("version=1", "version=2"), record2.getMetadataDocumentUri()); + + // Get old version... + result = this.mockMvc.perform(get("/api/v1/metadata/" + record.getId() + "?version=1").header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE)). + andDo(print()).andExpect(status().isOk()).andReturn(); + +// result = this.mockMvc.perform(put("/api/v1/metadata/dc").header("If-Match", etag).contentType(MetadataRecord.METADATA_RECORD_MEDIA_TYPE).content(mapper.writeValueAsString(record))).andDo(print()).andExpect(status().isOk()).andReturn(); + body = result.getResponse().getContentAsString(); + + MetadataRecord record1 = mapper.readValue(body, MetadataRecord.class); + Assert.assertEquals(record.getDocumentHash(), record1.getDocumentHash()); + Assert.assertNotEquals(record2.getDocumentHash(), record1.getDocumentHash()); + Assert.assertEquals(record.getCreatedAt(), record1.getCreatedAt()); + Assert.assertEquals(record.getSchema().getIdentifier(), record1.getSchema().getIdentifier()); + Assert.assertEquals(record.getRecordVersion(), record1.getRecordVersion());// version should be 1 higher + if (record.getAcl() != null) { + Assert.assertTrue(record.getAcl().containsAll(record1.getAcl())); + } + Assert.assertEquals(record.getLastUpdate(), record1.getLastUpdate()); + // Check for new metadata document. + result = this.mockMvc.perform(get("/api/v1/metadata/" + metadataRecordId + "?version=1")).andDo(print()).andExpect(status().isOk()).andReturn(); + content = result.getResponse().getContentAsString(); + + String dcMetadata1 = DC_DOCUMENT; + + Assert.assertEquals(dcMetadata1, content); + + Assert.assertEquals(record.getMetadataDocumentUri(), record1.getMetadataDocumentUri()); + } @Test From fcf4c9014f1f17ee5ecfa93fea34c3c9a71d0531 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:28:42 +0000 Subject: [PATCH 108/181] Update dependency org.springframework:spring-messaging to v6.1.14 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 538f4b30..567b7229 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,7 @@ if (System.getProperty('profile') == 'minimal') { dependencies { // Spring - implementation 'org.springframework:spring-messaging:6.1.1' + implementation 'org.springframework:spring-messaging:6.1.14' implementation 'org.springframework.cloud:spring-cloud-gateway-mvc:4.1.5' // Spring Boot From cf42439d945650be657d9fa8636eae283af8c075 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:34:13 +0000 Subject: [PATCH 109/181] Update dependency org.springframework.data:spring-data-elasticsearch to v5.3.5 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 538f4b30..de4f57f6 100644 --- a/build.gradle +++ b/build.gradle @@ -113,7 +113,7 @@ dependencies { implementation "edu.kit.datamanager:service-base:1.3.1" // elasticsearch (since service-base 1.1.0) - implementation "org.springframework.data:spring-data-elasticsearch:5.3.4" + implementation "org.springframework.data:spring-data-elasticsearch:5.3.5" // DOIP SDK implementation "net.dona.doip:doip-sdk:2.2.0" From b2d5e3eeb838e7e189bde4c4c9da04605408e651 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 18 Oct 2024 16:24:52 +0200 Subject: [PATCH 110/181] Fix tests. API v1 and API v2 should now be usable in parallel. --- .../util/DataResourceRecordUtil.java | 19 ++++++++++++++++--- .../metastore2/util/MetadataRecordUtil.java | 2 +- .../util/MetadataSchemaRecordUtil.java | 2 +- ...rollerTestWithAuthenticationEnabledV2.java | 10 +++++++++- .../test/SchemaRegistryControllerTest.java | 11 +++++------ .../test/SchemaRegistryControllerTestV2.java | 1 - .../util/MetadataRecordUtilTest.java | 6 +++++- .../util/MetadataSchemaRecordUtilTest.java | 3 ++- 8 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 6465aa88..660bf86b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -373,7 +373,13 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur } if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { updatedDataResource.setAcls(oldDataResource.getAcls()); + } else { + // Check for access rights for changing ACL (need ADMINISTRATION rights!) + MetadataRecordUtil.checkAccessRights(oldDataResource.getAcls(), true); + // Check if access rights still valid afterwards (at least one user with ADMINISTRATION rights should be available!) + MetadataRecordUtil.checkAccessRights(updatedDataResource.getAcls(), false); } + if (updatedDataResource.getRights() == null) { updatedDataResource.setRights(new HashSet<>()); } @@ -606,8 +612,11 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app } else { // set to current version of schema SchemaRecord currentSchema = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(resourceIdentifier.getIdentifier()); - - metadataRecord.setSchemaVersion(currentSchema.getVersion()); + if (currentSchema != null ) { + metadataRecord.setSchemaVersion(currentSchema.getVersion()); + } else { + metadataRecord.setSchemaVersion(1L); + } } LOG.trace("Set schema to '{}'", resourceIdentifier); } @@ -1213,6 +1222,10 @@ public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { public static final void fixSchemaUrl(DataResource dataresource) { RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataresource); + fixSchemaUrl(schemaIdentifier); + } + + public static final void fixSchemaUrl(RelatedIdentifier schemaIdentifier) { if ((schemaIdentifier != null) && (schemaIdentifier.getIdentifierType().equals(Identifier.IDENTIFIER_TYPE.INTERNAL))) { String value = schemaIdentifier.getValue(); StringTokenizer tokenizer = new StringTokenizer(schemaIdentifier.getValue(), SCHEMA_VERSION_SEPARATOR); @@ -1927,7 +1940,7 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro errorMessage.append(ex.getMessage()).append("\n"); } } else { - errorMessage.append("No matching schema found for '" + findByAlternateId.getSchemaId() + "'!"); + errorMessage.append("No matching schema found for '" + getSchemaIdentifier(metadataRecord).getValue() + "'!"); } if (!validationSuccess) { LOG.error(errorMessage.toString()); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 8df44b1c..697e8118 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -308,7 +308,7 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati DataResource dataResource; if (metadataRecord.getId() != null) { try { - dataResource = applicationProperties.getDataResourceService().findById(metadataRecord.getId(), metadataRecord.getRecordVersion()); + dataResource = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, metadataRecord.getId(), metadataRecord.getRecordVersion()); dataResource = DataResourceUtils.copyDataResource(dataResource); } catch (ResourceNotFoundException rnfe) { LOG.error("Error catching DataResource for " + metadataRecord.getId() + " -> " + rnfe.getMessage()); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index e958b6d6..8a854a89 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -393,7 +393,7 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati } if (metadataSchemaRecord.getPid() != null) { PrimaryIdentifier pid = PrimaryIdentifier.factoryPrimaryIdentifier(); - pid.setIdentifierType(metadataSchemaRecord.getPid().getIdentifierType().value()); + pid.setIdentifierType(metadataSchemaRecord.getPid().getIdentifierType().name()); pid.setValue(metadataSchemaRecord.getPid().getIdentifier()); dataResource.setIdentifier(pid); } else { diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java index c05dadb7..331b00cb 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -1304,13 +1304,21 @@ public void testUpdateWrongAclOnly() throws Exception { record.getAcls().add(new AclEntry(otherUserPrincipal, PERMISSION.ADMINISTRATE)); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). + file(recordFile). + header(HttpHeaders.AUTHORIZATION, "Bearer " + userToken). + header("If-Match", etag). + with(putMultipart())). + andDo(print()). + andExpect(status().isBadRequest()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH + record.getId()). file(recordFile). header(HttpHeaders.AUTHORIZATION, "Bearer " + otherUserPrincipal). header("If-Match", etag). with(putMultipart())). andDo(print()). - andExpect(status().isUnauthorized()); + andExpect(status().isForbidden()); } @Test diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index f341bac9..116666bc 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -958,7 +958,7 @@ public void testUpdateRecordAndDocument() throws Exception { ObjectMapper mapper = new ObjectMapper(); MetadataSchemaRecord record = mapper.readValue(body, MetadataSchemaRecord.class); String mimeTypeBefore = record.getMimeType(); - record.setMimeType(MediaType.APPLICATION_JSON.toString()); + record.setMimeType(MediaType.APPLICATION_JSON_VALUE); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA_V2.getBytes()); @@ -969,7 +969,7 @@ public void testUpdateRecordAndDocument() throws Exception { MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); Assert.assertNull(record2.getLicenseUri()); Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type depends on document and can't be changed Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); @@ -1022,7 +1022,7 @@ public void testUpdateRecordAndDocumentWithLicense() throws Exception { MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); Assert.assertNotNull(record2.getLicenseUri()); Assert.assertEquals(record.getLicenseUri(), record2.getLicenseUri()); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type depends on document and can't be changed Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); @@ -1088,7 +1088,7 @@ public void testUpdateRecordAndDocumentWithWrongVersion() throws Exception { body = result.getResponse().getContentAsString(); MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type depends on document and can't be changed Assert.assertEquals(record.getCreatedAt(), record2.getCreatedAt()); testForNextVersion(record.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); // Assert.assertEquals(record.getSchemaDocumentUri().replace("version=1", "version=2"), record2.getSchemaDocumentUri()); @@ -1211,7 +1211,7 @@ public void testUpdateRecordWithoutExplizitGet() throws Exception { body = result.getResponse().getContentAsString(); MetadataSchemaRecord record2 = mapper.readValue(body, MetadataSchemaRecord.class); - Assert.assertNotEquals(mimeTypeBefore, record2.getMimeType());//mime type was changed by update + Assert.assertEquals(mimeTypeBefore, record2.getMimeType());//mime type depends on document and can't be changed Assert.assertEquals(record1.getCreatedAt(), record2.getCreatedAt()); // Version shouldn't be updated Assert.assertEquals(record1.getSchemaDocumentUri(), record2.getSchemaDocumentUri()); @@ -1437,7 +1437,6 @@ public void testMigrateToDataResource() { MetadataSchemaRecord expResult = null; DataResource result = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataSchemaRecord); expResult = MetadataSchemaRecordUtil.migrateToMetadataSchemaRecord(applicationProperties, result, false); - metadataSchemaRecord.setPid(null); assertEquals(metadataSchemaRecord, expResult); // Test with all possible values containing valid PID. metadataSchemaRecord = new MetadataSchemaRecordUtilTest().createSchemaRecord(5, 7, 11, 12); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index e064561f..60a67716 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1554,7 +1554,6 @@ public void testMigrateToDataResource() { MetadataSchemaRecord expResult = null; DataResource result = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataSchemaRecord); expResult = MetadataSchemaRecordUtil.migrateToMetadataSchemaRecord(applicationProperties, result, false); - metadataSchemaRecord.setPid(null); assertEquals(metadataSchemaRecord, expResult); // Test with all possible values containing valid PID. metadataSchemaRecord = new MetadataSchemaRecordUtilTest().createSchemaRecord(5, 7, 11, 12); diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java index b3f7e718..6b988f4d 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java @@ -20,6 +20,7 @@ import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.Date; +import edu.kit.datamanager.repo.domain.PrimaryIdentifier; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.Scheme; import java.time.Instant; @@ -691,7 +692,10 @@ public void testMigrateToMetadataRecord() { assertNull("Last update date should be empty!", result.getLastUpdate()); // Test migration of PID with two alternate identifiers (internal & UPC) - dataResource.getAlternateIdentifiers().add(Identifier.factoryIdentifier(PID, Identifier.IDENTIFIER_TYPE.UPC)); + PrimaryIdentifier pid = PrimaryIdentifier.factoryPrimaryIdentifier(); + pid.setValue(PID); + pid.setIdentifierType(Identifier.IDENTIFIER_TYPE.UPC.name()); + dataResource.setIdentifier(pid); result = MetadataRecordUtil.migrateToMetadataRecord(applicationProperties, dataResource, false); assertNotNull(result.getId()); assertEquals("Id should be the same!", result.getId(), dataResource.getId()); diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java index 0cd29aeb..459b848c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtilTest.java @@ -25,6 +25,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; @@ -317,7 +318,7 @@ public MetadataSchemaRecord createSchemaRecord(int... skipped) { entry.setSid("write"); aclEntries.add(createEntry(1L, PERMISSION.NONE, "none")); aclEntries.add(createEntry(2L, PERMISSION.READ, "read")); - MetadataSchemaRecord msr = buildMSR(aclEntries, "comment", Instant.now().truncatedTo(ChronoUnit.SECONDS), "definition", "eTag", "label", Instant.MAX.truncatedTo(ChronoUnit.SECONDS), true, "mimetype", "pid", "schemadocument", "hash", "schemaId", 1L, SCHEMA_TYPE.XML); + MetadataSchemaRecord msr = buildMSR(aclEntries, "comment", Instant.now().truncatedTo(ChronoUnit.SECONDS), "definition", "eTag", "label", Instant.MAX.truncatedTo(ChronoUnit.SECONDS), true, MediaType.APPLICATION_XML_VALUE, "pid", "schemadocument", "hash", "schemaId", 1L, SCHEMA_TYPE.XML); for (int remove : skipped) { switch (remove) { case 1: From da47a3d6804d597568405453d4dc1e18746c0df1 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 18 Oct 2024 17:00:40 +0200 Subject: [PATCH 111/181] First step adding provenance to datacite record. (no tested yet) --- .../util/DataResourceRecordUtil.java | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 660bf86b..fb6920a5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -449,6 +449,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur version = "0"; } updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); + addProvenance(updatedDataResource); ContentDataUtils.addFile(applicationProperties, updatedDataResource, document, fileName, null, true, supplier); } @@ -484,6 +485,40 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur return oldDataResource; } + /** Add or replace link to predecessor. + * + * @param newDataResource Data resource holding the new version. + */ + public static void addProvenance(DataResource newDataResource) { + if (Long.parseLong(newDataResource.getVersion()) > 1L) { + replaceIsDerivedFrom(newDataResource); + } + } + /** Replace outdated link to predecessor with new one. + * + * @param newDataResource Data resource holding the new version. + */ + public static void replaceIsDerivedFrom(DataResource newDataResource) { + boolean foundOldIdentifier = false; + long oldVersion = Long.parseLong(newDataResource.getVersion()) - 1L; + String urlToPredecessor = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class). + getMetadataDocumentById(newDataResource.getId(), oldVersion, null, null)). + toUri(). + toString(); + for (RelatedIdentifier item : newDataResource.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + String oldUrl = item.getValue(); + item.setValue(urlToPredecessor); + item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + LOG.trace("Fix related identifier 'isDerivedFrom' : '{}' -> '{}'", oldUrl, urlToPredecessor); + foundOldIdentifier = true; + } + } + if (!foundOldIdentifier) { + RelatedIdentifier newRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, urlToPredecessor, null, null); + newDataResource.getRelatedIdentifiers().add(newRelatedIdentifier); + } + } /** * Delete schema document. @@ -612,7 +647,7 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app } else { // set to current version of schema SchemaRecord currentSchema = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(resourceIdentifier.getIdentifier()); - if (currentSchema != null ) { + if (currentSchema != null) { metadataRecord.setSchemaVersion(currentSchema.getVersion()); } else { metadataRecord.setSchemaVersion(1L); From dac4f3ce57fbebf2f300c41c02f2ab0f5f28f02a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 19 Oct 2024 18:14:33 +0000 Subject: [PATCH 112/181] Update dependency org.apache.tika:tika-core to v3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 538f4b30..fb02218b 100644 --- a/build.gradle +++ b/build.gradle @@ -100,7 +100,7 @@ dependencies { // apache implementation "commons-io:commons-io:2.17.0" - implementation "org.apache.tika:tika-core:2.9.2" + implementation "org.apache.tika:tika-core:3.0.0" // JSON validator implementation "com.networknt:json-schema-validator:1.5.2" From 6ffd128d12895529b9ea49f025befe09d1339ad5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 20 Oct 2024 16:38:09 +0000 Subject: [PATCH 113/181] Update plugin net.ltgt.errorprone to v4.1.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 538f4b30..54dda799 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'io.freefair.maven-publish-java' version '8.10.2' id 'org.owasp.dependencycheck' version '10.0.4' id 'org.asciidoctor.jvm.convert' version '4.0.3' - id 'net.ltgt.errorprone' version '4.0.1' + id 'net.ltgt.errorprone' version '4.1.0' id 'net.researchgate.release' version '3.0.2' id 'com.gorylenko.gradle-git-properties' version '2.4.2' id 'java' From 3a62e6ba9c69aab6335aba2918f914ef16a1d855 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 21 Oct 2024 08:47:32 +0200 Subject: [PATCH 114/181] Add provenance also for schemas (API v2 only). --- .../util/DataResourceRecordUtil.java | 23 ++++++++++++++++++- .../impl/SchemaRegistryControllerImplV2.java | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index fb6920a5..83ef480c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -1851,7 +1851,7 @@ public static void setUrl2PathDao(IUrl2PathDao aUrl2PathDao) { * @param supplier Method for creating access URL. * @return Record of updated schema document. */ - public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration applicationProperties, + public static DataResource updateDataResource4SchemaDocument(MetastoreConfiguration applicationProperties, String resourceId, String eTag, MultipartFile recordDocument, @@ -1871,6 +1871,26 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app } else { dataResource = DataResourceUtils.copyDataResource(dataResource); } + return updateDataResource4SchemaDocument(applicationProperties, resourceId, eTag, dataResource, schemaDocument, supplier); + } + + /** + * Update schema document. + * + * @param applicationProperties Settings of repository. + * @param resourceId ID of the schema document. + * @param eTag E-Tag of the current schema document. + * @param recordDocument Record of the schema. + * @param schemaDocument Schema document. + * @param supplier Method for creating access URL. + * @return Record of updated schema document. + */ + public static DataResource updateDataResource4SchemaDocument(MetastoreConfiguration applicationProperties, + String resourceId, + String eTag, + DataResource dataResource, + MultipartFile schemaDocument, + UnaryOperator<String> supplier) { ContentInformation info; info = getContentInformationOfResource(applicationProperties, dataResource); @@ -1911,6 +1931,7 @@ public static DataResource updateMetadataSchemaRecord(MetastoreConfiguration app if (version != null) { dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); } + addProvenance(dataResource); ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); SchemaRecord schemaRecord = createSchemaRecord(dataResource, contentInformation); MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 5ec53acc..96675bf5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -339,7 +339,7 @@ public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final UnaryOperator<String> getById; getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); String eTag = ControllerUtils.getEtagFromHeader(request); - DataResource updatedSchemaRecord = DataResourceRecordUtil.updateMetadataSchemaRecord(schemaConfig, schemaId, eTag, schemaRecord, document, getById); + DataResource updatedSchemaRecord = DataResourceRecordUtil.updateDataResource4SchemaDocument(schemaConfig, schemaId, eTag, schemaRecord, document, getById); LOG.trace("DataResource record successfully persisted. Updating document URI and returning result."); String etag = updatedSchemaRecord.getEtag(); From 9375a2d54931d45981f64fd824a0c87a12004cf7 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 21 Oct 2024 12:55:07 +0200 Subject: [PATCH 115/181] Fix tests and refactoring. --- .../util/DataResourceRecordUtil.java | 158 +++++++++-------- .../util/MetadataSchemaRecordUtil.java | 165 +++++++++--------- .../test/SchemaRegistryControllerTestV2.java | 46 ++++- 3 files changed, 210 insertions(+), 159 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 83ef480c..f6c32754 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -352,63 +352,12 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur UnaryOperator<String> supplier) { DataResource updatedDataResource; - LOG.trace("Obtaining most recent metadata record with id {}.", resourceId); + LOG.trace("Obtaining most recent datacite record with id {}.", resourceId); DataResource oldDataResource = applicationProperties.getDataResourceService().findById(resourceId); LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, oldDataResource); LOG.trace("ETag: '{}'", oldDataResource.getEtag()); - if (givenDataResource != null) { - LOG.trace("metadataRecord: '{}'", givenDataResource); - givenDataResource.setVersion(oldDataResource.getVersion()); - givenDataResource.setId(oldDataResource.getId()); - updatedDataResource = givenDataResource; - if ((updatedDataResource.getCreators() == null) || updatedDataResource.getCreators().isEmpty()) { - updatedDataResource.setCreators(oldDataResource.getCreators()); - } - if (updatedDataResource.getPublicationYear() == null) { - updatedDataResource.setPublicationYear(oldDataResource.getPublicationYear()); - } - if (updatedDataResource.getPublisher() == null) { - updatedDataResource.setPublisher(oldDataResource.getPublisher()); - } - if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { - updatedDataResource.setAcls(oldDataResource.getAcls()); - } else { - // Check for access rights for changing ACL (need ADMINISTRATION rights!) - MetadataRecordUtil.checkAccessRights(oldDataResource.getAcls(), true); - // Check if access rights still valid afterwards (at least one user with ADMINISTRATION rights should be available!) - MetadataRecordUtil.checkAccessRights(updatedDataResource.getAcls(), false); - } - - if (updatedDataResource.getRights() == null) { - updatedDataResource.setRights(new HashSet<>()); - } - if (updatedDataResource.getState() == null) { - updatedDataResource.setState(oldDataResource.getState()); - } - if (updatedDataResource.getResourceType() == null) { - updatedDataResource.setResourceType(oldDataResource.getResourceType()); - } - // Set create date - Date createDate = null; - for (Date date : oldDataResource.getDates()) { - if (date.getType().equals(Date.DATE_TYPE.CREATED)) { - createDate = date; - } - } - Date newCreateDate = null; - for (Date date : updatedDataResource.getDates()) { - if (date.getType().equals(Date.DATE_TYPE.CREATED)) { - newCreateDate = date; - } - } - if (newCreateDate != null) { - updatedDataResource.getDates().remove(newCreateDate); - } - updatedDataResource.getDates().add(createDate); - } else { - updatedDataResource = DataResourceUtils.copyDataResource(oldDataResource); - } + updatedDataResource = mergeDataResource(oldDataResource, givenDataResource); boolean noChanges = false; if (document != null) { @@ -485,8 +434,10 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur return oldDataResource; } - /** Add or replace link to predecessor. - * + + /** + * Add or replace link to predecessor. + * * @param newDataResource Data resource holding the new version. */ public static void addProvenance(DataResource newDataResource) { @@ -494,8 +445,10 @@ public static void addProvenance(DataResource newDataResource) { replaceIsDerivedFrom(newDataResource); } } - /** Replace outdated link to predecessor with new one. - * + + /** + * Replace outdated link to predecessor with new one. + * * @param newDataResource Data resource holding the new version. */ public static void replaceIsDerivedFrom(DataResource newDataResource) { @@ -516,6 +469,7 @@ public static void replaceIsDerivedFrom(DataResource newDataResource) { } if (!foundOldIdentifier) { RelatedIdentifier newRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, urlToPredecessor, null, null); + newRelatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); newDataResource.getRelatedIdentifiers().add(newRelatedIdentifier); } } @@ -1880,7 +1834,7 @@ public static DataResource updateDataResource4SchemaDocument(MetastoreConfigurat * @param applicationProperties Settings of repository. * @param resourceId ID of the schema document. * @param eTag E-Tag of the current schema document. - * @param recordDocument Record of the schema. + * @param givenDataResource Record of the schema. * @param schemaDocument Schema document. * @param supplier Method for creating access URL. * @return Record of updated schema document. @@ -1888,15 +1842,21 @@ public static DataResource updateDataResource4SchemaDocument(MetastoreConfigurat public static DataResource updateDataResource4SchemaDocument(MetastoreConfiguration applicationProperties, String resourceId, String eTag, - DataResource dataResource, + DataResource givenDataResource, MultipartFile schemaDocument, UnaryOperator<String> supplier) { - + DataResource updatedDataResource; + LOG.trace("Obtaining most recent datacite record with id {}.", resourceId); + DataResource oldDataResource = applicationProperties.getDataResourceService().findById(resourceId); + LOG.trace("Checking provided ETag."); + ControllerUtils.checkEtag(eTag, oldDataResource); + LOG.trace("ETag: '{}'", oldDataResource.getEtag()); + updatedDataResource = mergeDataResource(oldDataResource, givenDataResource); ContentInformation info; - info = getContentInformationOfResource(applicationProperties, dataResource); + info = getContentInformationOfResource(applicationProperties, updatedDataResource); if (schemaDocument != null) { // Get schema record for this schema - validateMetadataSchemaDocument(applicationProperties, dataResource, schemaDocument); + validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDocument); boolean noChanges = false; String fileName = schemaDocument.getOriginalFilename(); @@ -1927,13 +1887,13 @@ public static DataResource updateDataResource4SchemaDocument(MetastoreConfigurat if (!noChanges) { // Everything seems to be fine update document and increment version LOG.trace("Updating schema document (and increment version)..."); - String version = dataResource.getVersion(); + String version = updatedDataResource.getVersion(); if (version != null) { - dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); + updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); } - addProvenance(dataResource); - ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); - SchemaRecord schemaRecord = createSchemaRecord(dataResource, contentInformation); + addProvenance(updatedDataResource); + ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, updatedDataResource, schemaDocument, fileName, null, true, supplier); + SchemaRecord schemaRecord = createSchemaRecord(updatedDataResource, contentInformation); MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); } } else { @@ -1951,15 +1911,73 @@ public static DataResource updateDataResource4SchemaDocument(MetastoreConfigurat try { byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); - DataResourceRecordUtil.validateMetadataSchemaDocument(applicationProperties, dataResource, schemaDoc); + DataResourceRecordUtil.validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDoc); } catch (IOException ex) { LOG.error("Error validating file!", ex); } } - dataResource = DataResourceUtils.updateResource(applicationProperties, dataResource.getId(), dataResource, eTag, supplier); + updatedDataResource = DataResourceUtils.updateResource(applicationProperties, updatedDataResource.getId(), updatedDataResource, eTag, supplier); + + return updatedDataResource; + } + + private static DataResource mergeDataResource(DataResource oldDataResource, DataResource givenDataResource) { + DataResource updatedDataResource; + + if (givenDataResource != null) { + LOG.trace("new DataResource: '{}'", givenDataResource); + givenDataResource.setVersion(oldDataResource.getVersion()); + givenDataResource.setId(oldDataResource.getId()); + updatedDataResource = givenDataResource; + if ((updatedDataResource.getCreators() == null) || updatedDataResource.getCreators().isEmpty()) { + updatedDataResource.setCreators(oldDataResource.getCreators()); + } + if (updatedDataResource.getPublicationYear() == null) { + updatedDataResource.setPublicationYear(oldDataResource.getPublicationYear()); + } + if (updatedDataResource.getPublisher() == null) { + updatedDataResource.setPublisher(oldDataResource.getPublisher()); + } + if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { + updatedDataResource.setAcls(oldDataResource.getAcls()); + } else { + // Check for access rights for changing ACL (need ADMINISTRATION rights!) + MetadataRecordUtil.checkAccessRights(oldDataResource.getAcls(), true); + // Check if access rights still valid afterwards (at least one user with ADMINISTRATION rights should be available!) + MetadataRecordUtil.checkAccessRights(updatedDataResource.getAcls(), false); + } - return dataResource; + if (updatedDataResource.getRights() == null) { + updatedDataResource.setRights(new HashSet<>()); + } + if (updatedDataResource.getState() == null) { + updatedDataResource.setState(oldDataResource.getState()); + } + if (updatedDataResource.getResourceType() == null) { + updatedDataResource.setResourceType(oldDataResource.getResourceType()); + } + // Set create date + Date createDate = null; + for (Date date : oldDataResource.getDates()) { + if (date.getType().equals(Date.DATE_TYPE.CREATED)) { + createDate = date; + } + } + Date newCreateDate = null; + for (Date date : updatedDataResource.getDates()) { + if (date.getType().equals(Date.DATE_TYPE.CREATED)) { + newCreateDate = date; + } + } + if (newCreateDate != null) { + updatedDataResource.getDates().remove(newCreateDate); + } + updatedDataResource.getDates().add(createDate); + } else { + updatedDataResource = DataResourceUtils.copyDataResource(oldDataResource); + } + return updatedDataResource; } /** diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 8a854a89..6022a0fe 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -236,88 +236,91 @@ public static MetadataSchemaRecord updateMetadataSchemaRecord(MetastoreConfigura } } - LOG.trace("Obtaining most recent metadata schema record with id {}.", resourceId); - DataResource dataResource = applicationProperties.getDataResourceService().findById(resourceId); - LOG.trace("Checking provided ETag."); - ControllerUtils.checkEtag(eTag, dataResource); - SchemaRecord schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(dataResource.getId() + "/"); + DataResource givenRecord = null; if (metadataRecord != null) { - metadataRecord.setSchemaVersion(schemaRecord.getVersion()); - MetadataSchemaRecord existingRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); - existingRecord = mergeRecords(existingRecord, metadataRecord); - mergeSchemaRecord(schemaRecord, existingRecord); - dataResource = migrateToDataResource(applicationProperties, existingRecord); - } else { - dataResource = DataResourceUtils.copyDataResource(dataResource); - } - - if (schemaDocument != null) { - // Get schema record for this schema - validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDocument); - - ContentInformation info; - info = getContentInformationOfResource(applicationProperties, dataResource); - - boolean noChanges = false; - String fileName = schemaDocument.getOriginalFilename(); - if (info != null) { - noChanges = true; - fileName = info.getRelativePath(); - // Check for changes... - try { - byte[] currentFileContent; - File file = new File(URI.create(info.getContentUri())); - if (schemaDocument.getSize() == Files.size(file.toPath())) { - currentFileContent = FileUtils.readFileToByteArray(file); - byte[] newFileContent = schemaDocument.getBytes(); - for (int index = 0; index < currentFileContent.length; index++) { - if (currentFileContent[index] != newFileContent[index]) { - noChanges = false; - break; - } - } - } else { - noChanges = false; - } - } catch (IOException ex) { - LOG.error("Error reading current file!", ex); - throw new BadArgumentException("Error reading schema document!"); - } - } - if (!noChanges) { - // Everything seems to be fine update document and increment version - LOG.trace("Updating schema document (and increment version)..."); - String version = dataResource.getVersion(); - if (version != null) { - dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); - } - ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); - } else { - schemaRecordDao.delete(schemaRecord); - } - } else { - schemaRecordDao.delete(schemaRecord); - // validate if document is still valid due to changed record settings. - metadataRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); - URI schemaDocumentUri = URI.create(metadataRecord.getSchemaDocumentUri()); - - Path schemaDocumentPath = Paths.get(schemaDocumentUri); - if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { - LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); - throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); - } - - try { - byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); - MetadataSchemaRecordUtil.validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDoc); - } catch (IOException ex) { - LOG.error("Error validating file!", ex); - } - - } - dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); - - return migrateToMetadataSchemaRecord(applicationProperties, dataResource, true); + givenRecord = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataRecord); + } + DataResource updateDataResource = DataResourceRecordUtil.updateDataResource4SchemaDocument(applicationProperties, resourceId, eTag, givenRecord, schemaDocument, supplier); +// LOG.trace("Checking provided ETag."); +// ControllerUtils.checkEtag(eTag, dataResource); +// SchemaRecord schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(dataResource.getId() + "/"); +// if (metadataRecord != null) { +// metadataRecord.setSchemaVersion(schemaRecord.getVersion()); +// MetadataSchemaRecord existingRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); +// existingRecord = mergeRecords(existingRecord, metadataRecord); +// mergeSchemaRecord(schemaRecord, existingRecord); +// dataResource = migrateToDataResource(applicationProperties, existingRecord); +// } else { +// dataResource = DataResourceUtils.copyDataResource(dataResource); +// } +// +// if (schemaDocument != null) { +// // Get schema record for this schema +// validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDocument); +// +// ContentInformation info; +// info = getContentInformationOfResource(applicationProperties, dataResource); +// +// boolean noChanges = false; +// String fileName = schemaDocument.getOriginalFilename(); +// if (info != null) { +// noChanges = true; +// fileName = info.getRelativePath(); +// // Check for changes... +// try { +// byte[] currentFileContent; +// File file = new File(URI.create(info.getContentUri())); +// if (schemaDocument.getSize() == Files.size(file.toPath())) { +// currentFileContent = FileUtils.readFileToByteArray(file); +// byte[] newFileContent = schemaDocument.getBytes(); +// for (int index = 0; index < currentFileContent.length; index++) { +// if (currentFileContent[index] != newFileContent[index]) { +// noChanges = false; +// break; +// } +// } +// } else { +// noChanges = false; +// } +// } catch (IOException ex) { +// LOG.error("Error reading current file!", ex); +// throw new BadArgumentException("Error reading schema document!"); +// } +// } +// if (!noChanges) { +// // Everything seems to be fine update document and increment version +// LOG.trace("Updating schema document (and increment version)..."); +// String version = dataResource.getVersion(); +// if (version != null) { +// dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); +// } +// ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); +// } else { +// schemaRecordDao.delete(schemaRecord); +// } +// } else { +// schemaRecordDao.delete(schemaRecord); +// // validate if document is still valid due to changed record settings. +// metadataRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); +// URI schemaDocumentUri = URI.create(metadataRecord.getSchemaDocumentUri()); +// +// Path schemaDocumentPath = Paths.get(schemaDocumentUri); +// if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { +// LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); +// throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); +// } +// +// try { +// byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); +// MetadataSchemaRecordUtil.validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDoc); +// } catch (IOException ex) { +// LOG.error("Error validating file!", ex); +// } +// +// } +// dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); + + return migrateToMetadataSchemaRecord(applicationProperties, updateDataResource, true); } /** diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 60a67716..86de349f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.CollectionType; import edu.kit.datamanager.entities.Identifier; +import edu.kit.datamanager.entities.Identifier.IDENTIFIER_TYPE; import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; @@ -76,6 +77,7 @@ import java.time.Instant; import java.util.*; import java.util.stream.Stream; +import org.apache.commons.lang.StringUtils; import static org.junit.Assert.assertEquals; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; @@ -2243,23 +2245,51 @@ public static void validateIdentifierSets(Set<Identifier> first, Set<Identifier> } public static void validateRelatedIdentifierSets(Set<RelatedIdentifier> first, Set<RelatedIdentifier> second) { - if (first == second) { - return; + // Check for provenance first... + RelatedIdentifier provenanceFirstRelatedIdentifier = null; + RelatedIdentifier provenanceSecondRelatedIdentifier = null; + Set<RelatedIdentifier> copyFirst = new HashSet<>(); + Set<RelatedIdentifier> copySecond = new HashSet<>(); + copyFirst.addAll(first); + copySecond.addAll(second); + for (RelatedIdentifier item : copyFirst) { + if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + provenanceFirstRelatedIdentifier = item; + Assert.assertEquals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, item.getRelationType()); + Assert.assertEquals(IDENTIFIER_TYPE.URL, item.getIdentifierType()); + break; + } } - Assert.assertEquals(first.size(), second.size()); - Set<RelatedIdentifier> copy = new HashSet<>(); - copy.addAll(second); + for (RelatedIdentifier item : copySecond) { + if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + Assert.assertEquals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, item.getRelationType()); + Assert.assertEquals(IDENTIFIER_TYPE.URL, item.getIdentifierType()); + provenanceSecondRelatedIdentifier = item; + if (provenanceFirstRelatedIdentifier != null) { + int indexOfDifference = StringUtils.indexOfDifference(provenanceFirstRelatedIdentifier.getValue(), provenanceSecondRelatedIdentifier.getValue()); + Assert.assertTrue("Provenance differ to much : " + provenanceFirstRelatedIdentifier + " <-> " + provenanceSecondRelatedIdentifier, indexOfDifference > "http://localhost:41112/metastore".length()); + } + break; + } + } + if (provenanceFirstRelatedIdentifier != null) { + copyFirst.remove(provenanceFirstRelatedIdentifier); + } + if (provenanceSecondRelatedIdentifier != null) { + copySecond.remove(provenanceSecondRelatedIdentifier); + } + + Assert.assertEquals(copyFirst.size(), copySecond.size()); boolean identical; - for (RelatedIdentifier item : first) { + for (RelatedIdentifier item : copyFirst) { identical = false; - for (RelatedIdentifier item2 : copy) { + for (RelatedIdentifier item2 : copySecond) { identical = (item.getIdentifierType() == item2.getIdentifierType()) && ((item.getValue() == item2.getValue()) || item.getValue().equals(item2.getValue())) && (item.getRelationType() == item2.getRelationType()) && ((item.getScheme() == item2.getScheme()) || (item.getScheme().getSchemeId().equals(item2.getScheme().getSchemeId()) && item.getScheme().getSchemeUri().equals(item2.getScheme().getSchemeUri()))); if (identical) { - copy.remove(item2); break; } } From 972e1eb77cd639d07f04dc7a98257bbe064756ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:41:14 +0000 Subject: [PATCH 116/181] Update dependency org.springframework.restdocs:spring-restdocs-mockmvc to v3.0.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09cbac3e..4c76373d 100644 --- a/build.gradle +++ b/build.gradle @@ -121,7 +121,7 @@ dependencies { runtimeOnly "org.apache.httpcomponents:httpclient:4.5.14" // Additional libraries for tests - testImplementation "org.springframework.restdocs:spring-restdocs-mockmvc:3.0.0" + testImplementation "org.springframework.restdocs:spring-restdocs-mockmvc:3.0.2" testImplementation "org.springframework.boot:spring-boot-starter-test" testImplementation "org.springframework:spring-test" testImplementation "org.springframework.security:spring-security-test" From 4ba43d6859c4c22c96cafadc2a42f6f94fbe5d1e Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 22 Oct 2024 14:58:15 +0200 Subject: [PATCH 117/181] Fix migration to enable related identifier to previous version. Fix also bug if only one version is available. --- .../runner/ElasticIndexerRunner.java | 137 +++++++++++++----- 1 file changed, 100 insertions(+), 37 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 79f9f583..36f6e4bf 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -60,9 +60,9 @@ /** * This class contains 2 runners: - * <ul><li>Runner for indexing all metadata documents of given schemas Arguments have to - * start with at least 'reindex' followed by all indices which have to be - * reindexed. If no indices are given all indices will be reindexed.</li> + * <ul><li>Runner for indexing all metadata documents of given schemas Arguments + * have to start with at least 'reindex' followed by all indices which have to + * be reindexed. If no indices are given all indices will be reindexed.</li> * <li>Runner for migrating dataresources from version 1 to version2. */ @Component @@ -96,12 +96,16 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Parameter(names = {"--indices", "-i"}, description = "Only for given indices (comma separated) or all indices if not present.") Set<String> indices; - /** + /** * Restrict reindexing to dataresources new than given date. */ @Parameter(names = {"--updateDate", "-u"}, description = "Starting reindexing only for documents updated at earliest on update date.") Date updateDate; - /** + /** + * Determine the baseUrl of the service. + */ + private String baseUrl; + /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexerRunner.class); @@ -110,12 +114,12 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Autowired private IDataResourceDao dataResourceDao; - /** + /** * DAO for all schema records. */ @Autowired private ISchemaRecordDao schemaRecordDao; - /** + /** * DAO for all data records. */ @Autowired @@ -130,14 +134,14 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Autowired private Javers javers; - /** + /** * Instance of schema repository. */ @Autowired private MetastoreConfiguration schemaConfig; /** * Instande of metadata reository. - * + * */ @Autowired private MetastoreConfiguration metadataConfig; @@ -148,10 +152,12 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Autowired private Optional<IMessagingService> messagingService; + /** * Start runner for actions before starting service. + * * @param args Arguments for the runner. - * @throws Exception Something went wrong. + * @throws Exception Something went wrong. */ @Override @SuppressWarnings({"StringSplitter", "JavaUtilDate"}) @@ -161,6 +167,15 @@ public void run(String... args) throws Exception { .build(); try { argueParser.parse(args); + List<SchemaRecord> findAllSchemas = schemaRecordDao.findAll(PageRequest.of(0, 1)).getContent(); + if (!findAllSchemas.isEmpty()) { + // There is at least one schema. + // Try to fetch baseURL from this + SchemaRecord get = findAllSchemas.get(0); + Url2Path findByPath = url2PathDao.findByPath(get.getSchemaDocumentUri()).get(0); + baseUrl = findByPath.getUrl().split("/api/v1/schema")[0]; + LOG.trace("Found baseUrl: '{}'", baseUrl); + } if (updateIndex) { if (updateDate == null) { updateDate = new Date(0); @@ -171,10 +186,6 @@ public void run(String... args) throws Exception { updateElasticsearchIndex(); } if (doMigration2DataCite) { - // Set adminitrative rights for reading. - JwtAuthenticationToken jwtAuthenticationToken = JwtBuilder.createServiceToken("migrationTool", RepoServiceRole.SERVICE_READ).getJwtAuthenticationToken(schemaConfig.getJwtSecret()); - SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken); - migrateToVersion2(); } } catch (Exception ex) { @@ -183,8 +194,10 @@ public void run(String... args) throws Exception { System.exit(0); } } + /** * Start runner to reindex dataresources according to the given parameters. + * * @throws InterruptedException Something went wrong. */ private void updateElasticsearchIndex() throws InterruptedException { @@ -197,8 +210,6 @@ private void updateElasticsearchIndex() throws InterruptedException { // Try to fetch baseURL from this SchemaRecord get = findAllSchemas.get(0); Url2Path findByPath = url2PathDao.findByPath(get.getSchemaDocumentUri()).get(0); - String baseUrl = findByPath.getUrl().split("/api/v1/schema")[0]; - LOG.trace("Found baseUrl: '{}'", baseUrl); determineIndices(indices); @@ -220,10 +231,11 @@ private void updateElasticsearchIndex() throws InterruptedException { LOG.trace("Finished ElasticIndexerRunner!"); } - + /** - * Determine all indices if an empty set is provided. - * Otherwise return provided set without any change. + * Determine all indices if an empty set is provided. Otherwise return + * provided set without any change. + * * @param indices Indices which should be reindexed. */ private void determineIndices(Set<String> indices) { @@ -303,13 +315,17 @@ private String toSchemaUrl(DataRecord dataRecord, String baseUrl) { schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(dataRecord.getSchemaId(), dataRecord.getVersion(), null, null)).toUri(); return schemaUrl; } - + /** * Migrate all data resources from version 1 to version 2. - * @throws InterruptedException + * + * @throws InterruptedException */ private void migrateToVersion2() throws InterruptedException { LOG.info("Start Migrate2DataCite Runner for migrating database from version 1 to version 2."); + // Set adminitrative rights for reading. + JwtAuthenticationToken jwtAuthenticationToken = JwtBuilder.createServiceToken("migrationTool", RepoServiceRole.SERVICE_READ).getJwtAuthenticationToken(schemaConfig.getJwtSecret()); + SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken); // Try to determine URL of repository // Search for resource type of MetadataSchemaRecord Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); @@ -346,6 +362,7 @@ private void migrateAllSchemasToDataciteVersion2() { /** * Migrate dataresources of schemas using version 1 to version 2. + * * @param schema Current version of schema document. */ private void migrateSchemaToDataciteVersion2(DataResource schema) { @@ -355,18 +372,21 @@ private void migrateSchemaToDataciteVersion2(DataResource schema) { for (long versionNo = 1; versionNo <= version; versionNo++) { saveSchema(id, versionNo); } + LOG.info("Migration for schema document with ID: '{}', finished! No of versions: '{}'", id, version); } - /** - * Migrate metadata of schema document from version 1 to version 2 and store new version in the database. + * Migrate metadata of schema document from version 1 to version 2 and store + * new version in the database. + * * @param id ID of the schema document. * @param version Version of the schema document. * @param format Format of the schema document. (XML/JSON) */ private void saveSchema(String id, long version) { - DataResource currentVersion = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); - DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentVersion); + LOG.info("Migrate datacite for schema document with id: '{}' / version: '{}'", id, version); + DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); + DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); // Remove type from first title with type 'OTHER' for (Title title : recordByIdAndVersion.getTitles()) { if (title.getTitleType() == Title.TYPE.OTHER) { @@ -378,13 +398,33 @@ private void saveSchema(String id, long version) { ResourceType resourceType = recordByIdAndVersion.getResourceType(); resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); resourceType.setValue(recordByIdAndVersion.getFormats().iterator().next() + DataResourceRecordUtil.SCHEMA_SUFFIX); + // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' + for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + } + } + // Add provenance + if (version > 1) { + String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(id, version - 1l, null, null)).toString(); + RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); + recordByIdAndVersion.getRelatedIdentifiers().add(provenance); + LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); + } else { + long currentVersion = metadataConfig.getAuditService().getCurrentVersion(id); + if (currentVersion == 1l) { + // Create an additional version for JaVers if only one version exists. + currentDataResource.setPublisher("migrationTool"); + metadataConfig.getAuditService().captureAuditInformation(currentDataResource, "Just4fun"); + } + } // Save migrated version - LOG.trace("Persisting created resource."); - dataResourceDao.save(recordByIdAndVersion); + LOG.trace("Persisting created schema document resource."); + DataResource migratedDataResource = dataResourceDao.save(recordByIdAndVersion); //Capture state change LOG.trace("Capturing audit information."); - schemaConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); + schemaConfig.getAuditService().captureAuditInformation(migratedDataResource, "migration2version2"); } @@ -395,7 +435,7 @@ private void migrateAllMetadataDocumentsToDataciteVersion2() { Specification spec; spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE, ResourceType.TYPE_GENERAL.DATASET)); int pageNumber = 0; - int pageSize = 1; + int pageSize = 10; Pageable pgbl = PageRequest.of(pageNumber, pageSize); Page<DataResource> queryDataResources; do { @@ -430,18 +470,21 @@ private void migrateMetadataDocumentsToDataciteVersion2(DataResource metadataDoc for (int versionNo = 1; versionNo <= version; versionNo++) { saveMetadata(id, versionNo, format); } + LOG.info("Migration for metadata document with ID: '{}', finished! No of versions: '{}'", id, version); } /** * Migrate metadata of metadata document from version 1 to version 2 and store * new version in the database. + * * @param id ID of the metadata document. * @param version Version of the metadata document. * @param format Format of the metadata document. (XML/JSON) */ private void saveMetadata(String id, long version, String format) { - DataResource currentVersion = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); - DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentVersion); + LOG.trace("Migrate datacite for metadata document with id: '{}' / version: '{}' and format: '{}'", id, version, format); + DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); // Remove type from first title with type 'OTHER' for (Title title : recordByIdAndVersion.getTitles()) { if (title.getTitleType() == Title.TYPE.OTHER) { @@ -449,18 +492,38 @@ private void saveMetadata(String id, long version, String format) { break; } } - // Set resource type to new definition of version 2 ('...'_Schema) + // Set resource type to new definition of version 2 ('...'_Metadata) ResourceType resourceType = recordByIdAndVersion.getResourceType(); resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); resourceType.setValue(format + DataResourceRecordUtil.METADATA_SUFFIX); - + // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' + for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + String replaceFirst = item.getValue().replaceFirst("/api/v1/schemas/", "/api/v2/schemas/"); + item.setValue(replaceFirst); + } + } + // Add provenance + if (version > 1) { + String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, version - 1l, null, null)).toString(); + RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); + recordByIdAndVersion.getRelatedIdentifiers().add(provenance); + LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); + } else { + long currentVersion = metadataConfig.getAuditService().getCurrentVersion(id); + if (currentVersion == 1l) { + // Create an additional version for JaVers if only one version exists. + currentDataResource.setPublisher("migrationTool"); + metadataConfig.getAuditService().captureAuditInformation(currentDataResource, "Just4fun"); + } + } // Save migrated version - LOG.trace("Persisting created resource."); - dataResourceDao.save(recordByIdAndVersion); + LOG.trace("Persisting created metadata document resource."); + DataResource migratedDataResource = dataResourceDao.save(recordByIdAndVersion); //capture state change LOG.trace("Capturing audit information."); - metadataConfig.getAuditService().captureAuditInformation(recordByIdAndVersion, "migration2version2"); - + metadataConfig.getAuditService().captureAuditInformation(migratedDataResource, "migration2version2"); } } From 73678f48f060a64f732d3ae09b7908ec4ed284ee Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 23 Oct 2024 15:15:52 +0200 Subject: [PATCH 118/181] Migrate reindex to new database structure. --- .../runner/ElasticIndexerRunner.java | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 36f6e4bf..0fccfbae 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -31,7 +31,9 @@ import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; +import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; @@ -201,18 +203,35 @@ public void run(String... args) throws Exception { * @throws InterruptedException Something went wrong. */ private void updateElasticsearchIndex() throws InterruptedException { + int entriesPerPage = 20; + int page = 0; LOG.info("Start ElasticIndexer Runner for indices '{}' and update date '{}'", indices, updateDate); LOG.info("No of schemas: '{}'", schemaRecordDao.count()); // Try to determine URL of repository - List<SchemaRecord> findAllSchemas = schemaRecordDao.findAll(PageRequest.of(0, 3)).getContent(); - if (!findAllSchemas.isEmpty()) { - // There is at least one schema. - // Try to fetch baseURL from this - SchemaRecord get = findAllSchemas.get(0); - Url2Path findByPath = url2PathDao.findByPath(get.getSchemaDocumentUri()).get(0); - - determineIndices(indices); + ResourceType resourceType = ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); + // Add authentication if enabled + if (updateDate != null) { + spec = spec.and(LastUpdateSpecification.toSpecification(updateDate.toInstant(), null)); + } + // Hide revoked and gone data resources. + DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; + List<DataResource.State> stateList = Arrays.asList(states); + spec = spec.and(StateSpecification.toSpecification(stateList)); + + LOG.debug("Performing query for records."); + Pageable pgbl = PageRequest.of(page, entriesPerPage); + Page<DataResource> records = DataResourceRecordUtil.queryDataResources(spec, pgbl); + int noOfEntries = records.getNumberOfElements(); +int noOfPages = records.getTotalPages(); + LOG.debug( "Find '{}' schemas!", noOfEntries); + // add also the schema registered in the schema registry + for (page = 0; page < noOfPages; page++) { + for (DataResource schema : records.getContent()) { + indices.add(schema.getId()); + } + } for (String index : indices) { LOG.info("Reindex '{}'", index); List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaIdAndLastUpdateAfter(index, updateDate.toInstant()); @@ -227,7 +246,6 @@ private void updateElasticsearchIndex() throws InterruptedException { indexAlternativeSchemaIds(index, baseUrl); } Thread.sleep(5000); - } LOG.trace("Finished ElasticIndexerRunner!"); } From 814a83d5fdc030c6d509a4b1444b34402ab06361 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 23 Oct 2024 16:12:07 +0200 Subject: [PATCH 119/181] First step determining all schemaIds. --- .../runner/ElasticIndexerRunner.java | 88 ++++++++----------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 0fccfbae..58f6d2f7 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -203,49 +203,25 @@ public void run(String... args) throws Exception { * @throws InterruptedException Something went wrong. */ private void updateElasticsearchIndex() throws InterruptedException { - int entriesPerPage = 20; - int page = 0; LOG.info("Start ElasticIndexer Runner for indices '{}' and update date '{}'", indices, updateDate); LOG.info("No of schemas: '{}'", schemaRecordDao.count()); // Try to determine URL of repository - ResourceType resourceType = ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); - Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); - // Add authentication if enabled - if (updateDate != null) { - spec = spec.and(LastUpdateSpecification.toSpecification(updateDate.toInstant(), null)); - } - // Hide revoked and gone data resources. - DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; - List<DataResource.State> stateList = Arrays.asList(states); - spec = spec.and(StateSpecification.toSpecification(stateList)); - - LOG.debug("Performing query for records."); - Pageable pgbl = PageRequest.of(page, entriesPerPage); - Page<DataResource> records = DataResourceRecordUtil.queryDataResources(spec, pgbl); - int noOfEntries = records.getNumberOfElements(); -int noOfPages = records.getTotalPages(); - LOG.debug( "Find '{}' schemas!", noOfEntries); - // add also the schema registered in the schema registry - for (page = 0; page < noOfPages; page++) { - for (DataResource schema : records.getContent()) { - indices.add(schema.getId()); - } - } - for (String index : indices) { - LOG.info("Reindex '{}'", index); - List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaIdAndLastUpdateAfter(index, updateDate.toInstant()); - LOG.trace("Search for documents for schema '{}' and update date '{}'", index, updateDate); - LOG.trace("No of documents: '{}'", findBySchemaId.size()); - for (DataRecord item : findBySchemaId) { - MetadataRecord result = toMetadataRecord(item, baseUrl); - LOG.trace("Sending CREATE event."); - messagingService.orElse(new LogfileMessagingService()). - send(MetadataResourceMessage.factoryCreateMetadataMessage(result, this.getClass().toString(), ControllerUtils.getLocalHostname())); - } - indexAlternativeSchemaIds(index, baseUrl); + determineIndices(indices); + for (String index : indices) { + LOG.info("Reindex '{}'", index); + List<DataRecord> findBySchemaId = dataRecordDao.findBySchemaIdAndLastUpdateAfter(index, updateDate.toInstant()); + LOG.trace("Search for documents for schema '{}' and update date '{}'", index, updateDate); + LOG.trace("No of documents: '{}'", findBySchemaId.size()); + for (DataRecord item : findBySchemaId) { + MetadataRecord result = toMetadataRecord(item, baseUrl); + LOG.trace("Sending CREATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryCreateMetadataMessage(result, this.getClass().toString(), ControllerUtils.getLocalHostname())); } - Thread.sleep(5000); + indexAlternativeSchemaIds(index, baseUrl); + } + Thread.sleep(5000); LOG.trace("Finished ElasticIndexerRunner!"); } @@ -259,20 +235,34 @@ private void updateElasticsearchIndex() throws InterruptedException { private void determineIndices(Set<String> indices) { if (indices.isEmpty()) { LOG.info("Reindex all indices!"); - long noOfEntries = url2PathDao.count(); - long entriesPerPage = 50; - long page = 0; + // Search for all indices... + // Build Specification + ResourceType resourceType = ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); + Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); + // Add authentication if enabled + if (updateDate != null) { + spec = spec.and(LastUpdateSpecification.toSpecification(updateDate.toInstant(), null)); + } + // Hide revoked and gone data resources. + DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; + List<DataResource.State> stateList = Arrays.asList(states); + spec = spec.and(StateSpecification.toSpecification(stateList)); + int entriesPerPage = 20; + int page = 0; + LOG.debug("Performing query for records."); + Pageable pgbl = PageRequest.of(page, entriesPerPage); + Page<DataResource> records = DataResourceRecordUtil.queryDataResources(spec, pgbl); + int noOfEntries = records.getNumberOfElements(); + int noOfPages = records.getTotalPages(); + + LOG.debug("Find '{}' schemas!", noOfEntries); // add also the schema registered in the schema registry - do { - List<SchemaRecord> allSchemas = schemaRecordDao.findAll(PageRequest.of((int) page, (int) entriesPerPage)).getContent(); - LOG.trace("Add '{}' schemas of '{}'", allSchemas.size(), noOfEntries); - for (SchemaRecord item : allSchemas) { - indices.add(item.getSchemaIdWithoutVersion()); + for (page = 0; page < noOfPages; page++) { + for (DataResource schema : records.getContent()) { + indices.add(schema.getId()); } - page++; - } while (page * entriesPerPage < noOfEntries); + } } - } private void indexAlternativeSchemaIds(String index, String baseUrl) { From 8e9afc95f3e54a9ecdb6c3969938b4144396c78d Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 23 Oct 2024 16:13:18 +0200 Subject: [PATCH 120/181] Mark untested method for filtering DataResources. --- .../metastore2/util/DataResourceRecordUtil.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index f6c32754..1a5ef8fb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -126,6 +126,8 @@ public class DataResourceRecordUtil { public static final String XML_METADATA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.XML + METADATA_SUFFIX; public static final String JSON_METADATA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.JSON + METADATA_SUFFIX; + private static String baseUrl; + DataResourceRecordUtil() { //Utility class } @@ -920,6 +922,7 @@ public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration meta * @return Specification with schemaIds added. */ public static Specification<DataResource> findBySchemaId(Specification<DataResource> specification, List<String> schemaIds) { + ToDo.... Specification<DataResource> specWithSchema = specification; if (schemaIds != null) { List<String> allSchemaIds = new ArrayList<>(); @@ -2073,7 +2076,11 @@ public static final String getSchemaDocumentUri(String schemaId, Long version) { * @return URI for accessing schema document. */ public static final URI getMetadataDocumentUri(String id, String version) { - return WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, Long.parseLong(version), null, null)).toUri(); + URI toUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, Long.valueOf(version), null, null)).toUri(); + if (toUri.getScheme() == null) { + toUri = URI.create(baseUrl + toUri.toString()); + } + return toUri; } /** @@ -2110,4 +2117,11 @@ public static Page<DataResource> queryDataResources(Specification spec, Pageable } return records; } + + /** + * @param aBaseUrl the baseUrl to set + */ + public static void setBaseUrl(String aBaseUrl) { + baseUrl = aBaseUrl; + } } From 4a14b5eb7e23f2b855bcf9d670766b62f622e9a1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:01:12 +0000 Subject: [PATCH 121/181] Update plugin org.springframework.boot to v3.3.5 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09cbac3e..5329fdc4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.3.4' + id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.10.2' id 'io.freefair.maven-publish-java' version '8.10.2' From 8ab70a7e896c3f7534f0e2cec38fb59cb291cec3 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 25 Oct 2024 14:22:14 +0200 Subject: [PATCH 122/181] The search index is now converted at the same time. --- .../runner/ElasticIndexerRunner.java | 193 +++++++---------- .../metastore2/runner/Migration2V2Runner.java | 198 ++++++++++++++++++ .../util/DataResourceRecordUtil.java | 2 +- 3 files changed, 272 insertions(+), 121 deletions(-) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 58f6d2f7..7d5cf245 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -17,6 +17,8 @@ import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; +import edu.kit.datamanager.clients.SimpleServiceClient; +import edu.kit.datamanager.configuration.SearchConfiguration; import edu.kit.datamanager.entities.RepoServiceRole; import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; @@ -37,11 +39,10 @@ import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.repo.domain.Title; -import edu.kit.datamanager.repo.util.DataResourceUtils; import edu.kit.datamanager.security.filter.JwtAuthenticationToken; import edu.kit.datamanager.service.IMessagingService; import edu.kit.datamanager.service.impl.LogfileMessagingService; +import edu.kit.datamanager.util.AuthenticationHelper; import edu.kit.datamanager.util.ControllerUtils; import edu.kit.datamanager.util.JwtBuilder; import org.slf4j.Logger; @@ -53,12 +54,15 @@ import org.springframework.stereotype.Component; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.javers.core.Javers; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.MediaType; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.HttpClientErrorException; /** * This class contains 2 runners: @@ -68,7 +72,6 @@ * <li>Runner for migrating dataresources from version 1 to version2. */ @Component -@Transactional public class ElasticIndexerRunner implements CommandLineRunner { /** @@ -154,6 +157,11 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Autowired private Optional<IMessagingService> messagingService; + @Autowired + private Migration2V2Runner migrationTool; + + @Autowired + private SearchConfiguration searchConfiguration; /** * Start runner for actions before starting service. @@ -168,7 +176,9 @@ public void run(String... args) throws Exception { .addObject(this) .build(); try { + LOG.trace("Parse arguments: '{}'", args); argueParser.parse(args); + LOG.trace("Find all schemas..."); List<SchemaRecord> findAllSchemas = schemaRecordDao.findAll(PageRequest.of(0, 1)).getContent(); if (!findAllSchemas.isEmpty()) { // There is at least one schema. @@ -177,6 +187,8 @@ public void run(String... args) throws Exception { Url2Path findByPath = url2PathDao.findByPath(get.getSchemaDocumentUri()).get(0); baseUrl = findByPath.getUrl().split("/api/v1/schema")[0]; LOG.trace("Found baseUrl: '{}'", baseUrl); + migrationTool.setBaseUrl(baseUrl); + DataResourceRecordUtil.setBaseUrl(baseUrl); } if (updateIndex) { if (updateDate == null) { @@ -364,10 +376,48 @@ private void migrateAllSchemasToDataciteVersion2() { queryDataResources = queryDataResources(spec, pgbl); for (DataResource schema : queryDataResources.getContent()) { migrateSchemaToDataciteVersion2(schema); + removeAllIndexedEntries(schema.getId()); } } while (queryDataResources.getTotalPages() > 1); } + /** + * Remove all indexed entries for given schema. (If search is enabled) + * + * @param schemaId schema + */ + private void removeAllIndexedEntries(String schemaId) { + // Delete all entries in elastic (if available) + // POST /my-index-000001/_delete_by_query + //{ + // "query": { + // "match": { + // "user.id": "elkbee" + // } + // } + LOG.trace("Remove all indexed entries..."); + if (searchConfiguration.isSearchEnabled()) { + String prefix4Indices = searchConfiguration.getIndex(); + Pattern pattern = Pattern.compile("(.*?)(\\*.*)"); + Matcher matcher = pattern.matcher(prefix4Indices); + if (matcher.find()) { + prefix4Indices = matcher.group(1); + } + + LOG.trace(searchConfiguration.toString()); + LOG.trace("Remove all entries for index: '{}'", prefix4Indices + schemaId); + SimpleServiceClient client = SimpleServiceClient.create(searchConfiguration.getUrl() + "/" + prefix4Indices + schemaId + "/_delete_by_query"); + String query = "{ \"query\": { \"range\" : { \"metadataRecord.schemaVersion\" : { \"gte\" : 1} } } }"; + client.withContentType(MediaType.APPLICATION_JSON); + try { + String postResource = client.postResource(query, String.class); + LOG.trace(postResource); + } catch (HttpClientErrorException hcee) { + LOG.error(hcee.getMessage()); + } + } + } + /** * Migrate dataresources of schemas using version 1 to version 2. * @@ -378,64 +428,11 @@ private void migrateSchemaToDataciteVersion2(DataResource schema) { String id = schema.getId(); // Migrate all versions of schema. for (long versionNo = 1; versionNo <= version; versionNo++) { - saveSchema(id, versionNo); + migrationTool.saveSchema(id, versionNo); } LOG.info("Migration for schema document with ID: '{}', finished! No of versions: '{}'", id, version); } - /** - * Migrate metadata of schema document from version 1 to version 2 and store - * new version in the database. - * - * @param id ID of the schema document. - * @param version Version of the schema document. - * @param format Format of the schema document. (XML/JSON) - */ - private void saveSchema(String id, long version) { - LOG.info("Migrate datacite for schema document with id: '{}' / version: '{}'", id, version); - DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); - DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); - // Remove type from first title with type 'OTHER' - for (Title title : recordByIdAndVersion.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER) { - title.setTitleType(null); - break; - } - } - // Set resource type to new definition of version 2 ('...'_Schema) - ResourceType resourceType = recordByIdAndVersion.getResourceType(); - resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); - resourceType.setValue(recordByIdAndVersion.getFormats().iterator().next() + DataResourceRecordUtil.SCHEMA_SUFFIX); - // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' - for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { - item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); - } - } - // Add provenance - if (version > 1) { - String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(id, version - 1l, null, null)).toString(); - RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); - recordByIdAndVersion.getRelatedIdentifiers().add(provenance); - LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); - } else { - long currentVersion = metadataConfig.getAuditService().getCurrentVersion(id); - if (currentVersion == 1l) { - // Create an additional version for JaVers if only one version exists. - currentDataResource.setPublisher("migrationTool"); - metadataConfig.getAuditService().captureAuditInformation(currentDataResource, "Just4fun"); - } - } - // Save migrated version - LOG.trace("Persisting created schema document resource."); - DataResource migratedDataResource = dataResourceDao.save(recordByIdAndVersion); - - //Capture state change - LOG.trace("Capturing audit information."); - schemaConfig.getAuditService().captureAuditInformation(migratedDataResource, "migration2version2"); - - } - /** * Migrate dataresources of metadata documents from version 1 to version 2. */ @@ -464,74 +461,30 @@ private void migrateMetadataDocumentsToDataciteVersion2(DataResource metadataDoc long version = Long.parseLong(metadataDocument.getVersion()); String id = metadataDocument.getId(); + DataResource copy = migrationTool.getCopyOfDataResource(metadataDocument); + // Get resource type of schema.... String format = null; - for (RelatedIdentifier identifier : metadataDocument.getRelatedIdentifiers()) { - if (identifier.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { - String schemaUrl = identifier.getValue(); - Optional<Url2Path> findByUrl = url2PathDao.findByUrl(schemaUrl); - LOG.trace("Found entry for schema: {}", findByUrl.get().toString()); - format = findByUrl.get().getType().toString(); - } + RelatedIdentifier identifier = DataResourceRecordUtil.getRelatedIdentifier(copy, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + if (identifier != null) { + String schemaUrl = identifier.getValue(); + Optional<Url2Path> findByUrl = url2PathDao.findByUrl(schemaUrl); + LOG.trace("Found entry for schema: {}", findByUrl.get().toString()); + format = findByUrl.get().getType().toString(); } // Migrate all versions of data resource. for (int versionNo = 1; versionNo <= version; versionNo++) { - saveMetadata(id, versionNo, format); - } - LOG.info("Migration for metadata document with ID: '{}', finished! No of versions: '{}'", id, version); - } - - /** - * Migrate metadata of metadata document from version 1 to version 2 and store - * new version in the database. - * - * @param id ID of the metadata document. - * @param version Version of the metadata document. - * @param format Format of the metadata document. (XML/JSON) - */ - private void saveMetadata(String id, long version, String format) { - LOG.trace("Migrate datacite for metadata document with id: '{}' / version: '{}' and format: '{}'", id, version, format); - DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); - DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); - // Remove type from first title with type 'OTHER' - for (Title title : recordByIdAndVersion.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER) { - title.setTitleType(null); - break; - } - } - // Set resource type to new definition of version 2 ('...'_Metadata) - ResourceType resourceType = recordByIdAndVersion.getResourceType(); - resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); - resourceType.setValue(format + DataResourceRecordUtil.METADATA_SUFFIX); - // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' - for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { - item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); - String replaceFirst = item.getValue().replaceFirst("/api/v1/schemas/", "/api/v2/schemas/"); - item.setValue(replaceFirst); - } - } - // Add provenance - if (version > 1) { - String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, version - 1l, null, null)).toString(); - RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); - recordByIdAndVersion.getRelatedIdentifiers().add(provenance); - LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); - } else { - long currentVersion = metadataConfig.getAuditService().getCurrentVersion(id); - if (currentVersion == 1l) { - // Create an additional version for JaVers if only one version exists. - currentDataResource.setPublisher("migrationTool"); - metadataConfig.getAuditService().captureAuditInformation(currentDataResource, "Just4fun"); + DataResource saveMetadata = migrationTool.saveMetadata(id, versionNo, format); + if (versionNo == 1) { + LOG.trace("Sending CREATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryCreateMetadataMessage(saveMetadata, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); + } else { + LOG.trace("Sending UPDATE event."); + messagingService.orElse(new LogfileMessagingService()). + send(MetadataResourceMessage.factoryUpdateMetadataMessage(saveMetadata, AuthenticationHelper.getPrincipal(), ControllerUtils.getLocalHostname())); } } - // Save migrated version - LOG.trace("Persisting created metadata document resource."); - DataResource migratedDataResource = dataResourceDao.save(recordByIdAndVersion); - - //capture state change - LOG.trace("Capturing audit information."); - metadataConfig.getAuditService().captureAuditInformation(migratedDataResource, "migration2version2"); + LOG.info("Migration for metadata document with ID: '{}', finished! No of versions: '{}'", id, version); } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java new file mode 100644 index 00000000..05cfae11 --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java @@ -0,0 +1,198 @@ +/* + * Copyright 2022 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.runner; + +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; +import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.repo.domain.Title; +import edu.kit.datamanager.repo.util.DataResourceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.stereotype.Component; + +import java.util.*; +import org.springframework.transaction.annotation.Transactional; + +/** + * This class contains 2 runners: + * <ul><li>Runner for indexing all metadata documents of given schemas Arguments + * have to start with at least 'reindex' followed by all indices which have to + * be reindexed. If no indices are given all indices will be reindexed.</li> + * <li>Runner for migrating dataresources from version 1 to version2. + */ +@Component +@Transactional +public class Migration2V2Runner { + + /** + * Determine the baseUrl of the service. + */ + private String baseUrl; + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(Migration2V2Runner.class); + /** + * DAO for all data resources. + */ + @Autowired + private IDataResourceDao dataResourceDao; + /** + * Instance of schema repository. + */ + @Autowired + private MetastoreConfiguration schemaConfig; + /** + * Instande of metadata reository. + * + */ + @Autowired + private MetastoreConfiguration metadataConfig; + + /** + * Migrate metadata of schema document from version 1 to version 2 and store + * new version in the database. + * + * @param id ID of the schema document. + * @param version Version of the schema document. + * @param format Format of the schema document. (XML/JSON) + */ + public void saveSchema(String id, long version) { + LOG.info("Migrate datacite for schema document with id: '{}' / version: '{}'", id, version); + DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); + DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); + // Remove type from first title with type 'OTHER' + for (Title title : recordByIdAndVersion.getTitles()) { + if (title.getTitleType() == Title.TYPE.OTHER) { + title.setTitleType(null); + break; + } + } + // Set resource type to new definition of version 2 ('...'_Schema) + ResourceType resourceType = recordByIdAndVersion.getResourceType(); + resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); + resourceType.setValue(recordByIdAndVersion.getFormats().iterator().next() + DataResourceRecordUtil.SCHEMA_SUFFIX); + // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' + for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + } + } + // Add provenance + if (version > 1) { + String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(id, version - 1l, null, null)).toString(); + RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); + recordByIdAndVersion.getRelatedIdentifiers().add(provenance); + LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); + } else { + long currentVersion = metadataConfig.getAuditService().getCurrentVersion(id); + if (currentVersion == 1l) { + // Create an additional version for JaVers if only one version exists. + currentDataResource.setPublisher("migrationTool"); + metadataConfig.getAuditService().captureAuditInformation(currentDataResource, "Just4fun"); + } + } + // Save migrated version + LOG.trace("Persisting created schema document resource."); + DataResource migratedDataResource = dataResourceDao.save(recordByIdAndVersion); + + //Capture state change + LOG.trace("Capturing audit information."); + schemaConfig.getAuditService().captureAuditInformation(migratedDataResource, "migration2version2"); + + } + + /** + * Migrate metadata of metadata document from version 1 to version 2 and store + * new version in the database. + * + * @param id ID of the metadata document. + * @param version Version of the metadata document. + * @param format Format of the metadata document. (XML/JSON) + */ + public DataResource saveMetadata(String id, long version, String format) { + LOG.trace("Migrate datacite for metadata document with id: '{}' / version: '{}' and format: '{}'", id, version, format); + DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); + DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); + // Remove type from first title with type 'OTHER' + for (Title title : recordByIdAndVersion.getTitles()) { + if (title.getTitleType() == Title.TYPE.OTHER) { + title.setTitleType(null); + break; + } + } + // Set resource type to new definition of version 2 ('...'_Metadata) + ResourceType resourceType = recordByIdAndVersion.getResourceType(); + resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); + resourceType.setValue(format + DataResourceRecordUtil.METADATA_SUFFIX); + // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' + for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { + if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + String replaceFirst = item.getValue().replaceFirst("/api/v1/schemas/", "/api/v2/schemas/"); + item.setValue(replaceFirst); + } + } + // Add provenance + if (version > 1) { + String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, version - 1l, null, null)).toString(); + RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); + recordByIdAndVersion.getRelatedIdentifiers().add(provenance); + LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); + } else { + long currentVersion = metadataConfig.getAuditService().getCurrentVersion(id); + if (currentVersion == 1l) { + // Create an additional version for JaVers if only one version exists. + currentDataResource.setPublisher("migrationTool"); + metadataConfig.getAuditService().captureAuditInformation(currentDataResource, "Just4fun"); + } + } + // Save migrated version + LOG.trace("Persisting created metadata document resource."); + DataResource migratedDataResource = dataResourceDao.save(recordByIdAndVersion); + + //capture state change + LOG.trace("Capturing audit information."); + metadataConfig.getAuditService().captureAuditInformation(migratedDataResource, "migration2version2"); + + return migratedDataResource; + } + + public DataResource getCopyOfDataResource(DataResource metadataDocument) { + DataResource copy = null; + Optional<DataResource> dataResource = dataResourceDao.findById(metadataDocument.getId()); + if (dataResource.isPresent()) { + copy = DataResourceUtils.copyDataResource(dataResource.get()); + } + return copy; + } + + /** + * @param baseUrl the baseUrl to set + */ + public void setBaseUrl(String baseUrl) { + LOG.trace("Set baseURL from '{}' to '{}'", this.baseUrl, baseUrl); + this.baseUrl = baseUrl; + } +} diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 1a5ef8fb..45f1ad39 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -922,7 +922,7 @@ public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration meta * @return Specification with schemaIds added. */ public static Specification<DataResource> findBySchemaId(Specification<DataResource> specification, List<String> schemaIds) { - ToDo.... +// ToDo.... Specification<DataResource> specWithSchema = specification; if (schemaIds != null) { List<String> allSchemaIds = new ArrayList<>(); From 6c7dce7bce48c657db068e6e4490c9262b68f0e8 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 25 Oct 2024 15:31:41 +0200 Subject: [PATCH 123/181] Start test filtering lists of schema/metadata documents API v2. --- .../test/MetadataControllerFilterTestV2.java | 571 ++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java new file mode 100644 index 00000000..30834b7a --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java @@ -0,0 +1,571 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package edu.kit.datamanager.metastore2.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; +import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.entities.repo.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * + * @author Torridity + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_filter;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/jsonfilter/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/jsonfilter/metadata"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class MetadataControllerFilterTestV2 { + + private final static String TEMP_DIR_4_ALL = "/tmp/metastore2/jsonfilter/"; + private final static String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private final static String TEMP_DIR_4_METADATA = TEMP_DIR_4_ALL + "metadata/"; + private final static String JSON_SCHEMA = "{\n" + + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n" + + " \"$id\": \"http://www.example.org/schema/json\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"Json schema for tests\",\n" + + " \"default\": {},\n" + + " \"required\": [\n" + + " \"title\",\n" + + " \"date\"\n" + + " ],\n" + + " \"properties\": {\n" + + " \"title\": {\n" + + " \"type\": \"string\",\n" + + " \"title\": \"Title\",\n" + + " \"description\": \"Title of object.\"\n" + + " },\n" + + " \"date\": {\n" + + " \"type\": \"string\",\n" + + " \"format\": \"date\",\n" + + " \"title\": \"Date\",\n" + + " \"description\": \"Date of object\"\n" + + " }\n" + + " },\n" + + " \"additionalProperties\": false\n" + + "}"; + private final static String JSON_DOCUMENT = "{\"title\":\"any string\",\"date\": \"2020-10-16\"}"; + private final static String XML_SCHEMA = "<xs:schema targetNamespace=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns=\"http://www.example.org/schema/xsd/\"\n" + + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" + + " elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n" + + " <xs:element name=\"metadata\">\n" + + " <xs:complexType>\n" + + " <xs:sequence>\n" + + " <xs:element name=\"title\" type=\"xs:string\"/>\n" + + " <xs:element name=\"date\" type=\"xs:date\"/>\n" + + " </xs:sequence>\n" + + " </xs:complexType>\n" + + " </xs:element>\n" + + " </xs:schema>"; + + private final static String XML_DOCUMENT = "<?xml version='1.0' encoding='utf-8'?>\n" + + "<ex:metadata xmlns:ex=\"http://www.example.org/schema/xsd/\">\n" + + " <ex:title>Title of second version</ex:title>\n" + + " <ex:date>2021-06-15</ex:date>\n" + + "</ex:metadata>"; + public static boolean initialize = true; + public final static int MAX_NO_OF_SCHEMAS = 4; + public final static int NO_OF_DOCUMENTS_PER_TYPE = ((MAX_NO_OF_SCHEMAS + 1) * MAX_NO_OF_SCHEMAS) / 2; + private static final String JSON_SCHEMA_ID = "json_schema_"; + private static final String XML_SCHEMA_ID = "xml_schema_"; + private static final String RELATED_RESOURCE = "resource_"; + private static final String INVALID_MIMETYPE = "application/invalid"; + + private static final String API_BASE_PATH = "/api/v2"; + private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; + private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; + private static final String API_METADATA_PATH = API_BASE_PATH + "/metadata/"; + + private MockMvc mockMvc; + @Autowired + private WebApplicationContext context; + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + @Autowired + private MetastoreConfiguration schemaConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + @Before + public void setUp() throws Exception { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation)) + .build(); + // preparation will be done only once. + prepareRepo(); + } + + @Test + public void testFindSchemaRecordsBySchemaId() throws Exception { + ObjectMapper map = new ObjectMapper(); + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + String schemaId = JSON_SCHEMA_ID + i; + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH) + .param("schemaId", schemaId) + .header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for schema '" + i + "'", 1, result.length); + Assert.assertEquals("SchemaID '" + schemaId + "'", schemaId, result[0].getSchemaId()); + } + } + + @Test + public void testFindRecordsBySchemaId() throws Exception { + ObjectMapper map = new ObjectMapper(); + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + String schemaId = JSON_SCHEMA_ID + i; + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) + .param("schemaId", schemaId) + .header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for schema '" + i + "'", i, result.length); + for (DataResource item : result) { + RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(item); + Assert.assertEquals(RelatedIdentifier.RELATED_IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = item.getSchema().getIdentifier(); + Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); + Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); + Assert.assertTrue(schemaUrl.contains(schemaId)); + } + } + } + + @Test + public void testFindRecordsByMultipleSchemaIds() throws Exception { + ObjectMapper map = new ObjectMapper(); + int noOfResults; + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + MockHttpServletRequestBuilder get = get(API_METADATA_PATH); + noOfResults = 0; + for (int j = 1; j <= i; j++) { + noOfResults += j; + String schemaId = JSON_SCHEMA_ID + j; + get.param("schemaId", schemaId); + } + get.header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE); + MvcResult res = this.mockMvc + .perform(get) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for schema '1 - " + i + "'", noOfResults, result.length); + } + } + + @Test + public void testFindRecordsByMultipleSchemaIdsPlusInvalidSchemaId() throws Exception { + ObjectMapper map = new ObjectMapper(); + int noOfResults; + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + MockHttpServletRequestBuilder get = get(API_METADATA_PATH); + noOfResults = 0; + for (int j = 1; j <= i; j++) { + noOfResults += j; + String schemaId = JSON_SCHEMA_ID + j; + get.param("schemaId", schemaId); + } + get.param("schemaId", "unknownSchemaId"); + get.header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE); + MvcResult res = this.mockMvc + .perform(get) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for schema '1 - " + i + "'", noOfResults, result.length); + } + } + + @Test + public void testFindSchemaRecordsByInvalidMimeType() throws Exception { + String mimeType = INVALID_MIMETYPE; + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH) + .param("mimeType", mimeType)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindSchemaRecordsByMimeType() throws Exception { + String mimeType = MediaType.APPLICATION_JSON.toString(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH) + .param("mimeType", mimeType)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(MAX_NO_OF_SCHEMAS, result.length); + for (DataResource item : result) { + Assert.assertEquals(mimeType, item.getMimeType()); + } + mimeType = MediaType.APPLICATION_XML.toString(); + res = this.mockMvc.perform(get(API_SCHEMA_PATH) + .param("mimeType", mimeType)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(MAX_NO_OF_SCHEMAS, result.length); + for (DataResource item : result) { + Assert.assertEquals(mimeType, item.getMimeType()); + } + } + + @Test + public void testFindSchemaRecordsByMultipleMimeTypes() throws Exception { + String mimeType1 = MediaType.APPLICATION_JSON.toString(); + String mimeType2 = MediaType.APPLICATION_XML.toString(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH) + .param("mimeType", mimeType1) + .param("mimeType", mimeType2)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(MAX_NO_OF_SCHEMAS * 2, result.length); + } + + @Test + public void testFindSchemaRecordsByMultipleMimeTypesIncludingInvalidMimeType() throws Exception { + String mimeType1 = MediaType.APPLICATION_JSON.toString(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH) + .param("mimeType", mimeType1) + .param("mimeType", INVALID_MIMETYPE)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(MAX_NO_OF_SCHEMAS, result.length); + } + + @Test + public void testFindRecordsByResourceId() throws Exception { + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + ResourceIdentifier relatedResource = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE + i); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) + .param("resourceId", relatedResource.getIdentifier())) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals((MAX_NO_OF_SCHEMAS - i + 1) * 2, result.length); + for (DataResource item : result) { + Assert.assertEquals(relatedResource, item.getRelatedResource()); + } + } + } + + @Test + public void testFindRecordsByMultipleResourceIds() throws Exception { + ObjectMapper map = new ObjectMapper(); + int noOfResults; + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + MockHttpServletRequestBuilder get = get(API_METADATA_PATH); + noOfResults = 0; + for (int j = 1; j <= i; j++) { + noOfResults += (MAX_NO_OF_SCHEMAS - j + 1) * 2; + String relatedResource = RELATED_RESOURCE + j; + get.param("resourceId", relatedResource); + } + get.param("size", Integer.toString(noOfResults * 2)); + get.header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE); + MvcResult res = this.mockMvc + .perform(get) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for schema '1 - " + i + "'", noOfResults, result.length); + } + } + + @Test + public void testFindRecordsByInvalidResourceId() throws Exception { + + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) + .param("resourceId", "invalid")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals(0, result.length); + } + + @Test + public void testFindRecordsByMultipleResourceIdsIncludingInvalidResourceId() throws Exception { + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + ResourceIdentifier relatedResource = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE + i); + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) + .param("resourceId", relatedResource.getIdentifier()) + .param("resourceId", INVALID_MIMETYPE)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + ObjectMapper map = new ObjectMapper(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals((MAX_NO_OF_SCHEMAS - i + 1) * 2, result.length); + for (DataResource item : result) { + Assert.assertEquals(relatedResource, item.getRelatedResource()); + } + } + } + + @Test + public void testFindRecordsByUnknownSchemaId() throws Exception { + ObjectMapper map = new ObjectMapper(); + String schemaId = "UnknownSchemaId"; + MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) + .param("schemaId", schemaId) + .header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for schema '" + schemaId + "'", 0, result.length); + } + + public void registerSchemaDocument(DataResource.SCHEMA_TYPE schemaType, String schemaId) throws Exception { + DataResource record = new DataResource(); + record.setSchemaId(schemaId); + record.setType(schemaType); + Set<AclEntry> aclEntries = new HashSet<>(); + aclEntries.add(new AclEntry("test", PERMISSION.READ)); + aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); + record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile schemaFile; + switch (schemaType) { + case JSON: + record.setMimeType(MediaType.APPLICATION_JSON.toString()); + schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", JSON_SCHEMA.getBytes()); + break; + case XML: + record.setMimeType(MediaType.APPLICATION_XML.toString()); + schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", XML_SCHEMA.getBytes()); + break; + default: + throw new Exception("Unknown schema type!"); + } + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + /** + * Ingest metadata document for given schema and related resource. + * + * @param schemaId schema + * @param resource related resource + * @throws Exception + */ + public void ingestMetadataDocument(String schemaId, String resource) throws Exception { + DataResource record = new DataResource(); + record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(schemaId)); + record.setRelatedResource(ResourceIdentifier.factoryInternalResourceIdentifier(resource)); + Set<AclEntry> aclEntries = new HashSet<>(); +// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); +// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); +// record.setAcl(aclEntries); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile metadataFile; + if (schemaId.startsWith(JSON_SCHEMA_ID)) { + metadataFile = new MockMultipartFile("document", "metadata.json", "application/json", JSON_DOCUMENT.getBytes()); + } else { + metadataFile = new MockMultipartFile("document", "metadata.xml", "application/xml", XML_DOCUMENT.getBytes()); + } + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). + file(recordFile). + file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andExpect(redirectedUrlPattern("http://*:*/**/*?version=1")).andReturn(); + } + + /** + * Prepare repository with schemas and metadata documents. + * + * @throws Exception + */ + private void prepareRepo() throws Exception { + if (initialize) { + initialize = false; + prepareEnvironment(); + prepareSchemas(); + prepareMetadataDocuments(); + } + } + + /** + * Prepare filesystem (remove old files) + */ + private void prepareEnvironment() { + System.out.println("------JsonSchemaRegistryControllerTest----------------"); + System.out.println("------" + this.schemaConfig); + System.out.println("------------------------------------------------------"); + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + schemaRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + try { + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_METADATA)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_METADATA).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /** + * Register MAX_NO_OF_SCHEMAS schemas for json and xml + * + * @throws Exception + */ + private void prepareSchemas() throws Exception { + // Prepare 5 different schemas + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + registerSchemaDocument(DataResource.SCHEMA_TYPE.JSON, JSON_SCHEMA_ID + i); + registerSchemaDocument(DataResource.SCHEMA_TYPE.XML, XML_SCHEMA_ID + i); + } + + } + + /** + * For first schema (xml and json) add one metadata document For second schema + * add two metadata documents For ... + * + * @throws Exception + */ + private void prepareMetadataDocuments() throws Exception { + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + String schemaId = JSON_SCHEMA_ID + i; + for (int j = 1; j <= i; j++) { + String resource = RELATED_RESOURCE + j; + ingestMetadataDocument(schemaId, resource); + } + schemaId = XML_SCHEMA_ID + i; + for (int j = 1; j <= i; j++) { + String resource = RELATED_RESOURCE + j; + ingestMetadataDocument(schemaId, resource); + } + } + + } +} From 8008947299b37355b57ffca9ba9322ebcb88d898 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:32:32 +0000 Subject: [PATCH 124/181] Update dependency com.google.errorprone:error_prone_core to v2.35.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09cbac3e..44ae4d54 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ ext { springDocVersion = '2.6.0' javersVersion = '7.6.3' keycloakVersion = '19.0.0' - errorproneVersion = '2.33.0' + errorproneVersion = '2.35.1' // directory for generated code snippets during tests snippetsDir = file("build/generated-snippets") } From 217ef8d1f6869b56f427ad860cf950ff22aa3c2a Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 28 Oct 2024 16:07:40 +0100 Subject: [PATCH 125/181] Clean up code and fix some tests. --- .../util/DataResourceRecordUtil.java | 690 +++--------------- .../metastore2/util/MetadataRecordUtil.java | 117 +-- .../util/MetadataSchemaRecordUtil.java | 119 +-- .../web/impl/MetadataControllerImplV2.java | 52 +- .../test/MetadataControllerFilterTestV2.java | 73 +- .../test/MetadataControllerTestV2.java | 2 +- 6 files changed, 169 insertions(+), 884 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 45f1ad39..dc4c870e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -16,7 +16,6 @@ package edu.kit.datamanager.metastore2.util; import com.fasterxml.jackson.core.JsonParseException; -import edu.kit.datamanager.clients.SimpleServiceClient; import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; @@ -25,7 +24,6 @@ import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; @@ -55,14 +53,10 @@ import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestClientException; import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.util.UriComponentsBuilder; import java.io.*; import java.net.URI; -import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -116,8 +110,6 @@ public class DataResourceRecordUtil { private static ISchemaRecordDao schemaRecordDao; private static IMetadataFormatDao metadataFormatDao; - private static IUrl2PathDao url2PathDao; - public static final String SCHEMA_SUFFIX = "_Schema"; public static final String XML_SCHEMA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.XML + SCHEMA_SUFFIX; public static final String JSON_SCHEMA_TYPE = MetadataSchemaRecord.SCHEMA_TYPE.JSON + SCHEMA_SUFFIX; @@ -132,6 +124,58 @@ public class DataResourceRecordUtil { //Utility class } + /** + * Checks if current user is allowed to access with given AclEntries. + * + * @param aclEntries AclEntries of resource. + * @param currentAcl Check current ACL (true) or new one (false). + * + * @return Allowed (true) or not. + */ + public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean currentAcl) { + boolean isAllowed = false; + String errorMessage1 = "Error invalid ACL! Reason: Only ADMINISTRATORS are allowed to change ACL entries."; + String errorMessage2 = "Error invalid ACL! Reason: You are not allowed to revoke your own administrator rights."; + Authentication authentication = AuthenticationHelper.getAuthentication(); + List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); + for (GrantedAuthority authority : authentication.getAuthorities()) { + authorizationIdentities.add(authority.getAuthority()); + } + if (authorizationIdentities.contains(RepoUserRole.ADMINISTRATOR.getValue())) { + //ROLE_ADMINISTRATOR detected -> no further permission check necessary. + return true; + } + if (LOG.isTraceEnabled()) { + LOG.trace("Check access rights for changing ACL list!"); + for (String authority : authorizationIdentities) { + LOG.trace("Indentity/Authority: '{}'", authority); + } + } + // Check if authorized user still has ADMINISTRATOR rights + Iterator<AclEntry> iterator = aclEntries.iterator(); + while (iterator.hasNext()) { + AclEntry aclEntry = iterator.next(); + LOG.trace("'{}' has \u2019{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); + if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) && authorizationIdentities.contains(aclEntry.getSid())) { + isAllowed = true; + LOG.trace("Confirm permission for updating ACL: '{}' has \u2019{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); + break; + } + } + if (!isAllowed) { + String errorMessage = currentAcl ? errorMessage1 : errorMessage2; + LOG.warn(errorMessage); + if (schemaConfig.isAuthEnabled()) { + if (currentAcl) { + throw new AccessForbiddenException(errorMessage1); + } else { + throw new BadArgumentException(errorMessage2); + } + } + } + return isAllowed; + } + /** * Create/Ingest an instance of MetadataSchemaRecord. * @@ -215,113 +259,31 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio public static DataResource createDataResourceRecord4Metadata(MetastoreConfiguration applicationProperties, MultipartFile recordDocument, MultipartFile document) { - DataResource metadataRecord; + DataResource dataResource; // Do some checks first. - metadataRecord = checkParameters(recordDocument, document, true); - Objects.requireNonNull(metadataRecord); - if (metadataRecord.getId() != null) { + dataResource = checkParameters(recordDocument, document, true); + Objects.requireNonNull(dataResource); + if (dataResource.getId() != null) { // Optional id set. Check for valid ID - check4validId(metadataRecord, true); + check4validId(dataResource, true); } // End of parameter checks + // Fix internal references, of necessary + dataResource = fixRelatedSchemaIfNeeded(dataResource); // validate schema document / determine or correct resource type - validateMetadataDocument(applicationProperties, metadataRecord, document); + validateMetadataDocument(applicationProperties, dataResource, document); - metadataRecord.setVersion(getSchemaRecordFromDataResource(metadataRecord).getVersion().toString()); + dataResource.setVersion(getSchemaRecordFromDataResource(dataResource).getVersion().toString()); // create record. - DataResource dataResource = metadataRecord; DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); // store document ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); - metadataRecord = DataResourceRecordUtil.getRecordByIdAndVersion(applicationProperties, metadataRecord.getId(), Long.valueOf(metadataRecord.getVersion())); + dataResource = DataResourceRecordUtil.getRecordByIdAndVersion(applicationProperties, dataResource.getId(), Long.valueOf(dataResource.getVersion())); - return metadataRecord; + return dataResource; } -// /** -// * Create a digital object from metadata record and metadata document. -// * -// * @param applicationProperties Configuration properties. -// * @param recordDocument Metadata record. -// * @param document Metadata document. -// * @return Enriched metadata record. -// */ -// public static MetadataRecord createDataResource4MetadataDocument(MetastoreConfiguration applicationProperties, -// MultipartFile recordDocument, MultipartFile document) { -// DataResource metadataRecord; -// long nano1 = System.nanoTime() / 1000000; -// // Do some checks first. -// metadataRecord = checkParameters(recordDocument, document, true); -// -// if (metadataRecord.getRelatedResource() == null || metadataRecord.getRelatedResource().getIdentifier() == null || metadataRecord.getSchema() == null || metadataRecord.getSchema().getIdentifier() == null) { -// String message = "Mandatory attributes relatedResource and/or schema not found in record. Returning HTTP BAD_REQUEST."; -// LOG.error(message); -// throw new BadArgumentException(message); -// } -// // Test for schema version -// if (metadataRecord.getSchemaVersion() == null) { -// MetadataSchemaRecord currentSchemaRecord; -// try { -// currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(applicationProperties, metadataRecord.getSchema()); -// } catch (ResourceNotFoundException rnfe) { -// throw new UnprocessableEntityException("Unknown schema ID '" + metadataRecord.getSchema().getIdentifier() + "'!"); -// } -// metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); -// } -// -// // validate document -// long nano2 = System.nanoTime() / 1000000; -// // validate schema document -// validateMetadataDocument(applicationProperties, metadataRecord, document); -// // set internal parameters -// metadataRecord.setRecordVersion(1l); -// -// long nano3 = System.nanoTime() / 1000000; -// // create record. -// DataResource dataResource = migrateToDataResource(applicationProperties, metadataRecord); -// // add id as internal identifier if exists -// // Note: DataResourceUtils.createResource will ignore id of resource. -// // id will be set to alternate identifier if exists. -// if (dataResource.getId() != null) { -// // check for valid identifier without any chars which may be encoded -// try { -// String originalId = dataResource.getId(); -// String value = URLEncoder.encode(originalId, StandardCharsets.UTF_8.toString()); -// if (!value.equals(originalId)) { -// String message = "Not a valid id! Encoded: " + value; -// LOG.error(message); -// throw new BadArgumentException(message); -// } -// } catch (UnsupportedEncodingException ex) { -// String message = "Error encoding id " + metadataRecord.getSchemaId(); -// LOG.error(message); -// throw new CustomInternalServerError(message); -// } -// -// dataResource.getAlternateIdentifiers().add(Identifier.factoryInternalIdentifier(dataResource.getId())); -// } -// long nano4 = System.nanoTime() / 1000000; -// DataResource createResource = DataResourceUtils.createResource(applicationProperties, dataResource); -// long nano5 = System.nanoTime() / 1000000; -// // store document -// ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, createResource, document, document.getOriginalFilename(), null, true, t -> "somethingStupid"); -// long nano6 = System.nanoTime() / 1000000; -// // Create additional metadata record for faster access -// DataRecord dataRecord = new DataRecord(); -// dataRecord.setMetadataId(createResource.getId()); -// dataRecord.setVersion(metadataRecord.getRecordVersion()); -// dataRecord.setSchemaId(metadataRecord.getSchema().getIdentifier()); -// dataRecord.setSchemaVersion(metadataRecord.getSchemaVersion()); -// dataRecord.setMetadataDocumentUri(contentInformation.getContentUri()); -// dataRecord.setDocumentHash(contentInformation.getHash()); -// dataRecord.setLastUpdate(dataResource.getLastUpdate()); -// saveNewDataRecord(dataRecord); -// long nano7 = System.nanoTime() / 1000000; -// LOG.info("Create Record times, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); -// -// return migrateToMetadataRecord(applicationProperties, createResource, true); -// } /** * Update a digital object with given metadata record and/or metadata * document. @@ -360,6 +322,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur ControllerUtils.checkEtag(eTag, oldDataResource); LOG.trace("ETag: '{}'", oldDataResource.getEtag()); updatedDataResource = mergeDataResource(oldDataResource, givenDataResource); + updatedDataResource = fixRelatedSchemaIfNeeded(updatedDataResource); boolean noChanges = false; if (document != null) { @@ -476,52 +439,6 @@ public static void replaceIsDerivedFrom(DataResource newDataResource) { } } - /** - * Delete schema document. - * - * @param applicationProperties Settings of repository. - * @param id ID of the schema document. - * @param eTag E-Tag of the current schema document. - * @param supplier Method for creating access URL. - */ - public static void deleteMetadataSchemaRecord(MetastoreConfiguration applicationProperties, - String id, - String eTag, - UnaryOperator<String> supplier) { - // Find all versions for given id... - int pageNo = 0; - int pageSize = 10; - int totalNoOfPages; - Set<String> uris = new HashSet<>(); - Pageable pgbl; - Page<DataResource> allVersionsOfResource; - do { - pgbl = PageRequest.of(pageNo, pageSize); - allVersionsOfResource = DataResourceUtils.readAllVersionsOfResource(applicationProperties, id, pgbl); - totalNoOfPages = allVersionsOfResource.getTotalPages(); - for (DataResource item : allVersionsOfResource.getContent()) { - uris.add(SchemaRegistryControllerImplV2.getSchemaDocumentUri(item).toString()); - } - pageNo++; - } while (pageNo < totalNoOfPages); - // Test for linked metadata documents - Specification<DataResource> spec = RelatedIdentifierSpec.toSpecification(uris.toArray(new String[]{})); - Optional<DataResource> findOne = dataResourceDao.findOne(spec); - // No references to this schema available -> Ready for deletion - if (findOne.isEmpty()) { - DataResourceUtils.deleteResource(applicationProperties, id, eTag, supplier); - List<SchemaRecord> listOfSchemaIds = schemaRecordDao.findBySchemaIdStartsWithOrderByVersionDesc(id + SCHEMA_VERSION_SEPARATOR); - for (SchemaRecord item : listOfSchemaIds) { - LOG.trace("Delete entry for path '{}'", item.getSchemaDocumentUri()); - List<Url2Path> findByPath = url2PathDao.findByPath(item.getSchemaDocumentUri()); - for (Url2Path entry : findByPath) { - url2PathDao.delete(entry); - } - } - schemaRecordDao.deleteAll(listOfSchemaIds); - } - } - /** * Delete a digital object with given identifier. * @@ -659,125 +576,6 @@ private static ContentInformation getContentInformationOfResource(RepoBaseConfig return returnValue; } - /** - * Returns schema record with the current version. - * - * @param metastoreProperties Configuration for accessing services - * @param schemaId SchemaID of the schema. - * @return MetadataSchemaRecord ResponseEntity in case of an error. - */ - public static MetadataSchemaRecord getCurrentInternalSchemaRecord(MetastoreConfiguration metastoreProperties, - String schemaId) { - LOG.trace("Get current internal schema record for id '{}'.", schemaId); - MetadataSchemaRecord returnValue = null; - boolean success = false; - StringBuilder errorMessage = new StringBuilder(); - if (metastoreProperties.getSchemaRegistries().size() == 0) { - LOG.trace(LOG_SCHEMA_REGISTRY); - - returnValue = MetadataSchemaRecordUtil.getRecordById(metastoreProperties, schemaId); - success = true; - } else { - for (String schemaRegistry : metastoreProperties.getSchemaRegistries()) { - LOG.trace(LOG_FETCH_SCHEMA, schemaRegistry); - URI schemaRegistryUri = URI.create(schemaRegistry); - UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(schemaRegistryUri.getScheme()).host(schemaRegistryUri.getHost()).port(schemaRegistryUri.getPort()).pathSegment(schemaRegistryUri.getPath(), PATH_SCHEMA, schemaId); - - URI finalUri = builder.build().toUri(); - - try { - returnValue = SimpleServiceClient.create(finalUri.toString()).withBearerToken(guestToken).accept(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).getResource(MetadataSchemaRecord.class - ); - success = true; - break; - } catch (HttpClientErrorException ce) { - String message = "Error accessing schema '" + schemaId + "' at '" + schemaRegistry + "'!"; - LOG.error(message, ce); - errorMessage.append(message).append("\n"); - } catch (RestClientException ex) { - String message = String.format(LOG_ERROR_ACCESS, schemaRegistry); - LOG.error(message, ex); - errorMessage.append(message).append("\n"); - } - } - } - if (!success) { - throw new UnprocessableEntityException(errorMessage.toString()); - } - LOG.trace(LOG_SCHEMA_RECORD, returnValue); - return returnValue; - } - - /** - * Returns schema record with the current version. - * - * @param metastoreProperties Configuration for accessing services - * @param schemaId SchemaID of the schema. - * @param version Version of the schema. - * @return MetadataSchemaRecord ResponseEntity in case of an error. - */ - public static MetadataSchemaRecord getInternalSchemaRecord(MetastoreConfiguration metastoreProperties, - String schemaId, - Long version) { - MetadataSchemaRecord returnValue = null; - boolean success = false; - StringBuilder errorMessage = new StringBuilder(); - LOG.trace("Get internal schema record for id '{}'.", schemaId); - if (metastoreProperties.getSchemaRegistries().size() == 0) { - LOG.trace(LOG_SCHEMA_REGISTRY); - - returnValue = MetadataSchemaRecordUtil.getRecordByIdAndVersion(metastoreProperties, schemaId, version); - success = true; - } else { - for (String schemaRegistry : metastoreProperties.getSchemaRegistries()) { - LOG.trace(LOG_FETCH_SCHEMA, schemaRegistry); - URI schemaRegistryUri = URI.create(schemaRegistry); - UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(schemaRegistryUri.getScheme()).host(schemaRegistryUri.getHost()).port(schemaRegistryUri.getPort()).pathSegment(schemaRegistryUri.getPath(), PATH_SCHEMA, schemaId).queryParam("version", version); - - URI finalUri = builder.build().toUri(); - - try { - returnValue = SimpleServiceClient.create(finalUri.toString()).withBearerToken(guestToken).accept(MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE).getResource(MetadataSchemaRecord.class - ); - success = true; - break; - } catch (HttpClientErrorException ce) { - String message = "Error accessing schema '" + schemaId + "' at '" + schemaRegistry + "'!"; - LOG.error(message, ce); - errorMessage.append(message).append("\n"); - } catch (RestClientException ex) { - String message = String.format(LOG_ERROR_ACCESS, schemaRegistry); - LOG.error(message, ex); - errorMessage.append(message).append("\n"); - } - } - } - if (!success) { - throw new UnprocessableEntityException(errorMessage.toString()); - } - LOG.trace(LOG_SCHEMA_RECORD, returnValue); - return returnValue; - } - - /** - * Update/create related identifier to values given by metadata record. - * - * @param relatedIdentifier related identifier (if null create a new one) - * @param metadataRecord record holding schema information. - * @return updated/created related identifier. - */ - private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentifier relatedIdentifier, MetadataRecord metadataRecord) { - if (relatedIdentifier == null) { - relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, null, null, null); - } - ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); - relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(schemaIdentifier.getIdentifierType().name())); - relatedIdentifier.setValue(schemaIdentifier.getIdentifier()); - LOG.trace("Set relatedId for schema to '{}'", relatedIdentifier); - - return relatedIdentifier; - } - /** * Validate metadata document with given schema. * @@ -955,32 +753,6 @@ public static Specification<DataResource> findByRelatedId(Specification<DataReso return specWithSchema; } - /** - * Merge new metadata record in the existing one. - * - * @param managed Existing metadata record. - * @param provided New metadata record. - * @return Merged record - */ -// public static MetadataRecord mergeDataResources(DataResource managed, DataResource provided) { -// if (provided != null && managed != null) { -// //update pid -// managed.setAlternateIdentifiers(mergeEntry("Update record->pid", managed.getAlternateIdentifiers(), provided.getAlternateIdentifiers())); -// -// //update acl -// managed.setAcls(mergeAcl(managed.getAcls(), provided.getAcls())); -// //update getRelatedResource -// managed.setRelatedIdentifiers(mergeEntry("Updating record->relatedResource", managed.getRelatedIdentifiers(), provided.getRelatedIdentifiers())); -// //update schemaVersion -// managed.setVersion(mergeEntry("Updating record->schemaVersion", managed.getVersion(), provided.getVersion())); -// managed.setRights(mergeEntry("halo", managed.getRights(), provided.getRights(), true)); -// // update licenseUri -// managed.setRights(mergeEntry("Updating record->licenseUri", managed.getRights(), provided.getRights(), true)); -//1 } else { -// managed = (managed != null) ? managed : provided; -// } -// return managed; -// } /** * Check validity of acl list and then merge new acl list in the existing one. * @@ -1115,95 +887,6 @@ public static void setSchemaRecordDao(ISchemaRecordDao aSchemaRecordDao) { schemaRecordDao = aSchemaRecordDao; } - private static void saveNewDataRecord(MetadataRecord result) { - DataRecord dataRecord; - - // Create shortcut for access. - LOG.trace("Save new data record!"); - dataRecord = transformToDataRecord(result); - - saveNewDataRecord(dataRecord); - } - - private static DataRecord transformToDataRecord(MetadataRecord result) { - DataRecord dataRecord = null; - if (result != null) { - LOG.trace("Transform to data record!"); - dataRecord = new DataRecord(); - dataRecord.setMetadataId(result.getId()); - dataRecord.setVersion(result.getRecordVersion()); - dataRecord.setSchemaId(result.getSchema().getIdentifier()); - dataRecord.setSchemaVersion(result.getSchemaVersion()); - dataRecord.setDocumentHash(result.getDocumentHash()); - dataRecord.setMetadataDocumentUri(result.getMetadataDocumentUri()); - dataRecord.setLastUpdate(result.getLastUpdate()); - } - return dataRecord; - } - - private static void saveNewDataRecord(DataRecord dataRecord) { - if (dataRecordDao != null) { - try { - dataRecordDao.save(dataRecord); - } catch (Exception ex) { - LOG.error("Error saving data record", ex); - } - LOG.trace("Data record saved: {}", dataRecord); - } - } - - /** - * Checks if current user is allowed to access with given AclEntries. - * - * @param aclEntries AclEntries of resource. - * @param currentAcl Check current ACL (true) or new one (false). - * @return Allowed (true) or not. - */ - public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean currentAcl) { - boolean isAllowed = false; - String errorMessage1 = "Error invalid ACL! Reason: Only ADMINISTRATORS are allowed to change ACL entries."; - String errorMessage2 = "Error invalid ACL! Reason: You are not allowed to revoke your own administrator rights."; - Authentication authentication = AuthenticationHelper.getAuthentication(); - List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - for (GrantedAuthority authority : authentication.getAuthorities()) { - authorizationIdentities.add(authority.getAuthority()); - } - if (authorizationIdentities.contains(RepoUserRole.ADMINISTRATOR.getValue())) { - //ROLE_ADMINISTRATOR detected -> no further permission check necessary. - return true; - } - if (LOG.isTraceEnabled()) { - LOG.trace("Check access rights for changing ACL list!"); - for (String authority : authorizationIdentities) { - LOG.trace("Indentity/Authority: '{}'", authority); - } - } - // Check if authorized user still has ADMINISTRATOR rights - Iterator<AclEntry> iterator = aclEntries.iterator(); - while (iterator.hasNext()) { - AclEntry aclEntry = iterator.next(); - LOG.trace("'{}' has ’{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); - if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) - && authorizationIdentities.contains(aclEntry.getSid())) { - isAllowed = true; - LOG.trace("Confirm permission for updating ACL: '{}' has ’{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); - break; - } - } - if (!isAllowed) { - String errorMessage = currentAcl ? errorMessage1 : errorMessage2; - LOG.warn(errorMessage); - if (schemaConfig.isAuthEnabled()) { - if (currentAcl) { - throw new AccessForbiddenException(errorMessage1); - } else { - throw new BadArgumentException(errorMessage2); - } - } - } - return isAllowed; - } - public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { String metadataDocumentUri = metadataRecord.getMetadataDocumentUri(); metadataRecord @@ -1264,26 +947,6 @@ public static void checkLicense(DataResource dataResource, String licenseUri) { } } - public static void check4RelatedResource(DataResource dataResource, RelatedIdentifier relatedResource) { - if (relatedResource != null) { - Set<RelatedIdentifier> relatedResources = dataResource.getRelatedIdentifiers(); - - if (relatedResources.isEmpty()) { - relatedResources.add(relatedResource); - } else { - // Check if related resource already exists (only one related resource of each type allowed) - for (RelatedIdentifier item : relatedResources) { - if (item.getRelationType().equals(relatedResource.getRelationType()) - && !item.getValue().equals(relatedResource.getValue())) { - relatedResources.remove(item); - relatedResources.add(relatedResource); - break; - } - } - } - } - } - /** * Test if exactly one schema and one related resource exists. This method * does NOT check the correctness of the references. @@ -1582,136 +1245,6 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp } validateMetadataDocument(metastoreProperties, document, schemaRecord); } -// -// /** -// * Validate metadata document with given schema. In case of an error a runtime -// * exception is thrown. -// * -// * @param metastoreProperties Configuration properties. -// * @param document Document to validate. -// * @param schemaRecord Record of the schema. -// */ -// public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, -// MultipartFile document, -// SchemaRecord schemaRecord) { -// LOG.trace("validateMetadataDocument {},{}, {}", metastoreProperties, schemaRecord, document); -// -// if (document == null || document.isEmpty()) { -// String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; -// LOG.error(message); -// throw new BadArgumentException(message); -// } -// try { -// try (InputStream inputStream = document.getInputStream()) { -// validateMetadataDocument(metastoreProperties, inputStream, schemaRecord); -// } -// } catch (IOException ex) { -// String message = LOG_ERROR_READ_METADATA_DOCUMENT; -// LOG.error(message, ex); -// throw new UnprocessableEntityException(message); -// } -// } -// -// /** -// * Validate metadata document with given schema. In case of an error a runtime -// * exception is thrown. -// * -// * @param metastoreProperties Configuration properties. -// * @param inputStream Document to validate. -// * @param schemaRecord Record of the schema. -// */ -// public static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, -// InputStream inputStream, -// SchemaRecord schemaRecord) throws IOException { -// LOG.trace("validateMetadataInputStream {},{}, {}", metastoreProperties, schemaRecord, inputStream); -// -// long nano1 = System.nanoTime() / 1000000; -// if (schemaRecord == null || schemaRecord.getSchemaDocumentUri() == null || schemaRecord.getSchemaDocumentUri().trim().isEmpty()) { -// String message = "Missing or invalid schema record. Returning HTTP BAD_REQUEST."; -// LOG.error(message + " -> '{}'", schemaRecord); -// throw new BadArgumentException(message); -// } -// long nano2 = System.nanoTime() / 1000000; -// LOG.trace("Checking local schema file."); -// Path schemaDocumentPath = Paths.get(URI.create(schemaRecord.getSchemaDocumentUri())); -// -// if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { -// LOG.error("Schema document with schemaId '{}'at path {} either does not exist or is no file or is not readable.", schemaRecord.getSchemaId(), schemaDocumentPath); -// throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); -// } -// LOG.trace("obtain validator for type"); -// IValidator applicableValidator; -// if (schemaRecord.getType() == null) { -// byte[] schemaDocument = FileUtils.readFileToByteArray(schemaDocumentPath.toFile()); -// applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, schemaDocument); -// } else { -// applicableValidator = getValidatorForRecord(metastoreProperties, schemaRecord, null); -// } -// long nano3 = System.nanoTime() / 1000000; -// -// if (applicableValidator == null) { -// String message = "No validator found for schema type " + schemaRecord.getType(); -// LOG.error(message); -// throw new UnprocessableEntityException(message); -// } else { -// LOG.trace("Validator found."); -// -// LOG.trace("Performing validation of metadata document using schema {}, version {} and validator {}.", schemaRecord.getSchemaId(), schemaRecord.getVersion(), applicableValidator); -// long nano4 = System.nanoTime() / 1000000; -// if (!applicableValidator.validateMetadataDocument(schemaDocumentPath.toFile(), inputStream)) { -// LOG.warn("Metadata document validation failed. -> " + applicableValidator.getErrorMessage()); -// throw new UnprocessableEntityException(applicableValidator.getErrorMessage()); -// } -// long nano5 = System.nanoTime() / 1000000; -// LOG.info("Validate document(schemaRecord), {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1); -// } -// LOG.trace("Metadata document validation succeeded."); -// } - - /** - * Gets SchemaRecord from identifier. Afterwards there should be a clean up. - * - * @param identifier ResourceIdentifier of type INTERNAL or URL. - * @param version Version (may be null) - * @return schema record. - * @see #cleanUp(edu.kit.datamanager.metastore2.domain.ResourceIdentifier, - * edu.kit.datamanager.metastore2.domain.SchemaRecord) - */ - public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long version) { - LOG.trace("getSchemaRecord {},{}", identifier, version); - SchemaRecord schemaRecord; - if (identifier == null || identifier.getIdentifierType() == null) { - String message = "Missing resource identifier for schema. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - switch (identifier.getIdentifierType()) { - case INTERNAL -> { - String schemaId = identifier.getIdentifier(); - if (schemaId == null) { - String message = "Missing schemaID. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - if (version != null) { - schemaRecord = schemaRecordDao.findBySchemaId(schemaId + SCHEMA_VERSION_SEPARATOR + version); - } else { - schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); - } - } - case URL -> { - schemaRecord = prepareResourceFromUrl(identifier, version); - } - default -> - throw new BadArgumentException("For schema document identifier type '" + identifier.getIdentifierType() + "' is not allowed!"); - } - if (schemaRecord != null) { - LOG.trace("getSchemaRecord {},{}", schemaRecord.getSchemaDocumentUri(), schemaRecord.getVersion()); - } else { - LOG.trace("No matching schema record found!"); - } - return schemaRecord; - } private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataResource) { SchemaRecord schemaRecord = null; @@ -1741,62 +1274,6 @@ private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataRes return schemaRecord; } - private static SchemaRecord prepareResourceFromUrl(ResourceIdentifier identifier, Long version) { - String url = identifier.getIdentifier(); - Path pathToFile; - MetadataSchemaRecord.SCHEMA_TYPE type = null; - Optional<Url2Path> findByUrl = url2PathDao.findByUrl(url); - if (findByUrl.isPresent()) { - url = findByUrl.get().getPath(); - type = findByUrl.get().getType(); - pathToFile = Paths.get(URI.create(url)); - } else { - URI resourceUrl; - try { - resourceUrl = new URI(url); - } catch (URISyntaxException ex) { - String message = String.format("Invalid URL: '%s'", url); - LOG.error(message, ex); - throw new BadArgumentException(message); - } - Optional<Path> path = DownloadUtil.downloadResource(resourceUrl); - pathToFile = path.get(); - } - SchemaRecord schemaRecord = new SchemaRecord(); - schemaRecord.setSchemaDocumentUri(pathToFile.toUri().toString()); - schemaRecord.setType(type); - return schemaRecord; - } - - /** - * Remove all downloaded files for schema Record. - * - * @param schemaRecord Schema record. - */ - public static void cleanUp(ContentInformation schemaRecord) { - LOG.trace("Clean up {}", schemaRecord); - if (schemaRecord == null || schemaRecord.getContentUri() == null) { - String message = "Missing resource locator for schema."; - LOG.error(message); - } else { - URI uri = URI.create(schemaRecord.getContentUri()); - if (!uri.getScheme().equals("file")) { - // remove downloaded file - } else { - // nothing to do - } - } - } - - /** - * Set the DAO holding url and paths. - * - * @param aUrl2PathDao the url2PathDao to set - */ - public static void setUrl2PathDao(IUrl2PathDao aUrl2PathDao) { - url2PathDao = aUrl2PathDao; - } - /** * Update schema document. * @@ -1914,7 +1391,7 @@ public static DataResource updateDataResource4SchemaDocument(MetastoreConfigurat try { byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); - DataResourceRecordUtil.validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDoc); + validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDoc); } catch (IOException ex) { LOG.error("Error validating file!", ex); } @@ -1946,9 +1423,9 @@ private static DataResource mergeDataResource(DataResource oldDataResource, Data updatedDataResource.setAcls(oldDataResource.getAcls()); } else { // Check for access rights for changing ACL (need ADMINISTRATION rights!) - MetadataRecordUtil.checkAccessRights(oldDataResource.getAcls(), true); + checkAccessRights(oldDataResource.getAcls(), true); // Check if access rights still valid afterwards (at least one user with ADMINISTRATION rights should be available!) - MetadataRecordUtil.checkAccessRights(updatedDataResource.getAcls(), false); + checkAccessRights(updatedDataResource.getAcls(), false); } if (updatedDataResource.getRights() == null) { @@ -1983,18 +1460,39 @@ private static DataResource mergeDataResource(DataResource oldDataResource, Data return updatedDataResource; } + /** + * Migrate schema from INTERNAL type to URL type (if necessary) + * + * @param dataResource + * @return + */ + private static DataResource fixRelatedSchemaIfNeeded(DataResource dataResource) { + RelatedIdentifier relatedIdentifier = getSchemaIdentifier(dataResource); + SchemaRecord schemaRecord = getSchemaRecordFromDataResource(dataResource); + if (schemaRecord != null) { + if (relatedIdentifier.getIdentifierType() == Identifier.IDENTIFIER_TYPE.INTERNAL) { + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + // schemaRecord should never be null for internal schema! + relatedIdentifier.setValue(schemaRecord.getAlternateId()); + } + } else { + throw new UnprocessableEntityException("Schema '" + relatedIdentifier.getValue() + "' is not known!"); + } + return dataResource; + } + /** * Validate metadata document with given schema. Determine type if not already * given or check type. * * @param metastoreProperties Configuration for accessing services * @param metadataRecord metadata of the document. - * @param document document + * @param document documentdataResource */ private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, - DataResource metadataRecord, + DataResource dataResource, MultipartFile document) { - LOG.trace("validateMetadataDocument (dataresource) {},{}, {}", metastoreProperties, metadataRecord, document); + LOG.trace("validateMetadataDocument (dataresource) {},{}, {}", metastoreProperties, dataResource, document); if (document == null || document.isEmpty()) { String message = "Missing metadata document in body. Returning HTTP BAD_REQUEST."; LOG.error(message); @@ -2002,14 +1500,14 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro } boolean validationSuccess = false; StringBuilder errorMessage = new StringBuilder(); - SchemaRecord findByAlternateId = getSchemaRecordFromDataResource(metadataRecord); + SchemaRecord findByAlternateId = getSchemaRecordFromDataResource(dataResource); if (findByAlternateId != null) { try { validateMetadataDocument(metastoreProperties, document, findByAlternateId); validationSuccess = true; // After successful validation set type for metadata document resource. MetadataSchemaRecord.SCHEMA_TYPE type = findByAlternateId.getType(); - metadataRecord.setResourceType(ResourceType.createResourceType(type + METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); + dataResource.setResourceType(ResourceType.createResourceType(type + METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); // } catch (Exception ex) { String message = "Error validating document!"; @@ -2017,7 +1515,7 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro errorMessage.append(ex.getMessage()).append("\n"); } } else { - errorMessage.append("No matching schema found for '" + getSchemaIdentifier(metadataRecord).getValue() + "'!"); + errorMessage.append("No matching schema found for '" + getSchemaIdentifier(dataResource).getValue() + "'!"); } if (!validationSuccess) { LOG.error(errorMessage.toString()); diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 697e8118..83470f3a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -18,8 +18,6 @@ import com.fasterxml.jackson.core.JsonParseException; import edu.kit.datamanager.clients.SimpleServiceClient; import edu.kit.datamanager.entities.Identifier; -import edu.kit.datamanager.entities.PERMISSION; -import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.exceptions.*; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.IDataRecordDao; @@ -33,10 +31,7 @@ import edu.kit.datamanager.repo.service.IContentInformationService; import edu.kit.datamanager.repo.util.ContentDataUtils; import edu.kit.datamanager.repo.util.DataResourceUtils; -import edu.kit.datamanager.util.AuthenticationHelper; -import edu.kit.datamanager.util.ControllerUtils; import io.swagger.v3.core.util.Json; -import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; @@ -44,26 +39,17 @@ import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestClientException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.UriComponentsBuilder; -import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.*; import java.util.function.UnaryOperator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Stream; /** @@ -383,7 +369,7 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati ResourceType resourceType = ResourceType.createResourceType(prefixDocument + DataResourceRecordUtil.METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); dataResource.setResourceType(resourceType); - checkLicense(dataResource, metadataRecord.getLicenseUri()); + DataResourceRecordUtil.checkLicense(dataResource, metadataRecord.getLicenseUri()); return dataResource; } @@ -734,25 +720,7 @@ public static MetadataRecord mergeRecords(MetadataRecord managed, MetadataRecord * @return Merged list */ public static Set<AclEntry> mergeAcl(Set<AclEntry> managed, Set<AclEntry> provided) { - // Check for null parameters (which shouldn't happen) - managed = (managed == null) ? new HashSet<>() : managed; - provided = (provided == null) ? new HashSet<>() : provided; - if (!provided.isEmpty()) { - if (!provided.equals(managed)) { - // check for special access rights - // - only administrators are allowed to change ACL - checkAccessRights(managed, true); - // - at least principal has to remain as ADMIN - checkAccessRights(provided, false); - LOG.trace("Updating record acl from {} to {}.", managed, provided); - managed = provided; - } else { - LOG.trace("Provided ACL is still the same -> Continue using old one."); - } - } else { - LOG.trace("Provided ACL is empty -> Continue using old one."); - } - return managed; + return DataResourceRecordUtil.mergeAcl(managed, provided); } /** @@ -764,7 +732,7 @@ public static Set<AclEntry> mergeAcl(Set<AclEntry> managed, Set<AclEntry> provid * @return Merged record */ public static <T> T mergeEntry(String description, T managed, T provided) { - return mergeEntry(description, managed, provided, false); + return DataResourceRecordUtil.mergeEntry(description, managed, provided); } /** @@ -777,12 +745,7 @@ public static <T> T mergeEntry(String description, T managed, T provided) { * @return Merged record */ public static <T> T mergeEntry(String description, T managed, T provided, boolean overwriteWithNull) { - if ((provided != null && !provided.equals(managed)) - || overwriteWithNull) { - LOG.trace(description + " from '{}' to '{}'", managed, provided); - managed = provided; - } - return managed; + return DataResourceRecordUtil.mergeEntry(description, managed, provided, overwriteWithNull); } /** @@ -854,58 +817,6 @@ private static void saveNewDataRecord(DataRecord dataRecord) { } } - /** - * Checks if current user is allowed to access with given AclEntries. - * - * @param aclEntries AclEntries of resource. - * @param currentAcl Check current ACL (true) or new one (false). - * - * @return Allowed (true) or not. - */ - public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean currentAcl) { - boolean isAllowed = false; - String errorMessage1 = "Error invalid ACL! Reason: Only ADMINISTRATORS are allowed to change ACL entries."; - String errorMessage2 = "Error invalid ACL! Reason: You are not allowed to revoke your own administrator rights."; - Authentication authentication = AuthenticationHelper.getAuthentication(); - List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - for (GrantedAuthority authority : authentication.getAuthorities()) { - authorizationIdentities.add(authority.getAuthority()); - } - if (authorizationIdentities.contains(RepoUserRole.ADMINISTRATOR.getValue())) { - //ROLE_ADMINISTRATOR detected -> no further permission check necessary. - return true; - } - if (LOG.isTraceEnabled()) { - LOG.trace("Check access rights for changing ACL list!"); - for (String authority : authorizationIdentities) { - LOG.trace("Indentity/Authority: '{}'", authority); - } - } - // Check if authorized user still has ADMINISTRATOR rights - Iterator<AclEntry> iterator = aclEntries.iterator(); - while (iterator.hasNext()) { - AclEntry aclEntry = iterator.next(); - LOG.trace("'{}' has ’{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); - if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) - && authorizationIdentities.contains(aclEntry.getSid())) { - isAllowed = true; - LOG.trace("Confirm permission for updating ACL: '{}' has ’{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); - break; - } - } - if (!isAllowed) { - String errorMessage = currentAcl ? errorMessage1 : errorMessage2; - LOG.warn(errorMessage); - if (schemaConfig.isAuthEnabled()) { - if (currentAcl) { - throw new AccessForbiddenException(errorMessage1); - } else { - throw new BadArgumentException(errorMessage2); - } - } - } - return isAllowed; - } public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { String metadataDocumentUri = metadataRecord.getMetadataDocumentUri(); @@ -914,24 +825,4 @@ public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { ).getMetadataDocumentById(metadataRecord.getId(), metadataRecord.getRecordVersion(), null, null)).toUri().toString()); LOG.trace("Fix metadata document Uri '{}' -> '{}'", metadataDocumentUri, metadataRecord.getMetadataDocumentUri()); } - - public static void checkLicense(DataResource dataResource, String licenseUri) { - if (licenseUri != null) { - Set<Scheme> rights = dataResource.getRights(); - String licenseId = licenseUri.substring(licenseUri.lastIndexOf("/")); - Scheme license = Scheme.factoryScheme(licenseId, licenseUri); - if (rights.isEmpty()) { - rights.add(license); - } else { - // Check if license already exists (only one license allowed) - if (!rights.contains(license)) { - rights.clear(); - rights.add(license); - } - } - } else { - // Remove license - dataResource.getRights().clear(); - } - } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 6022a0fe..11f93b0a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -16,7 +16,6 @@ package edu.kit.datamanager.metastore2.util; import com.fasterxml.jackson.core.JsonParseException; -import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.exceptions.BadArgumentException; import edu.kit.datamanager.exceptions.CustomInternalServerError; import edu.kit.datamanager.exceptions.ResourceNotFoundException; @@ -39,7 +38,6 @@ import edu.kit.datamanager.repo.service.IContentInformationService; import edu.kit.datamanager.repo.util.ContentDataUtils; import edu.kit.datamanager.repo.util.DataResourceUtils; -import edu.kit.datamanager.util.ControllerUtils; import io.swagger.v3.core.util.Json; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; @@ -69,7 +67,6 @@ import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeAcl; import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeEntry; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; -import org.springframework.util.MimeType; /** * Utility class for handling json documents @@ -241,84 +238,6 @@ public static MetadataSchemaRecord updateMetadataSchemaRecord(MetastoreConfigura givenRecord = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataRecord); } DataResource updateDataResource = DataResourceRecordUtil.updateDataResource4SchemaDocument(applicationProperties, resourceId, eTag, givenRecord, schemaDocument, supplier); -// LOG.trace("Checking provided ETag."); -// ControllerUtils.checkEtag(eTag, dataResource); -// SchemaRecord schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(dataResource.getId() + "/"); -// if (metadataRecord != null) { -// metadataRecord.setSchemaVersion(schemaRecord.getVersion()); -// MetadataSchemaRecord existingRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); -// existingRecord = mergeRecords(existingRecord, metadataRecord); -// mergeSchemaRecord(schemaRecord, existingRecord); -// dataResource = migrateToDataResource(applicationProperties, existingRecord); -// } else { -// dataResource = DataResourceUtils.copyDataResource(dataResource); -// } -// -// if (schemaDocument != null) { -// // Get schema record for this schema -// validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDocument); -// -// ContentInformation info; -// info = getContentInformationOfResource(applicationProperties, dataResource); -// -// boolean noChanges = false; -// String fileName = schemaDocument.getOriginalFilename(); -// if (info != null) { -// noChanges = true; -// fileName = info.getRelativePath(); -// // Check for changes... -// try { -// byte[] currentFileContent; -// File file = new File(URI.create(info.getContentUri())); -// if (schemaDocument.getSize() == Files.size(file.toPath())) { -// currentFileContent = FileUtils.readFileToByteArray(file); -// byte[] newFileContent = schemaDocument.getBytes(); -// for (int index = 0; index < currentFileContent.length; index++) { -// if (currentFileContent[index] != newFileContent[index]) { -// noChanges = false; -// break; -// } -// } -// } else { -// noChanges = false; -// } -// } catch (IOException ex) { -// LOG.error("Error reading current file!", ex); -// throw new BadArgumentException("Error reading schema document!"); -// } -// } -// if (!noChanges) { -// // Everything seems to be fine update document and increment version -// LOG.trace("Updating schema document (and increment version)..."); -// String version = dataResource.getVersion(); -// if (version != null) { -// dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); -// } -// ContentDataUtils.addFile(applicationProperties, dataResource, schemaDocument, fileName, null, true, supplier); -// } else { -// schemaRecordDao.delete(schemaRecord); -// } -// } else { -// schemaRecordDao.delete(schemaRecord); -// // validate if document is still valid due to changed record settings. -// metadataRecord = migrateToMetadataSchemaRecord(applicationProperties, dataResource, false); -// URI schemaDocumentUri = URI.create(metadataRecord.getSchemaDocumentUri()); -// -// Path schemaDocumentPath = Paths.get(schemaDocumentUri); -// if (!Files.exists(schemaDocumentPath) || !Files.isRegularFile(schemaDocumentPath) || !Files.isReadable(schemaDocumentPath)) { -// LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); -// throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); -// } -// -// try { -// byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); -// MetadataSchemaRecordUtil.validateMetadataSchemaDocument(applicationProperties, schemaRecord, schemaDoc); -// } catch (IOException ex) { -// LOG.error("Error validating file!", ex); -// } -// -// } -// dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); return migrateToMetadataSchemaRecord(applicationProperties, updateDataResource, true); } @@ -426,7 +345,7 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati checkDescription(descriptions, metadataSchemaRecord.getLabel(), Description.TYPE.OTHER); checkDescription(descriptions, metadataSchemaRecord.getDefinition(), Description.TYPE.TECHNICAL_INFO); checkDescription(descriptions, metadataSchemaRecord.getComment(), Description.TYPE.ABSTRACT); - MetadataRecordUtil.checkLicense(dataResource, metadataSchemaRecord.getLicenseUri()); + DataResourceRecordUtil.checkLicense(dataResource, metadataSchemaRecord.getLicenseUri()); return dataResource; } @@ -467,42 +386,6 @@ private static void checkDescription(Set<Description> descriptions, String descr } } - /** - * Test if alternate identifier exists. If alternate identifier is null remove - * existing alternate identifier type. if alternate identifier added/changed - * add/change alternate identifier with given type. - * - * @param identifiers all alternate identifiers - * @param identifier Content of (new) alternate identifier - * @param type Type of the alternate identifier - */ - public static void checkAlternateIdentifier(Set<Identifier> identifiers, String identifier, Identifier.IDENTIFIER_TYPE type) { - Iterator<Identifier> iterator = identifiers.iterator(); - Identifier item = null; - while (iterator.hasNext()) { - Identifier next = iterator.next(); - - if (next.getIdentifierType().compareTo(type) == 0) { - item = next; - break; - } - } - if (item != null) { - if (identifier != null) { - if (!identifier.equals(item.getValue())) { - item.setValue(identifier); - } - } else { - identifiers.remove(item); - } - } else { - if (identifier != null) { - item = Identifier.factoryIdentifier(identifier, type); - identifiers.add(item); - } - } - } - /** * Transform dataresource to metadata schema record. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index ac041a0d..6ee07070 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -190,26 +190,26 @@ public ResponseEntity createRecord( LOG.debug("Test for existing metadata record for given schema and resource"); RelatedIdentifier schemaIdentifier; // try { - schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); - switch (schemaIdentifier.getIdentifierType()) { - case INTERNAL: - // nothing to do - break; - case URL: - SchemaRecord schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); - if (schemaRecord == null) { - String message = "External URLs are not supported yet!\n" - + "But '" + schemaIdentifier.getValue() + "' seems not to be an internal one!\n" - + "Hint: Maybe version number is missing (e.g.: [...]?version=1"; - LOG.error(message); - throw new ResourceNotFoundException(message); - } - schemaIdentifier.setValue(schemaRecord.getSchemaId()); - schemaIdentifier.setIdentifierType(INTERNAL); - break; - default: - throw new UnprocessableEntityException("Schema referenced by '" + schemaIdentifier.getIdentifierType().toString() + "' is not supported yet!"); - } + schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); + switch (schemaIdentifier.getIdentifierType()) { + case INTERNAL: + // nothing to do + break; + case URL: + SchemaRecord schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); + if (schemaRecord == null) { + String message = "External URLs are not supported yet!\n" + + "But '" + schemaIdentifier.getValue() + "' seems not to be an internal one!\n" + + "Hint: Maybe version number is missing (e.g.: [...]?version=1"; + LOG.error(message); + throw new ResourceNotFoundException(message); + } + schemaIdentifier.setValue(schemaRecord.getSchemaId()); + schemaIdentifier.setIdentifierType(INTERNAL); + break; + default: + throw new UnprocessableEntityException("Schema referenced by '" + schemaIdentifier.getIdentifierType().toString() + "' is not supported yet!"); + } // } catch (ResourceNotFoundException rnfe) { // LOG.debug("Error checking for existing relations.", rnfe); // throw new UnprocessableEntityException("Schema ID seems to be invalid"); @@ -295,7 +295,7 @@ public ResponseEntity<ContentInformation> getContentInformationById( contentInformation.setContentUri(locationUri.toString()); contentInformation.setRelativePath(null); contentInformation.setVersioningService(null); - + return ResponseEntity.ok().body(contentInformation); } @@ -306,7 +306,7 @@ public ResponseEntity<ElasticWrapper> getAclById( WebRequest wr, HttpServletResponse hsr ) { - LOG.trace("Performing getAclById({}, {}).", id, version); + LOG.info("Performing getAclById({}, {}).", id, version); if (!AuthenticationHelper.isAuthenticatedAsService()) { throw new AccessForbiddenException("Only for services!"); } @@ -424,7 +424,6 @@ public ResponseEntity<List<DataResource>> getRecords( Page<DataResource> records = DataResourceRecordUtil.queryDataResources(spec, pgbl); - LOG.trace("Transforming Dataresource to DataResource"); List<DataResource> recordList = records.getContent(); for (DataResource item : recordList) { @@ -493,16 +492,17 @@ public void contribute(Info.Builder builder) { builder.withDetail("metadataRepo", details); } } + /** * Get value of relatedIdentitier with given relation type. - * + * * @param result data resource. * @param relationType type of related identifier. * @return related identifier. */ private RelatedIdentifier getRelatedIdentifier(DataResource result, RelatedIdentifier.RELATION_TYPES relationType) { RelatedIdentifier relatedIdentifier = null; - for (RelatedIdentifier item: result.getRelatedIdentifiers()) { + for (RelatedIdentifier item : result.getRelatedIdentifiers()) { if (item.getRelationType().equals(relationType)) { relatedIdentifier = item; break; @@ -510,7 +510,7 @@ private RelatedIdentifier getRelatedIdentifier(DataResource result, RelatedIdent } return relatedIdentifier; } - + @Bean public RestTemplate restTemplate() { return new RestTemplate(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java index 30834b7a..4bd24445 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java @@ -6,18 +6,19 @@ package edu.kit.datamanager.metastore2.test; import com.fasterxml.jackson.databind.ObjectMapper; +import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.entities.repo.RelatedIdentifier; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; -import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.DataResource; -import edu.kit.datamanager.entities.repo.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.ResourceType; +import edu.kit.datamanager.repo.domain.Title; import edu.kit.datamanager.repo.domain.acl.AclEntry; import org.junit.Assert; import org.junit.Before; @@ -186,7 +187,7 @@ public void testFindSchemaRecordsBySchemaId() throws Exception { DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); Assert.assertEquals("No of records for schema '" + i + "'", 1, result.length); - Assert.assertEquals("SchemaID '" + schemaId + "'", schemaId, result[0].getSchemaId()); + Assert.assertEquals("SchemaID '" + schemaId + "'", schemaId, result[0].getId()); } } @@ -205,9 +206,9 @@ public void testFindRecordsBySchemaId() throws Exception { Assert.assertEquals("No of records for schema '" + i + "'", i, result.length); for (DataResource item : result) { - RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(item); - Assert.assertEquals(RelatedIdentifier.RELATED_IDENTIFIER_TYPE.URL, schemaIdentifier.getIdentifierType()); - String schemaUrl = item.getSchema().getIdentifier(); + edu.kit.datamanager.repo.domain.RelatedIdentifier schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(item); + Assert.assertEquals(schemaIdentifier.getIdentifierType().URL, schemaIdentifier.getIdentifierType()); + String schemaUrl = DataResourceRecordUtil.getSchemaIdentifier(item).getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); Assert.assertTrue(schemaUrl.contains(schemaId)); @@ -291,7 +292,7 @@ public void testFindSchemaRecordsByMimeType() throws Exception { Assert.assertEquals(MAX_NO_OF_SCHEMAS, result.length); for (DataResource item : result) { - Assert.assertEquals(mimeType, item.getMimeType()); + Assert.assertTrue(item.getFormats().contains(mimeType)); } mimeType = MediaType.APPLICATION_XML.toString(); res = this.mockMvc.perform(get(API_SCHEMA_PATH) @@ -303,7 +304,7 @@ public void testFindSchemaRecordsByMimeType() throws Exception { Assert.assertEquals(MAX_NO_OF_SCHEMAS, result.length); for (DataResource item : result) { - Assert.assertEquals(mimeType, item.getMimeType()); + Assert.assertTrue(item.getFormats().contains(mimeType)); } } @@ -341,9 +342,10 @@ public void testFindSchemaRecordsByMultipleMimeTypesIncludingInvalidMimeType() t @Test public void testFindRecordsByResourceId() throws Exception { for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { - ResourceIdentifier relatedResource = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE + i); + edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE + i, null, null); + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) - .param("resourceId", relatedResource.getIdentifier())) + .param("resourceId", relatedIdentifier.getValue())) .andDo(print()) .andExpect(status().isOk()) .andReturn(); @@ -352,7 +354,9 @@ public void testFindRecordsByResourceId() throws Exception { Assert.assertEquals((MAX_NO_OF_SCHEMAS - i + 1) * 2, result.length); for (DataResource item : result) { - Assert.assertEquals(relatedResource, item.getRelatedResource()); + Assert.assertEquals(relatedIdentifier.getValue(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue()); + Assert.assertEquals(relatedIdentifier.getIdentifierType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getIdentifierType()); + Assert.assertEquals(relatedIdentifier.getRelationType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getRelationType()); } } } @@ -399,9 +403,10 @@ public void testFindRecordsByInvalidResourceId() throws Exception { @Test public void testFindRecordsByMultipleResourceIdsIncludingInvalidResourceId() throws Exception { for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { - ResourceIdentifier relatedResource = ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE + i); + edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE + i, null, null); + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) - .param("resourceId", relatedResource.getIdentifier()) + .param("resourceId", relatedIdentifier.getValue()) .param("resourceId", INVALID_MIMETYPE)) .andDo(print()) .andExpect(status().isOk()) @@ -411,7 +416,9 @@ public void testFindRecordsByMultipleResourceIdsIncludingInvalidResourceId() thr Assert.assertEquals((MAX_NO_OF_SCHEMAS - i + 1) * 2, result.length); for (DataResource item : result) { - Assert.assertEquals(relatedResource, item.getRelatedResource()); + Assert.assertEquals(relatedIdentifier.getValue(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue()); + Assert.assertEquals(relatedIdentifier.getIdentifierType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getIdentifierType()); + Assert.assertEquals(relatedIdentifier.getRelationType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getRelationType()); } } } @@ -431,24 +438,25 @@ public void testFindRecordsByUnknownSchemaId() throws Exception { Assert.assertEquals("No of records for schema '" + schemaId + "'", 0, result.length); } - public void registerSchemaDocument(DataResource.SCHEMA_TYPE schemaType, String schemaId) throws Exception { + public void registerSchemaDocument(String schemaType, String schemaId) throws Exception { DataResource record = new DataResource(); - record.setSchemaId(schemaId); - record.setType(schemaType); + record.getTitles().add(Title.factoryTitle("Title for " + schemaId)); + record.setResourceType(ResourceType.createResourceType(schemaType + "_Schema", ResourceType.TYPE_GENERAL.MODEL)); + record.setId(schemaId); Set<AclEntry> aclEntries = new HashSet<>(); aclEntries.add(new AclEntry("test", PERMISSION.READ)); aclEntries.add(new AclEntry("SELF", PERMISSION.ADMINISTRATE)); - record.setAcl(aclEntries); + record.setAcls(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile schemaFile; switch (schemaType) { - case JSON: - record.setMimeType(MediaType.APPLICATION_JSON.toString()); + case "JSON": + record.getFormats().add(MediaType.APPLICATION_JSON_VALUE); schemaFile = new MockMultipartFile("schema", "schema.json", "application/json", JSON_SCHEMA.getBytes()); break; - case XML: - record.setMimeType(MediaType.APPLICATION_XML.toString()); + case "XML": + record.getFormats().add(MediaType.APPLICATION_XML_VALUE); schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", XML_SCHEMA.getBytes()); break; default: @@ -469,12 +477,17 @@ public void registerSchemaDocument(DataResource.SCHEMA_TYPE schemaType, String s */ public void ingestMetadataDocument(String schemaId, String resource) throws Exception { DataResource record = new DataResource(); - record.setSchema(ResourceIdentifier.factoryInternalResourceIdentifier(schemaId)); - record.setRelatedResource(ResourceIdentifier.factoryInternalResourceIdentifier(resource)); + edu.kit.datamanager.repo.domain.RelatedIdentifier schemaIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.HAS_METADATA, schemaId, null, null); + schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, resource, null, null); + relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); + record.getRelatedIdentifiers().add(schemaIdentifier); + record.getRelatedIdentifiers().add(relatedIdentifier); + record.getTitles().add(Title.factoryTitle("Document for schemaID: " + schemaId)); Set<AclEntry> aclEntries = new HashSet<>(); -// aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); -// aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); -// record.setAcl(aclEntries); + aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); + aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); + record.setAcls(aclEntries); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -541,8 +554,8 @@ private void prepareEnvironment() { private void prepareSchemas() throws Exception { // Prepare 5 different schemas for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { - registerSchemaDocument(DataResource.SCHEMA_TYPE.JSON, JSON_SCHEMA_ID + i); - registerSchemaDocument(DataResource.SCHEMA_TYPE.XML, XML_SCHEMA_ID + i); + registerSchemaDocument("JSON", JSON_SCHEMA_ID + i); + registerSchemaDocument("XML", XML_SCHEMA_ID + i); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 7b28980c..ad9305cc 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -1978,7 +1978,7 @@ public void testUpdateRecordWithInvalidSetting4Json() throws Exception { // Change only version of schema to a version which is not valid. CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, 1L, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); // Change to a nonexistent version of schema. - CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isBadRequest()); + CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, alternativeSchemaId, Long.MAX_VALUE, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); // Change to another schema CreateSchemaUtil.ingestOrUpdateXmlMetadataDocumentV2(mockMvc, SCHEMA_ID, 1L, "document", null, schemaConfig.getJwtSecret(), true, status().isUnprocessableEntity()); } From eb7a31a072bf5ba7cba9bff6f5b76d271170b411 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:43:29 +0000 Subject: [PATCH 126/181] Update plugin org.owasp.dependencycheck to v11 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09cbac3e..dd7bc682 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.10.2' id 'io.freefair.maven-publish-java' version '8.10.2' - id 'org.owasp.dependencycheck' version '10.0.4' + id 'org.owasp.dependencycheck' version '11.1.0' id 'org.asciidoctor.jvm.convert' version '4.0.3' id 'net.ltgt.errorprone' version '4.0.1' id 'net.researchgate.release' version '3.0.2' From 7de42619359290591f3edea3e769ef1a56349bd5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:04:49 +0000 Subject: [PATCH 127/181] Update dependency com.networknt:json-schema-validator to v1.5.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09cbac3e..ce255d42 100644 --- a/build.gradle +++ b/build.gradle @@ -103,7 +103,7 @@ dependencies { implementation "org.apache.tika:tika-core:3.0.0" // JSON validator - implementation "com.networknt:json-schema-validator:1.5.2" + implementation "com.networknt:json-schema-validator:1.5.3" // XML validator // https://mvnrepository.com/artifact/xerces/xercesImpl implementation 'xerces:xercesImpl:2.12.2' From e37d529c8821d349f46940c4a5bb5e7d8cbc4f2b Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 4 Nov 2024 16:15:08 +0100 Subject: [PATCH 128/181] Add some more tests for filtering metadata documents. --- .../test/MetadataControllerFilterTestV2.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java index 4bd24445..6ea3d6fc 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java @@ -240,6 +240,31 @@ public void testFindRecordsByMultipleSchemaIds() throws Exception { } } + @Test + public void testFindRecordsByMultipleButWrongSchemaIds() throws Exception { + ObjectMapper map = new ObjectMapper(); + int noOfResults; + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + MockHttpServletRequestBuilder get = get(API_METADATA_PATH); + noOfResults = 0; + for (int j = 1; j <= i; j++) { + noOfResults += (MAX_NO_OF_SCHEMAS - j + 1) * 2; + String relatedResource = RELATED_RESOURCE + j; + get.param("schemaId", relatedResource); + } + get.param("size", Integer.toString(noOfResults * 2)); + get.header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE); + MvcResult res = this.mockMvc + .perform(get) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for schema should be always '0'!", 0, result.length); + } + } + @Test public void testFindRecordsByMultipleSchemaIdsPlusInvalidSchemaId() throws Exception { ObjectMapper map = new ObjectMapper(); @@ -386,6 +411,30 @@ public void testFindRecordsByMultipleResourceIds() throws Exception { } } + @Test + public void testFindRecordsByMultipleButWronResourceIds() throws Exception { + ObjectMapper map = new ObjectMapper(); + int noOfResults; + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + MockHttpServletRequestBuilder get = get(API_METADATA_PATH); + noOfResults = 0; + for (int j = 1; j <= i; j++) { + noOfResults += j; + String schemaId = JSON_SCHEMA_ID + j; + get.param("resourceId", schemaId); + } + get.header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE); + MvcResult res = this.mockMvc + .perform(get) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of records for resourceIDs should be always '0'!", 0, result.length); + } + } + @Test public void testFindRecordsByInvalidResourceId() throws Exception { From 458969d0a30223a56a6b1c1d7edb46a3cbdcaabc Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 4 Nov 2024 16:16:54 +0100 Subject: [PATCH 129/181] First steps creating specification for schemas API v2. --- .../spec/RelatedIdentifier4SchemaSpec.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifier4SchemaSpec.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifier4SchemaSpec.java b/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifier4SchemaSpec.java new file mode 100644 index 00000000..5eb872fa --- /dev/null +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifier4SchemaSpec.java @@ -0,0 +1,57 @@ +/* + * Copyright 2018 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.dao.spec; + +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Root; +import org.datacite.schema.kernel_4.Resource.AlternateIdentifiers.AlternateIdentifier; +import org.springframework.data.jpa.domain.Specification; + +/** + * + * @author jejkal + */ +public class RelatedIdentifier4SchemaSpec { + + /** + * Hidden constructor. + */ + private RelatedIdentifier4SchemaSpec() { + } + + public static Specification<DataResource> toSpecification(final RelatedIdentifier.RELATION_TYPES relationType, final String... identifierValues) { + Specification<DataResource> newSpec = Specification.where(null); + if (identifierValues == null || identifierValues.length == 0) { + return newSpec; + } + + return (Root<DataResource> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> { + query.distinct(true); + + //join dataresource table with alternate identifiers table + Join<DataResource, AlternateIdentifier> altJoin = root.join("relatedIdentifiers", JoinType.INNER); + //get all alternate identifiers NOT of type INTERNAL with one of the provided values + return builder. + and(altJoin.get("value"). + in((Object[]) identifierValues)), altJoin.get("relationType").in((Object[]) identifierValues))); + }; + } +} From 77d9a258de1d441ab56612a392b8d7a5092e2e88 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 5 Nov 2024 08:24:40 +0100 Subject: [PATCH 130/181] Fix Specification for relatedIdentifiers with given relation type. --- ...ava => RelatedIdentifierWithTypeSpec.java} | 27 +++++++++++++++---- .../util/DataResourceRecordUtil.java | 6 ++--- 2 files changed, 25 insertions(+), 8 deletions(-) rename src/main/java/edu/kit/datamanager/metastore2/dao/spec/{RelatedIdentifier4SchemaSpec.java => RelatedIdentifierWithTypeSpec.java} (67%) diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifier4SchemaSpec.java b/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifierWithTypeSpec.java similarity index 67% rename from src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifier4SchemaSpec.java rename to src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifierWithTypeSpec.java index 5eb872fa..a64a9d4b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifier4SchemaSpec.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifierWithTypeSpec.java @@ -15,6 +15,7 @@ */ package edu.kit.datamanager.metastore2.dao.spec; +import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.domain.DataResource; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import jakarta.persistence.criteria.CriteriaBuilder; @@ -23,26 +24,42 @@ import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Root; import org.datacite.schema.kernel_4.Resource.AlternateIdentifiers.AlternateIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.jpa.domain.Specification; /** * * @author jejkal */ -public class RelatedIdentifier4SchemaSpec { +public class RelatedIdentifierWithTypeSpec { + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(RelatedIdentifierWithTypeSpec.class); + /** * Hidden constructor. */ - private RelatedIdentifier4SchemaSpec() { + private RelatedIdentifierWithTypeSpec() { } public static Specification<DataResource> toSpecification(final RelatedIdentifier.RELATION_TYPES relationType, final String... identifierValues) { Specification<DataResource> newSpec = Specification.where(null); + if (relationType == null) { + return RelatedIdentifierSpec.toSpecification(identifierValues); + } if (identifierValues == null || identifierValues.length == 0) { return newSpec; } - + String[] relationTypes = {relationType.name()}; + for (String identifierValue : identifierValues) { + LOG.trace("RelatedIdentifier4SchemaSpec->identifierValue: '{}'", identifierValue); + } + for (String relationTypeValues : relationTypes) { + LOG.trace("RelatedIdentifier4SchemaSpec->relationType: '{}'", relationTypeValues); + } return (Root<DataResource> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> { query.distinct(true); @@ -50,8 +67,8 @@ public static Specification<DataResource> toSpecification(final RelatedIdentifie Join<DataResource, AlternateIdentifier> altJoin = root.join("relatedIdentifiers", JoinType.INNER); //get all alternate identifiers NOT of type INTERNAL with one of the provided values return builder. - and(altJoin.get("value"). - in((Object[]) identifierValues)), altJoin.get("relationType").in((Object[]) identifierValues))); + and(altJoin.get("value").in((Object[]) identifierValues), + altJoin.get("relationType").in((Object[]) relationTypes)); }; } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index dc4c870e..e8bf434d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -24,6 +24,7 @@ import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.dao.spec.RelatedIdentifierWithTypeSpec; import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; @@ -32,7 +33,6 @@ import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; import edu.kit.datamanager.repo.configuration.RepoBaseConfiguration; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.domain.*; import edu.kit.datamanager.repo.domain.acl.AclEntry; @@ -732,7 +732,7 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou } } if (!allSchemaIds.isEmpty()) { - specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(allSchemaIds.toArray(String[]::new))); + specWithSchema = specWithSchema.and(RelatedIdentifierWithTypeSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, allSchemaIds.toArray(String[]::new))); } } return specWithSchema; @@ -748,7 +748,7 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou public static Specification<DataResource> findByRelatedId(Specification<DataResource> specification, List<String> relatedIds) { Specification<DataResource> specWithSchema = specification; if ((relatedIds != null) && !relatedIds.isEmpty()) { - specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(relatedIds.toArray(String[]::new))); + specWithSchema = specWithSchema.and(RelatedIdentifierWithTypeSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, relatedIds.toArray(String[]::new))); } return specWithSchema; } From f0ce4fe1d4933db58b0b01bf07ee874447d69c88 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 5 Nov 2024 08:25:38 +0100 Subject: [PATCH 131/181] Add also some tests for filtering metadata documents with API v1. --- .../test/MetadataControllerFilterTest.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java index 8e457ec9..252ac19b 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java @@ -230,6 +230,31 @@ public void testFindRecordsByMultipleSchemaIds() throws Exception { } } + @Test + public void testFindRecordsByMultipleButWrongSchemaIds() throws Exception { + ObjectMapper map = new ObjectMapper(); + int noOfResults; + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + MockHttpServletRequestBuilder get = get("/api/v1/metadata/"); + noOfResults = 0; + for (int j = 1; j <= i; j++) { + noOfResults += (MAX_NO_OF_SCHEMAS - j + 1) * 2; + String relatedResource = RELATED_RESOURCE + j; + get.param("schemaId", relatedResource); + } + get.param("size", Integer.toString(noOfResults * 2)); + get.header("Accept", MetadataRecord.METADATA_RECORD_MEDIA_TYPE); + MvcResult res = this.mockMvc + .perform(get) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals("No of records for schema '1 - " + i + "'", 0, result.length); + } + } + @Test public void testFindRecordsByMultipleSchemaIdsPlusInvalidSchemaId() throws Exception { ObjectMapper map = new ObjectMapper(); @@ -373,6 +398,30 @@ public void testFindRecordsByMultipleResourceIds() throws Exception { } } + @Test + public void testFindRecordsByMultipleButWrongResourceIds() throws Exception { + ObjectMapper map = new ObjectMapper(); + int noOfResults; + for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { + MockHttpServletRequestBuilder get = get("/api/v1/metadata/"); + noOfResults = 0; + for (int j = 1; j <= i; j++) { + noOfResults += j; + String schemaId = JSON_SCHEMA_ID + j; + get.param("resourceId", schemaId); + } + get.header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE); + MvcResult res = this.mockMvc + .perform(get) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + MetadataRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataRecord[].class); + + Assert.assertEquals("No of records for schema '1 - " + i + "'", 0, result.length); + } + } + @Test public void testFindRecordsByInvalidResourceId() throws Exception { From 32e63577bdba4c01fc423b8d105e39fade9ef60b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 07:50:19 +0000 Subject: [PATCH 132/181] Update eclipse-temurin Docker tag to v23 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e937a4f5..4cdd337e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ ARG SERVICE_ROOT_DIRECTORY_DEFAULT=/spring/ #################################################### # Building environment (java & git) #################################################### -FROM eclipse-temurin:17 AS build-env-java +FROM eclipse-temurin:23 AS build-env-java LABEL maintainer=webmaster@datamanager.kit.edu LABEL stage=build-env @@ -48,7 +48,7 @@ RUN bash ./build.sh $SERVICE_DIRECTORY #################################################### # Runtime environment 4 metastore2 #################################################### -FROM eclipse-temurin:17 AS run-service-metastore2 +FROM eclipse-temurin:23 AS run-service-metastore2 LABEL maintainer=webmaster@datamanager.kit.edu LABEL stage=run From f8c6bbaa1889123d6787d206cdeeade6684153e3 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 5 Nov 2024 19:02:57 +0100 Subject: [PATCH 133/181] Fix test with schemaIDs with upper cases. (No longer allowed) --- .../metastore2/util/MetadataSchemaRecordUtil.java | 7 +++++++ .../metastore2/test/SchemaRegistryControllerTest.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 11f93b0a..3ee34000 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -136,6 +136,13 @@ public static MetadataSchemaRecord createMetadataSchemaRecord(MetastoreConfigura LOG.error(message); throw new BadArgumentException(message); } + String toLowerCase; + toLowerCase = metadataRecord.getSchemaId().toLowerCase(Locale.getDefault()); + if (!toLowerCase.equals(metadataRecord.getSchemaId())) { + String message = "Not a valid schema id! Please try '" + toLowerCase + "' instead!"; + LOG.error(message); + throw new BadArgumentException(message); + } } // Create schema record SchemaRecord schemaRecord = new SchemaRecord(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java index 6ca451fc..72f0a06a 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTest.java @@ -265,7 +265,7 @@ public void testCreateSchemaRecordWithCapitalLetter() throws Exception { @Test public void testCreateRegisterSchemaRecordWithSameIdButCapitalLetter() throws Exception { String schemaIDWithCapitalLetters = "mySecondTest"; - MetadataSchemaRecord record = createMetadataRecord(schemaIDWithCapitalLetters); + MetadataSchemaRecord record = createMetadataRecord(schemaIDWithCapitalLetters.toLowerCase(Locale.getDefault())); ObjectMapper mapper = new ObjectMapper(); MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); From 8caa16ed0a3acfddd87b00919fcde75a86fdeaec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:15:40 +0000 Subject: [PATCH 134/181] Update dependency edu.kit.datamanager:service-base to v1.3.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index da89dcf1..20f69917 100644 --- a/build.gradle +++ b/build.gradle @@ -110,7 +110,7 @@ dependencies { // datamanager implementation "edu.kit.datamanager:repo-core:1.2.2" - implementation "edu.kit.datamanager:service-base:1.3.1" + implementation "edu.kit.datamanager:service-base:1.3.2" // elasticsearch (since service-base 1.1.0) implementation "org.springframework.data:spring-data-elasticsearch:5.3.5" From 143f11b41973362fd4ad3e835d96371afb191db4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 07:00:57 +0000 Subject: [PATCH 135/181] Update dependency edu.kit.datamanager:repo-core to v1.2.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 20f69917..c839046a 100644 --- a/build.gradle +++ b/build.gradle @@ -109,7 +109,7 @@ dependencies { implementation "xerces:xercesImpl:2.12.2" // datamanager - implementation "edu.kit.datamanager:repo-core:1.2.2" + implementation "edu.kit.datamanager:repo-core:1.2.3" implementation "edu.kit.datamanager:service-base:1.3.2" // elasticsearch (since service-base 1.1.0) From 8a167cfcf9fb6fb0a4a251d95349770de0a6bcc5 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 11 Nov 2024 15:07:32 +0100 Subject: [PATCH 136/181] Add test for landing page for schema (v2). --- .../spec/RelatedIdentifierWithTypeSpec.java | 74 -------- .../util/DataResourceRecordUtil.java | 168 ++++++++++++------ .../web/impl/LandingPageControllerImplV2.java | 9 +- .../test/MetadataControllerTest.java | 40 +++++ .../test/MetadataControllerTestV2.java | 41 +++++ 5 files changed, 205 insertions(+), 127 deletions(-) delete mode 100644 src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifierWithTypeSpec.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifierWithTypeSpec.java b/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifierWithTypeSpec.java deleted file mode 100644 index a64a9d4b..00000000 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/spec/RelatedIdentifierWithTypeSpec.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2018 Karlsruhe Institute of Technology. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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 edu.kit.datamanager.metastore2.dao.spec; - -import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; -import edu.kit.datamanager.repo.domain.DataResource; -import edu.kit.datamanager.repo.domain.RelatedIdentifier; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Join; -import jakarta.persistence.criteria.JoinType; -import jakarta.persistence.criteria.Root; -import org.datacite.schema.kernel_4.Resource.AlternateIdentifiers.AlternateIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.jpa.domain.Specification; - -/** - * - * @author jejkal - */ -public class RelatedIdentifierWithTypeSpec { - - /** - * Logger. - */ - private static final Logger LOG = LoggerFactory.getLogger(RelatedIdentifierWithTypeSpec.class); - - /** - * Hidden constructor. - */ - private RelatedIdentifierWithTypeSpec() { - } - - public static Specification<DataResource> toSpecification(final RelatedIdentifier.RELATION_TYPES relationType, final String... identifierValues) { - Specification<DataResource> newSpec = Specification.where(null); - if (relationType == null) { - return RelatedIdentifierSpec.toSpecification(identifierValues); - } - if (identifierValues == null || identifierValues.length == 0) { - return newSpec; - } - String[] relationTypes = {relationType.name()}; - for (String identifierValue : identifierValues) { - LOG.trace("RelatedIdentifier4SchemaSpec->identifierValue: '{}'", identifierValue); - } - for (String relationTypeValues : relationTypes) { - LOG.trace("RelatedIdentifier4SchemaSpec->relationType: '{}'", relationTypeValues); - } - return (Root<DataResource> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> { - query.distinct(true); - - //join dataresource table with alternate identifiers table - Join<DataResource, AlternateIdentifier> altJoin = root.join("relatedIdentifiers", JoinType.INNER); - //get all alternate identifiers NOT of type INTERNAL with one of the provided values - return builder. - and(altJoin.get("value").in((Object[]) identifierValues), - altJoin.get("relationType").in((Object[]) relationTypes)); - }; - } -} diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index e8bf434d..71730434 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -24,7 +24,6 @@ import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.IMetadataFormatDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import edu.kit.datamanager.metastore2.dao.spec.RelatedIdentifierWithTypeSpec; import edu.kit.datamanager.metastore2.domain.*; import edu.kit.datamanager.metastore2.domain.ResourceIdentifier.IdentifierType; import edu.kit.datamanager.metastore2.domain.oaipmh.MetadataFormat; @@ -71,7 +70,9 @@ import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; +import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.domain.Date; +import java.time.Instant; /** * Utility class for handling json documents @@ -477,58 +478,18 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app metadataRecord.setId(dataResource.getId()); metadataRecord.setAcl(dataResource.getAcls()); - for (edu.kit.datamanager.repo.domain.Date d : dataResource.getDates()) { - if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { - LOG.trace("Creation date entry found."); - metadataRecord.setCreatedAt(d.getValue()); - break; - } - } - if (dataResource.getLastUpdate() != null) { - metadataRecord.setLastUpdate(dataResource.getLastUpdate()); - } + metadataRecord.setCreatedAt(getCreationDate(dataResource)); + metadataRecord.setLastUpdate(dataResource.getLastUpdate()); PrimaryIdentifier pid = dataResource.getIdentifier(); if ((pid != null) && pid.hasDoi()) { metadataRecord.setPid(ResourceIdentifier.factoryResourceIdentifier(pid.getValue(), IdentifierType.valueOf(pid.getIdentifierType()))); } - Long recordVersion = 1L; - if (dataResource.getVersion() != null) { - recordVersion = Long.valueOf(dataResource.getVersion()); - } - metadataRecord.setRecordVersion(recordVersion); - - for (RelatedIdentifier relatedIds : dataResource.getRelatedIdentifiers()) { - LOG.trace("Found related Identifier: '{}'", relatedIds); - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(relatedIds.getValue()); - if (relatedIds.getIdentifierType() != null) { - resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); - } - LOG.trace("Set relation to '{}'", resourceIdentifier); - metadataRecord.setRelatedResource(resourceIdentifier); - } - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { - ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedIds.getValue(), IdentifierType.valueOf(relatedIds.getIdentifierType().name())); - metadataRecord.setSchema(resourceIdentifier); - if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { - //Try to fetch version from URL (only works with URLs including the version as query parameter. - Matcher matcher = Pattern.compile(".*[&?]version=(\\d*).*").matcher(resourceIdentifier.getIdentifier()); - while (matcher.find()) { - metadataRecord.setSchemaVersion(Long.valueOf(matcher.group(1))); - } - } else { - // set to current version of schema - SchemaRecord currentSchema = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(resourceIdentifier.getIdentifier()); - if (currentSchema != null) { - metadataRecord.setSchemaVersion(currentSchema.getVersion()); - } else { - metadataRecord.setSchemaVersion(1L); - } - } - LOG.trace("Set schema to '{}'", resourceIdentifier); - } - } + metadataRecord.setRecordVersion(getVersion(dataResource)); + + setSchemaAndVersion(metadataRecord, dataResource); + setRelatedResource(metadataRecord, dataResource); + if (metadataRecord.getSchema() == null) { String message = "Missing schema identifier for metadata document. Not a valid metadata document ID. Returning HTTP BAD_REQUEST."; LOG.error(message); @@ -550,6 +511,45 @@ public static MetadataRecord migrateToMetadataRecordV2(RepoBaseConfiguration app return metadataRecord; } + /** + * Migrate data resource to metadata record. + * + * @param applicationProperties Configuration settings of repository. + * @param dataResource Data resource to migrate. + * @return Metadata record of data resource. + */ + public static MetadataSchemaRecord migrateToMetadataSchemaRecordV2(RepoBaseConfiguration applicationProperties, + DataResource dataResource) { + MetadataSchemaRecord metadataSchemaRecord = new MetadataSchemaRecord(); + if (dataResource != null) { + metadataSchemaRecord.setSchemaId(dataResource.getId()); + metadataSchemaRecord.setAcl(dataResource.getAcls()); + + metadataSchemaRecord.setCreatedAt(getCreationDate(dataResource)); + metadataSchemaRecord.setLastUpdate(dataResource.getLastUpdate()); + + PrimaryIdentifier pid = dataResource.getIdentifier(); + if ((pid != null) && pid.hasDoi()) { + metadataSchemaRecord.setPid(ResourceIdentifier.factoryResourceIdentifier(pid.getValue(), IdentifierType.valueOf(pid.getIdentifierType()))); + } + metadataSchemaRecord.setSchemaVersion(getVersion(dataResource)); + + LOG.trace("Get document URI from ContentInformation."); + ContentInformation info; + info = getContentInformationOfResource(applicationProperties, dataResource); + if (info != null) { + metadataSchemaRecord.setSchemaHash(info.getHash()); + metadataSchemaRecord.setSchemaDocumentUri(info.getContentUri()); + } + // Only one license allowed. So don't worry about size of set. + if (!dataResource.getRights().isEmpty()) { + metadataSchemaRecord.setLicenseUri(dataResource.getRights().iterator().next().getSchemeUri()); + } + } + + return metadataSchemaRecord; + } + private static ContentInformation getContentInformationOfResource(RepoBaseConfiguration applicationProperties, DataResource dataResource) { ContentInformation returnValue = null; @@ -732,7 +732,7 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou } } if (!allSchemaIds.isEmpty()) { - specWithSchema = specWithSchema.and(RelatedIdentifierWithTypeSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, allSchemaIds.toArray(String[]::new))); + specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, allSchemaIds.toArray(String[]::new))); } } return specWithSchema; @@ -748,7 +748,7 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou public static Specification<DataResource> findByRelatedId(Specification<DataResource> specification, List<String> relatedIds) { Specification<DataResource> specWithSchema = specification; if ((relatedIds != null) && !relatedIds.isEmpty()) { - specWithSchema = specWithSchema.and(RelatedIdentifierWithTypeSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, relatedIds.toArray(String[]::new))); + specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, relatedIds.toArray(String[]::new))); } return specWithSchema; } @@ -1555,6 +1555,74 @@ public static final SchemaRecord createSchemaRecord(DataResource dataResource, C return schemaRecord; } + /** + * Get creation date of data resource. + * + * @param dataResource data resource. + * @return creation date. + */ + public static final Instant getCreationDate(DataResource dataResource) { + Instant creationDate = null; + for (edu.kit.datamanager.repo.domain.Date d : dataResource.getDates()) { + if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { + LOG.trace("Creation date entry found."); + creationDate = d.getValue(); + break; + } + } + return creationDate; + } + + /** + * Get version of data resource. + * + * @param dataResource data resource. + * @return version or 1 if no version is available. + */ + public static final Long getVersion(DataResource dataResource) { + Long recordVersion = 1L; + if (dataResource.getVersion() != null) { + recordVersion = Long.valueOf(dataResource.getVersion()); + } + return recordVersion; + } + + public static final void setSchemaAndVersion(MetadataRecord metadataRecord, DataResource dataResource) { + RelatedIdentifier relatedId = getSchemaIdentifier(dataResource); + if (relatedId != null) { + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedId.getValue(), IdentifierType.valueOf(relatedId.getIdentifierType().name())); + metadataRecord.setSchema(resourceIdentifier); + if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { + //Try to fetch version from URL (only works with URLs including the version as query parameter. + Matcher matcher = Pattern.compile(".*[&?]version=(\\d*).*").matcher(resourceIdentifier.getIdentifier()); + while (matcher.find()) { + metadataRecord.setSchemaVersion(Long.valueOf(matcher.group(1))); + } + } else { + // set to current version of schema + SchemaRecord currentSchema = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(resourceIdentifier.getIdentifier()); + if (currentSchema != null) { + metadataRecord.setSchemaVersion(currentSchema.getVersion()); + } else { + metadataRecord.setSchemaVersion(1L); + } + } + LOG.trace("Set schema to '{}'", resourceIdentifier); + } + } + + public static final void setRelatedResource(MetadataRecord metadataRecord, DataResource dataResource) { + RelatedIdentifier relatedId = getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + if (relatedId != null) { + ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(relatedId.getValue()); + if (relatedId.getIdentifierType() != null) { + resourceIdentifier = ResourceIdentifier.factoryResourceIdentifier(relatedId.getValue(), IdentifierType.valueOf(relatedId.getIdentifierType().name())); + } + LOG.trace("Set relation to '{}'", resourceIdentifier); + metadataRecord.setRelatedResource(resourceIdentifier); + } + } + /** * Get String (URL) for accessing schema document via schemaId and version. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java index 249bd67d..81ea1ca5 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/LandingPageControllerImplV2.java @@ -18,7 +18,9 @@ import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; +import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.fixSchemaUrl; import edu.kit.datamanager.metastore2.web.ILandingPageControllerV2; import edu.kit.datamanager.repo.domain.DataResource; import io.swagger.v3.oas.annotations.media.Schema; @@ -89,10 +91,11 @@ public String getLandingPageOfSchemaWithId(@RequestParam(value = "schemaId") Str } LOG.trace("Fix URL for all schema records"); - List<DataResource> metadataList = new ArrayList<>(); + List<MetadataSchemaRecord> metadataList = new ArrayList<>(); recordList.forEach(metadataRecord -> { - DataResourceRecordUtil.fixSchemaUrl(metadataRecord); - metadataList.add(metadataRecord); + MetadataSchemaRecord metadataSchemaRecord = DataResourceRecordUtil.migrateToMetadataSchemaRecordV2(schemaConfig, metadataRecord); + metadataSchemaRecord.setSchemaDocumentUri(DataResourceRecordUtil.getSchemaDocumentUri(id, metadataSchemaRecord.getSchemaVersion())); + metadataList.add(metadataSchemaRecord); }); model.addAttribute("records", metadataList); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java index fa90dd94..f317799c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTest.java @@ -2139,6 +2139,46 @@ public void testLandingPage4Metadata() throws Exception { .andExpect(status().isOk()); } + @Test + public void testLandingPage4Schema() throws Exception { + + MvcResult andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + SCHEMA_ID) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + SCHEMA_ID + "&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + SCHEMA_ID) + .queryParam("version", "1") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + SCHEMA_ID + "&version=1")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get("/api/v1/schemas/" + SCHEMA_ID) + .queryParam("version", "2") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page?schemaId=" + SCHEMA_ID + "&version=2")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + @Test public void testDeleteSchemaWithLinkedDocument() throws Exception { String schemaId = "deleteschema"; diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index ad9305cc..99743c71 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -101,6 +101,7 @@ @TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/v2/md/metadata"}) @TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) @TestPropertySource(properties = {"metastore.metadata.landingpage=/metadata-landing-page-v2?id=$(id)&version=$(version)"}) +@TestPropertySource(properties = {"metastore.schema.landingpage=/schema-landing-page-v2?schemaId=$(id)&version=$(version)"}) @TestPropertySource(properties = {"repo.search.url=http://localhost:41421"}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) public class MetadataControllerTestV2 { @@ -2576,6 +2577,46 @@ public void testLandingPage4Metadata() throws Exception { .andExpect(status().isOk()); } + @Test + public void testLandingPage4Schema() throws Exception { + + MvcResult andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + SCHEMA_ID) + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page-v2?schemaId=" + SCHEMA_ID + "&version=")) + .andReturn(); + String redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + SCHEMA_ID) + .queryParam("version", "1") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page-v2?schemaId=" + SCHEMA_ID + "&version=1")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isOk()); + andReturn = this.mockMvc.perform(get(API_SCHEMA_PATH + SCHEMA_ID) + .queryParam("version", "2") + .accept("text/html")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/schema-landing-page-v2?schemaId=" + SCHEMA_ID + "&version=2")) + .andReturn(); + redirectedUrl = andReturn.getResponse().getRedirectedUrl(); + this.mockMvc.perform(get(redirectedUrl) + .accept("text/html")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + @Test public void testDeleteSchemaWithLinkedDocument() throws Exception { String schemaId = "deleteschema"; From 262ae15894b4492ad595ac21b64fd31883bd73dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:37:47 +0000 Subject: [PATCH 137/181] Update dependency gradle to v8.11 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72b..94113f20 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From e39c66df6119dab5595e81006a23438e636206af Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 11 Nov 2024 17:48:03 +0100 Subject: [PATCH 138/181] Refactor code. --- .../util/DataResourceRecordUtil.java | 348 +++++++++++------- .../web/impl/MetadataControllerImpl.java | 17 +- .../web/impl/MetadataControllerImplV2.java | 16 +- .../impl/SchemaRegistryControllerImpl.java | 23 +- .../impl/SchemaRegistryControllerImplV2.java | 21 +- 5 files changed, 225 insertions(+), 200 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 71730434..06a943ad 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -70,6 +70,7 @@ import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.JSON; import static edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord.SCHEMA_TYPE.XML; +import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.domain.Date; import java.time.Instant; @@ -156,10 +157,10 @@ public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean curren Iterator<AclEntry> iterator = aclEntries.iterator(); while (iterator.hasNext()) { AclEntry aclEntry = iterator.next(); - LOG.trace("'{}' has \u2019{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); + LOG.trace("'{}' has '{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) && authorizationIdentities.contains(aclEntry.getSid())) { isAllowed = true; - LOG.trace("Confirm permission for updating ACL: '{}' has \u2019{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); + LOG.trace("Confirm permission for updating ACL: '{}' has '{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); break; } } @@ -319,11 +320,10 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur LOG.trace("Obtaining most recent datacite record with id {}.", resourceId); DataResource oldDataResource = applicationProperties.getDataResourceService().findById(resourceId); - LOG.trace("Checking provided ETag."); ControllerUtils.checkEtag(eTag, oldDataResource); LOG.trace("ETag: '{}'", oldDataResource.getEtag()); - updatedDataResource = mergeDataResource(oldDataResource, givenDataResource); - updatedDataResource = fixRelatedSchemaIfNeeded(updatedDataResource); + DataResource mergedDataResource = mergeDataResource(oldDataResource, givenDataResource); + updatedDataResource = fixRelatedSchemaIfNeeded(mergedDataResource); boolean noChanges = false; if (document != null) { @@ -331,31 +331,11 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur validateMetadataDocument(applicationProperties, document, schemaRecord); ContentInformation info; - String fileName = document.getOriginalFilename(); + String fileName; info = getContentInformationOfResource(applicationProperties, updatedDataResource); - if (info != null) { - fileName = info.getRelativePath(); - noChanges = true; - // Check for changes... - try { - byte[] currentFileContent; - File file = new File(URI.create(info.getContentUri())); - if (document.getSize() == Files.size(file.toPath())) { - currentFileContent = FileUtils.readFileToByteArray(file); - byte[] newFileContent = document.getBytes(); - for (int index = 0; index < currentFileContent.length; index++) { - if (currentFileContent[index] != newFileContent[index]) { - noChanges = false; - break; - } - } - } else { - noChanges = false; - } - } catch (IOException ex) { - LOG.error("Error reading current file!", ex); - } - } + fileName = (info != null) ? info.getRelativePath() : document.getOriginalFilename(); + noChanges = checkDocumentForChanges(info, document); + if (!noChanges) { // Everything seems to be fine update document and increment version LOG.trace("Updating schema document (and increment version)..."); @@ -550,6 +530,22 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecordV2(RepoBaseConfi return metadataSchemaRecord; } + private static Long determineVersionFromResourceIdentifier(ResourceIdentifier resourceIdentifier) { + Long version = 1L; + if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { + //Try to fetch version from URL (only works with URLs including the version as query parameter. + Matcher matcher = Pattern.compile(".*[&?]version=(\\d*).*").matcher(resourceIdentifier.getIdentifier()); + while (matcher.find()) { + version = Long.valueOf(matcher.group(1)); + } + } else { + // set to current version of schema + SchemaRecord currentSchema = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(resourceIdentifier.getIdentifier()); + version = (currentSchema != null) ? currentSchema.getVersion() : 1L; + } + return version; + } + private static ContentInformation getContentInformationOfResource(RepoBaseConfiguration applicationProperties, DataResource dataResource) { ContentInformation returnValue = null; @@ -712,6 +708,26 @@ public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration meta return metadataDocumentPath; } + public static Specification<DataResource> findByAccessRights(Specification<DataResource> spec) { + // Add authentication if enabled + if (schemaConfig.isAuthEnabled()) { + boolean isAdmin; + isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); + // Add authorization for non administrators + if (!isAdmin) { + List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); + if (authorizationIdentities != null) { + LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); + Specification<DataResource> permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); + spec = spec.and(permissionSpec); + } else { + LOG.trace("No permission information provided. Skip creating permission specification."); + } + } + } + return spec; + } + /** * Create specification for all listed schemaIds. * @@ -957,20 +973,29 @@ public static void checkLicense(DataResource dataResource, String licenseUri) { public static void validateRelatedResources4MetadataDocuments(DataResource dataResource) throws BadArgumentException { int noOfRelatedData = 0; int noOfRelatedSchemas = 0; - String message = "Invalid related resources! Expected '1' related resource found '%d'. Expected '1' related schema found '%d'!"; if (dataResource != null) { Set<RelatedIdentifier> relatedResources = dataResource.getRelatedIdentifiers(); // Check if related resource already exists (only one related resource of type isMetadataFor allowed) for (RelatedIdentifier item : relatedResources) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR)) { - noOfRelatedData++; - } - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { - noOfRelatedSchemas++; + switch (item.getRelationType()) { + case IS_METADATA_FOR: + noOfRelatedData++; + break; + case HAS_METADATA: + noOfRelatedSchemas++; + break; + default: } } } + checkNoOfRelatedIdentifiers(noOfRelatedData, noOfRelatedSchemas); + } + + private static void checkNoOfRelatedIdentifiers(int noOfRelatedData, int noOfRelatedSchemas) { + String message; + message = "Invalid related resources! Expected '1' related resource found '%d'. Expected '1' related schema found '%d'!"; + if (noOfRelatedData != 1 || noOfRelatedSchemas != 1) { String errorMessage = ""; if (noOfRelatedData == 0) { @@ -1197,16 +1222,22 @@ private static DataResource checkParameters(MultipartFile dataResourceRecord, Mu } // Do some checks first. if (!recordNotAvailable) { - try { - metadataRecord = Json.mapper().readValue(dataResourceRecord.getInputStream(), DataResource.class); - } catch (IOException ex) { - message = "Can't map record document to DataResource"; - if (ex instanceof JsonParseException) { - message = message + " Reason: " + ex.getMessage(); - } - LOG.error(ERROR_PARSING_JSON, ex); - throw new BadArgumentException(message); + metadataRecord = getDataResourceFromBody(dataResourceRecord); + } + return metadataRecord; + } + + private static DataResource getDataResourceFromBody(MultipartFile dataResourceRecord) { + DataResource metadataRecord = null; + try { + metadataRecord = Json.mapper().readValue(dataResourceRecord.getInputStream(), DataResource.class); + } catch (IOException ex) { + String message = "Can't map record document to DataResource"; + if (ex instanceof JsonParseException) { + message = message + " Reason: " + ex.getMessage(); } + LOG.error(ERROR_PARSING_JSON, ex); + throw new BadArgumentException(message); } return metadataRecord; } @@ -1332,74 +1363,69 @@ public static DataResource updateDataResource4SchemaDocument(MetastoreConfigurat ControllerUtils.checkEtag(eTag, oldDataResource); LOG.trace("ETag: '{}'", oldDataResource.getEtag()); updatedDataResource = mergeDataResource(oldDataResource, givenDataResource); + if (schemaDocument != null) { + updateSchemaDocument(applicationProperties, updatedDataResource, schemaDocument, supplier); + } else { + updateOnlyMetadata4SchemaDocument(applicationProperties, updatedDataResource); + } + updatedDataResource = DataResourceUtils.updateResource(applicationProperties, updatedDataResource.getId(), updatedDataResource, eTag, supplier); + + return updatedDataResource; + } + + private static void updateSchemaDocument(MetastoreConfiguration applicationProperties, + DataResource updatedDataResource, + MultipartFile schemaDocument, + UnaryOperator<String> supplier) { ContentInformation info; info = getContentInformationOfResource(applicationProperties, updatedDataResource); - if (schemaDocument != null) { - // Get schema record for this schema - validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDocument); + // Get schema record for this schema + validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDocument); - boolean noChanges = false; - String fileName = schemaDocument.getOriginalFilename(); - if (info != null) { - noChanges = true; - fileName = info.getRelativePath(); - // Check for changes... - try { - byte[] currentFileContent; - File file = new File(URI.create(info.getContentUri())); - if (schemaDocument.getSize() == Files.size(file.toPath())) { - currentFileContent = FileUtils.readFileToByteArray(file); - byte[] newFileContent = schemaDocument.getBytes(); - for (int index = 0; index < currentFileContent.length; index++) { - if (currentFileContent[index] != newFileContent[index]) { - noChanges = false; - break; - } - } - } else { - noChanges = false; - } - } catch (IOException ex) { - LOG.error("Error reading current file!", ex); - throw new BadArgumentException("Error reading schema document!"); - } - } - if (!noChanges) { - // Everything seems to be fine update document and increment version - LOG.trace("Updating schema document (and increment version)..."); - String version = updatedDataResource.getVersion(); - if (version != null) { - updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); - } - addProvenance(updatedDataResource); - ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, updatedDataResource, schemaDocument, fileName, null, true, supplier); - SchemaRecord schemaRecord = createSchemaRecord(updatedDataResource, contentInformation); - MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); - } - } else { - // validate if document is still valid due to changed record settings. - Objects.requireNonNull(info); - URI schemaDocumentUri = URI.create(info.getContentUri()); - - Path schemaDocumentPath = Paths.get(schemaDocumentUri); - if (!Files.exists(schemaDocumentPath) - || !Files.isRegularFile(schemaDocumentPath) - || !Files.isReadable(schemaDocumentPath)) { - LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); - throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); - } + boolean noChanges = false; + String fileName; - try { - byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); - validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDoc); - } catch (IOException ex) { - LOG.error("Error validating file!", ex); + fileName = (info != null) ? info.getRelativePath() : schemaDocument.getOriginalFilename(); + noChanges = checkDocumentForChanges(info, schemaDocument); + + if (!noChanges) { + // Everything seems to be fine update document and increment version + LOG.trace("Updating schema document (and increment version)..."); + String version = updatedDataResource.getVersion(); + if (version != null) { + updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); } + addProvenance(updatedDataResource); + ContentInformation contentInformation = ContentDataUtils.addFile(applicationProperties, updatedDataResource, schemaDocument, fileName, null, true, supplier); + SchemaRecord schemaRecord = createSchemaRecord(updatedDataResource, contentInformation); + MetadataSchemaRecordUtil.saveNewSchemaRecord(schemaRecord); + } + + } + private static void updateOnlyMetadata4SchemaDocument(MetastoreConfiguration applicationProperties, + DataResource updatedDataResource) { + ContentInformation info; + info = getContentInformationOfResource(applicationProperties, updatedDataResource); + // validate if document is still valid due to changed record settings. + Objects.requireNonNull(info); + URI schemaDocumentUri = URI.create(info.getContentUri()); + + Path schemaDocumentPath = Paths.get(schemaDocumentUri); + if (!Files.exists(schemaDocumentPath) + || !Files.isRegularFile(schemaDocumentPath) + || !Files.isReadable(schemaDocumentPath)) { + LOG.warn("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); + throw new CustomInternalServerError("Schema document on server either does not exist or is no file or is not readable."); + } + + try { + byte[] schemaDoc = Files.readAllBytes(schemaDocumentPath); + validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDoc); + } catch (IOException ex) { + LOG.error("Error validating file!", ex); } - updatedDataResource = DataResourceUtils.updateResource(applicationProperties, updatedDataResource.getId(), updatedDataResource, eTag, supplier); - return updatedDataResource; } private static DataResource mergeDataResource(DataResource oldDataResource, DataResource givenDataResource) { @@ -1410,15 +1436,49 @@ private static DataResource mergeDataResource(DataResource oldDataResource, Data givenDataResource.setVersion(oldDataResource.getVersion()); givenDataResource.setId(oldDataResource.getId()); updatedDataResource = givenDataResource; + mergeCreators(oldDataResource, updatedDataResource); + mergePublicationYear(oldDataResource, updatedDataResource); + mergePublisher(oldDataResource, updatedDataResource); + mergeAcl(oldDataResource, updatedDataResource); + mergeRights(oldDataResource, updatedDataResource); + mergeState(oldDataResource, updatedDataResource); + mergeResourceType(oldDataResource, updatedDataResource); + mergeCreateDate(oldDataResource, updatedDataResource); + } else { + updatedDataResource = DataResourceUtils.copyDataResource(oldDataResource); + } + return updatedDataResource; + } + + private static DataResource mergeCreators(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null) { if ((updatedDataResource.getCreators() == null) || updatedDataResource.getCreators().isEmpty()) { updatedDataResource.setCreators(oldDataResource.getCreators()); + } else { + LOG.trace("Update creators!"); } - if (updatedDataResource.getPublicationYear() == null) { - updatedDataResource.setPublicationYear(oldDataResource.getPublicationYear()); - } - if (updatedDataResource.getPublisher() == null) { - updatedDataResource.setPublisher(oldDataResource.getPublisher()); - } + } + return updatedDataResource; + } + + private static DataResource mergePublicationYear(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null && updatedDataResource.getPublicationYear() == null) { + updatedDataResource.setPublicationYear(oldDataResource.getPublicationYear()); + + } + return updatedDataResource; + } + + private static DataResource mergePublisher(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null && updatedDataResource.getPublisher() == null) { + updatedDataResource.setPublisher(oldDataResource.getPublisher()); + + } + return updatedDataResource; + } + + private static DataResource mergeAcl(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null) { if ((updatedDataResource.getAcls() == null) || updatedDataResource.getAcls().isEmpty()) { updatedDataResource.setAcls(oldDataResource.getAcls()); } else { @@ -1427,16 +1487,39 @@ private static DataResource mergeDataResource(DataResource oldDataResource, Data // Check if access rights still valid afterwards (at least one user with ADMINISTRATION rights should be available!) checkAccessRights(updatedDataResource.getAcls(), false); } + } + return updatedDataResource; + } + private static DataResource mergeRights(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null) { if (updatedDataResource.getRights() == null) { updatedDataResource.setRights(new HashSet<>()); } + } + return updatedDataResource; + } + + private static DataResource mergeState(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null) { if (updatedDataResource.getState() == null) { updatedDataResource.setState(oldDataResource.getState()); } + } + return updatedDataResource; + } + + private static DataResource mergeResourceType(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null) { if (updatedDataResource.getResourceType() == null) { updatedDataResource.setResourceType(oldDataResource.getResourceType()); } + } + return updatedDataResource; + } + + private static DataResource mergeCreateDate(DataResource oldDataResource, DataResource updatedDataResource) { + if (updatedDataResource != null) { // Set create date Date createDate = null; for (Date date : oldDataResource.getDates()) { @@ -1454,12 +1537,38 @@ private static DataResource mergeDataResource(DataResource oldDataResource, Data updatedDataResource.getDates().remove(newCreateDate); } updatedDataResource.getDates().add(createDate); - } else { - updatedDataResource = DataResourceUtils.copyDataResource(oldDataResource); } return updatedDataResource; } + public static boolean checkDocumentForChanges(ContentInformation info, MultipartFile document) { + boolean noChanges = true; + if (info != null) { + // Check for changes... + try { + byte[] currentFileContent; + File file = new File(URI.create(info.getContentUri())); + if (document.getSize() == Files.size(file.toPath())) { + currentFileContent = FileUtils.readFileToByteArray(file); + byte[] newFileContent = document.getBytes(); + for (int index = 0; index < currentFileContent.length; index++) { + if (currentFileContent[index] != newFileContent[index]) { + noChanges = false; + break; + } + } + } else { + noChanges = false; + } + } catch (IOException ex) { + LOG.error("Error reading current file!", ex); + throw new BadArgumentException("Error reading schema document!"); + } + } + return noChanges; + } + // Check for changes... + /** * Migrate schema from INTERNAL type to URL type (if necessary) * @@ -1662,21 +1771,6 @@ public static Page<DataResource> queryDataResources(Specification spec, Pageable Page<DataResource> records = null; try { records = spec != null ? dataResourceDao.findAll(spec, pgbl) : dataResourceDao.findAll(pgbl); - if (LOG.isTraceEnabled()) { - if (spec != null) { - LOG.trace("Query data resources with spec '{}'", spec); - } else { - LOG.trace("Query all data resources..."); - } - LOG.trace("-----------------------------------------------"); - LOG.trace("List '{}' of '{}' data resources in total!", records.getContent().size(), records.getTotalElements()); - LOG.trace("-----------------------------------------------"); - int itemNo = 1; - for (DataResource item : records.getContent()) { - LOG.trace("#{} - '{}'- Version: '{}'", itemNo++, item.getId(), item.getVersion()); - } - LOG.trace("-----------------------------------------------"); - } } catch (Exception ex) { LOG.error("Error finding data resource records by specification!", ex); throw ex; diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index d0341268..da2e8de6 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -16,7 +16,6 @@ package edu.kit.datamanager.metastore2.web.impl; import com.fasterxml.jackson.core.JsonParseException; -import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.entities.messaging.MetadataResourceMessage; import edu.kit.datamanager.exceptions.AccessForbiddenException; @@ -342,21 +341,7 @@ public ResponseEntity<List<MetadataRecord>> getRecords( Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResourceRecordUtil.METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); // Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE)); // Add authentication if enabled - if (metadataConfig.isAuthEnabled()) { - boolean isAdmin; - isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); - // Add authorization for non administrators - if (!isAdmin) { - List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - if (authorizationIdentities != null) { - LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); - Specification<DataResource> permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); - spec = spec.and(permissionSpec); - } else { - LOG.trace("No permission information provided. Skip creating permission specification."); - } - } - } + spec = DataResourceRecordUtil.findByAccessRights(spec); List<String> allRelatedIdentifiersSchema = new ArrayList<>(); List<String> allRelatedIdentifiersResource = new ArrayList<>(); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index 6ee07070..b3da72fa 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -395,21 +395,7 @@ public ResponseEntity<List<DataResource>> getRecords( // Search for resource type of MetadataSchemaRecord Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResourceRecordUtil.METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); // Add authentication if enabled - if (metadataConfig.isAuthEnabled()) { - boolean isAdmin; - isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); - // Add authorization for non administrators - if (!isAdmin) { - List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - if (authorizationIdentities != null) { - LOG.trace("Creating (READ) permission specification. '{}'", authorizationIdentities); - Specification<DataResource> permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); - spec = spec.and(permissionSpec); - } else { - LOG.trace("No permission information provided. Skip creating permission specification."); - } - } - } + spec = DataResourceRecordUtil.findByAccessRights(spec); spec = DataResourceRecordUtil.findBySchemaId(spec, schemaIds); spec = DataResourceRecordUtil.findByRelatedId(spec, relatedIds); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index 4be35771..bafa1db0 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -15,8 +15,6 @@ */ package edu.kit.datamanager.metastore2.web.impl; -import edu.kit.datamanager.entities.PERMISSION; -import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.exceptions.ResourceNotFoundException; import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; @@ -275,7 +273,7 @@ public ResponseEntity<List<MetadataSchemaRecord>> getRecords(@RequestParam(value } Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); // Add authentication if enabled - spec = addAuthenticationSpecification(spec); + spec = DataResourceRecordUtil.findByAccessRights(spec); //one of given mimetypes. if ((mimeTypes != null) && !mimeTypes.isEmpty()) { spec = spec.and(TitleSpec.toSpecification(mimeTypes.toArray(new String[mimeTypes.size()]))); @@ -360,23 +358,4 @@ public void contribute(Info.Builder builder) { LOG.trace("Check for SchemaRepo actuator information (v1)..."); LOG.trace("Check for SchemaRepo actuator information (v1) disabled!"); } - - private Specification<DataResource> addAuthenticationSpecification(Specification<DataResource> spec) { - if (schemaConfig.isAuthEnabled()) { - boolean isAdmin; - isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); - // Add authorization for non administrators - if (!isAdmin) { - List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - if (authorizationIdentities != null) { - LOG.trace("Creating (READ) permission specification."); - Specification<DataResource> permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); - spec = spec.and(permissionSpec); - } else { - LOG.trace("No permission information provided. Skip creating permission specification."); - } - } - } - return spec; - } } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 96675bf5..53bdcc81 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -306,7 +306,7 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche } Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); // Add authentication if enabled - spec = addAuthenticationSpecification(spec); + spec = DataResourceRecordUtil.findByAccessRights(spec); if ((updateFrom != null) || (updateUntil != null)) { spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); } @@ -379,25 +379,6 @@ public void contribute(Info.Builder builder) { } } - private Specification<DataResource> addAuthenticationSpecification(Specification<DataResource> spec) { - if (schemaConfig.isAuthEnabled()) { - boolean isAdmin; - isAdmin = AuthenticationHelper.hasAuthority(RepoUserRole.ADMINISTRATOR.toString()); - // Add authorization for non administrators - if (!isAdmin) { - List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - if (authorizationIdentities != null) { - LOG.trace("Creating (READ) permission specification."); - Specification<DataResource> permissionSpec = PermissionSpecification.toSpecification(authorizationIdentities, PERMISSION.READ); - spec = spec.and(permissionSpec); - } else { - LOG.trace("No permission information provided. Skip creating permission specification."); - } - } - } - return spec; - } - /** * Get URI for accessing schema document via schemaId and version. * From 2d7800fc36dcb03b4f3d407eef43d39d9e7048f7 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 12 Nov 2024 15:56:00 +0100 Subject: [PATCH 139/181] Refactor code. --- .../metastore2/dao/ISchemaRecordDao.java | 4 - .../metastore2/domain/ElasticWrapper.java | 2 +- .../metastore2/domain/ResourceIdentifier.java | 4 +- .../metastore2/domain/SchemaRecord.java | 1 - .../service/MetastoreOAIPMHRepository.java | 1 - .../metastore2/oaipmh/util/OAIPMHBuilder.java | 42 +-- .../runner/ElasticIndexerRunner.java | 2 +- .../util/DataResourceRecordUtil.java | 249 ++++++++++-------- .../metastore2/util/MetadataRecordUtil.java | 201 +++++--------- .../util/MetadataSchemaRecordUtil.java | 159 ++++++----- .../metastore2/validation/IValidator.java | 1 + .../web/impl/MetadataControllerImpl.java | 45 +--- .../web/impl/MetadataControllerImplV2.java | 55 +--- .../web/impl/MetadataSearchController.java | 1 + .../impl/SchemaRegistryControllerImpl.java | 30 +-- .../impl/SchemaRegistryControllerImplV2.java | 37 +-- 16 files changed, 330 insertions(+), 504 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java index fb977ea5..67392f3f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java +++ b/src/main/java/edu/kit/datamanager/metastore2/dao/ISchemaRecordDao.java @@ -33,12 +33,8 @@ public interface ISchemaRecordDao extends JpaRepository<SchemaRecord, String>, J SchemaRecord findBySchemaId(String schemaIdWithVersion); -// SchemaRecord findBySchemaIdAndVersion(String schemaId, Long version); - SchemaRecord findByAlternateId(String alternateId); -// List<SchemaRecord> findBySchemaIdOrderByVersionDesc(String schemaId); - List<SchemaRecord> findBySchemaIdStartsWithOrderByVersionDesc(String schemaId); SchemaRecord findFirstBySchemaIdStartsWithOrderByVersionDesc(String schemaId); diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java index 5485bdaf..db8d2f1e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java @@ -73,7 +73,7 @@ public ElasticWrapper(DataResource resource) { id = resource.getId(); pid = (resource.getIdentifier() != null) ? resource.getIdentifier().getValue() : null; metadataRecord = resource; - read = new HashSet<String>(); + read = new HashSet<>(); resource.getAcls().forEach(entry -> { String sid = entry.getSid(); if (entry.getPermission().atLeast(PERMISSION.READ)) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java b/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java index 168e983e..d956256b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/ResourceIdentifier.java @@ -29,7 +29,7 @@ public class ResourceIdentifier implements Serializable { @Override public String toString() { - String sb = ResourceIdentifier.class.getName() + '@' + Integer.toHexString(System.identityHashCode(this)) + '[' + + return ResourceIdentifier.class.getName() + '@' + Integer.toHexString(System.identityHashCode(this)) + '[' + "resourceIdentifier" + '=' + ((this.getIdentifier() == null) ? "<null>" : this.getIdentifier()) + @@ -38,8 +38,6 @@ public String toString() { '=' + ((this.getIdentifierType() == null) ? "<null>" : this.getIdentifierType()) + ']'; - - return sb; } @Override diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index 668eecee..2ad1d225 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -54,7 +54,6 @@ public class SchemaRecord implements Serializable { public String getSchemaIdWithoutVersion() { String pureSchemaId = null; if (schemaId != null) { - String[] split = schemaId.split("/"); pureSchemaId = schemaId.split("/")[0]; } return pureSchemaId; diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java index 141c224b..74b0bf71 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java @@ -195,7 +195,6 @@ public void identify(OAIPMHBuilder builder) { public void listSets(OAIPMHBuilder builder) { LOGGER.trace("Performing listSets()."); //@TODO support collections? - //builder.addSet("default", "default"); builder.addError(OAIPMHerrorcodeType.NO_SET_HIERARCHY, "Sets are currently not supported."); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java index cf000349..88bc871a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/util/OAIPMHBuilder.java @@ -222,7 +222,28 @@ public OAIPMHBuilder addRecord(String identifier, Date recordDatestamp, List<Str } switch (verb) { case GET_RECORD: - case LIST_RECORDS: { + case LIST_RECORDS: + getOrListRecords(identifier, recordDatestamp, setSpecs, metadata, about); + break; + case LIST_IDENTIFIERS: { + HeaderType header = new HeaderType(); + if (identifier == null || recordDatestamp == null) { + throw new IllegalArgumentException("Arguments identifier and recordDatestamp must not be null."); + } + header.setIdentifier(identifier); + header.setDatestamp(repository.getDateFormat().format(recordDatestamp)); + // header.setStatus(StatusType.DELETED); --> not supported yet + header.getSetSpec().addAll(setSpecs); + listIdentifiers.getHeader().add(header); + break; + } + default: + // no action required + } + return this; + } + + private void getOrListRecords(String identifier, Date recordDatestamp, List<String> setSpecs, Object metadata, Object about) { RecordType recordType = new RecordType(); HeaderType header = new HeaderType(); if (identifier == null || recordDatestamp == null) { @@ -252,25 +273,6 @@ public OAIPMHBuilder addRecord(String identifier, Date recordDatestamp, List<Str } else { listRecordsType.getRecord().add(recordType); } - - break; - } - case LIST_IDENTIFIERS: { - HeaderType header = new HeaderType(); - if (identifier == null || recordDatestamp == null) { - throw new IllegalArgumentException("Arguments identifier and recordDatestamp must not be null."); - } - header.setIdentifier(identifier); - header.setDatestamp(repository.getDateFormat().format(recordDatestamp)); - // header.setStatus(StatusType.DELETED); --> not supported yet - header.getSetSpec().addAll(setSpecs); - listIdentifiers.getHeader().add(header); - break; - } - default: - // no action required - } - return this; } public OAIPMHBuilder addError(OAIPMHerrorcodeType code, String message) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 7d5cf245..f314dec2 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -294,7 +294,7 @@ private void indexAlternativeSchemaIds(String index, String baseUrl) { templateRecord.setMetadataId(item.getMetadataId()); templateRecord.setVersion(item.getVersion()); MetadataRecord result = toMetadataRecord(templateRecord, baseUrl); - LOG.trace("Sending CREATE event."); + LOG.trace("Sending CREATE event (alternativeSchemaId: '{}').", index); messagingService.orElse(new LogfileMessagingService()). send(MetadataResourceMessage.factoryCreateMetadataMessage(result, this.getClass().toString(), ControllerUtils.getLocalHostname())); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 06a943ad..cc1f42a9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -73,6 +73,7 @@ import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.RelatedIdentifierSpec; import edu.kit.datamanager.repo.domain.Date; +import java.nio.charset.Charset; import java.time.Instant; /** @@ -96,12 +97,6 @@ public class DataResourceRecordUtil { private static final Logger LOG = LoggerFactory.getLogger(DataResourceRecordUtil.class); private static final String LOG_ERROR_READ_METADATA_DOCUMENT = "Failed to read metadata document from input stream."; - private static final String LOG_SEPARATOR = "-----------------------------------------"; - private static final String LOG_SCHEMA_REGISTRY = "No external schema registries defined. Try to use internal one..."; - private static final String LOG_FETCH_SCHEMA = "Try to fetch schema from '{}'."; - private static final String PATH_SCHEMA = "schemas"; - private static final String LOG_ERROR_ACCESS = "Failed to access schema registry at '%s'. Proceeding with next registry."; - private static final String LOG_SCHEMA_RECORD = "Found schema record: '{}'"; private static final String ERROR_PARSING_JSON = "Error parsing json: "; private static MetastoreConfiguration schemaConfig; @@ -135,42 +130,31 @@ public class DataResourceRecordUtil { * @return Allowed (true) or not. */ public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean currentAcl) { - boolean isAllowed = false; + boolean isAllowed = true; String errorMessage1 = "Error invalid ACL! Reason: Only ADMINISTRATORS are allowed to change ACL entries."; String errorMessage2 = "Error invalid ACL! Reason: You are not allowed to revoke your own administrator rights."; - Authentication authentication = AuthenticationHelper.getAuthentication(); - List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); - for (GrantedAuthority authority : authentication.getAuthorities()) { - authorizationIdentities.add(authority.getAuthority()); - } - if (authorizationIdentities.contains(RepoUserRole.ADMINISTRATOR.getValue())) { - //ROLE_ADMINISTRATOR detected -> no further permission check necessary. - return true; - } - if (LOG.isTraceEnabled()) { - LOG.trace("Check access rights for changing ACL list!"); - for (String authority : authorizationIdentities) { - LOG.trace("Indentity/Authority: '{}'", authority); - } - } - // Check if authorized user still has ADMINISTRATOR rights - Iterator<AclEntry> iterator = aclEntries.iterator(); - while (iterator.hasNext()) { - AclEntry aclEntry = iterator.next(); - LOG.trace("'{}' has '{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); - if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) && authorizationIdentities.contains(aclEntry.getSid())) { - isAllowed = true; - LOG.trace("Confirm permission for updating ACL: '{}' has '{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); - break; + List<String> authorizationIdentities = getAllAuthorizationIdentities(); + // No authentication enabled or + //ROLE_ADMINISTRATOR detected -> no further permission check necessary. + if (schemaConfig.isAuthEnabled() && !authorizationIdentities.contains(RepoUserRole.ADMINISTRATOR.getValue())) { + isAllowed = false; + // Check if authorized user still has ADMINISTRATOR rights + Iterator<AclEntry> iterator = aclEntries.iterator(); + while (iterator.hasNext()) { + AclEntry aclEntry = iterator.next(); + LOG.trace("'{}' has '{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); + if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) && authorizationIdentities.contains(aclEntry.getSid())) { + isAllowed = true; + LOG.trace("Confirm permission for updating ACL: '{}' has '{}' rights!", aclEntry.getSid(), PERMISSION.ADMINISTRATE); + break; + } } - } - if (!isAllowed) { - String errorMessage = currentAcl ? errorMessage1 : errorMessage2; - LOG.warn(errorMessage); - if (schemaConfig.isAuthEnabled()) { + if (!isAllowed) { if (currentAcl) { + LOG.warn(errorMessage1); throw new AccessForbiddenException(errorMessage1); } else { + LOG.warn(errorMessage2); throw new BadArgumentException(errorMessage2); } } @@ -232,7 +216,7 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio MetadataFormat metadataFormat = new MetadataFormat(); metadataFormat.setMetadataPrefix(schemaRecord.getSchemaIdWithoutVersion()); metadataFormat.setSchema(schemaRecord.getAlternateId()); - String documentString = new String(document.getBytes()); + String documentString = new String(document.getBytes(), Charset.defaultCharset()); LOG.trace(documentString); String metadataNamespace = SchemaUtils.getTargetNamespaceFromSchema(document.getBytes()); metadataFormat.setMetadataNamespace(metadataNamespace); @@ -255,7 +239,6 @@ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguratio * @param applicationProperties Settings of repository. * @param recordDocument Record of the metadata. * @param document Schema document. - * @param getSchemaDocumentById Method for creating access URL. * @return Record of registered schema document. */ public static DataResource createDataResourceRecord4Metadata(MetastoreConfiguration applicationProperties, @@ -272,7 +255,7 @@ public static DataResource createDataResourceRecord4Metadata(MetastoreConfigurat } // End of parameter checks // Fix internal references, of necessary - dataResource = fixRelatedSchemaIfNeeded(dataResource); + fixRelatedSchemaIfNeeded(dataResource); // validate schema document / determine or correct resource type validateMetadataDocument(applicationProperties, dataResource, document); @@ -327,46 +310,28 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur boolean noChanges = false; if (document != null) { - SchemaRecord schemaRecord = getSchemaRecordFromDataResource(updatedDataResource); - validateMetadataDocument(applicationProperties, document, schemaRecord); - - ContentInformation info; - String fileName; - info = getContentInformationOfResource(applicationProperties, updatedDataResource); - fileName = (info != null) ? info.getRelativePath() : document.getOriginalFilename(); - noChanges = checkDocumentForChanges(info, document); - - if (!noChanges) { - // Everything seems to be fine update document and increment version - LOG.trace("Updating schema document (and increment version)..."); - String version = oldDataResource.getVersion(); - if (version == null) { - version = "0"; - } - updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); - addProvenance(updatedDataResource); - ContentDataUtils.addFile(applicationProperties, updatedDataResource, document, fileName, null, true, supplier); - } - + updateMetadataDocument(applicationProperties, updatedDataResource, document, supplier); } else { ContentInformation info; info = getContentInformationOfResource(applicationProperties, updatedDataResource); // validate if document is still valid due to changed record settings. - // metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); - String metadataDocumentUri = info.getContentUri(); - - Path metadataDocumentPath = Paths.get(URI.create(metadataDocumentUri)); - if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { - LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); - throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); - } - // test if document is still valid for updated(?) schema. - try { - InputStream inputStream = Files.newInputStream(metadataDocumentPath); - SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecordFromDataResource(updatedDataResource); - MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); - } catch (IOException ex) { - LOG.error("Error validating file!", ex); + if (info != null) { + String metadataDocumentUri = info.getContentUri(); + Path metadataDocumentPath = Paths.get(URI.create(metadataDocumentUri)); + if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { + LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); + throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); + } + // test if document is still valid for updated(?) schema. + try { + InputStream inputStream = Files.newInputStream(metadataDocumentPath); + SchemaRecord schemaRecord = DataResourceRecordUtil.getSchemaRecordFromDataResource(updatedDataResource); + MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); + } catch (IOException ex) { + LOG.error("Error validating file!", ex); + } + } else { + throw new CustomInternalServerError("Metadata document on server does not exist!"); } } @@ -530,22 +495,6 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecordV2(RepoBaseConfi return metadataSchemaRecord; } - private static Long determineVersionFromResourceIdentifier(ResourceIdentifier resourceIdentifier) { - Long version = 1L; - if (resourceIdentifier.getIdentifierType().equals(IdentifierType.URL)) { - //Try to fetch version from URL (only works with URLs including the version as query parameter. - Matcher matcher = Pattern.compile(".*[&?]version=(\\d*).*").matcher(resourceIdentifier.getIdentifier()); - while (matcher.find()) { - version = Long.valueOf(matcher.group(1)); - } - } else { - // set to current version of schema - SchemaRecord currentSchema = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(resourceIdentifier.getIdentifier()); - version = (currentSchema != null) ? currentSchema.getVersion() : 1L; - } - return version; - } - private static ContentInformation getContentInformationOfResource(RepoBaseConfiguration applicationProperties, DataResource dataResource) { ContentInformation returnValue = null; @@ -645,7 +594,7 @@ public static DataResource getRecordById(MetastoreConfiguration metastorePropert public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metastoreProperties, String recordId, Long version) throws ResourceNotFoundException { - LOG.trace("Obtaining schema record with id {} and version {}.", recordId, version); + LOG.trace("Obtaining record with id {} and version {}.", recordId, version); //if security enabled, check permission -> if not matching, return HTTP UNAUTHORIZED or FORBIDDEN long nano = System.nanoTime() / 1000000; long nano2; @@ -754,6 +703,38 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou return specWithSchema; } + public static final Specification<DataResource> findByMimetypes(List<String> mimeTypes) { + // Search for both mimetypes (xml & json) + ResourceType resourceType = null; + // + if (mimeTypes != null) { + int searchFor = 0; // 1 - JSON, 2 - XML, 3 - both + for (String mimeType : mimeTypes) { + if (mimeType.contains("json")) { + searchFor += 1; + } + if (mimeType.contains("xml")) { + searchFor += 2; + } + } + resourceType = switch (searchFor) { + // 1 -> search for JSON only + case 1 -> + ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + // 2 -> search for XML only + case 2 -> + ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + // 3 -> Search for both mimetypes (xml & json) + case 3 -> + ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); + // 0 -> Unknown mimetype + default -> + ResourceType.createResourceType("unknown"); + }; + } + return ResourceTypeSpec.toSpecification(resourceType); + } + /** * Create specification for all listed schemaIds. * @@ -1024,8 +1005,7 @@ private static void checkNoOfRelatedIdentifiers(int noOfRelatedData, int noOfRel */ public static RelatedIdentifier getSchemaIdentifier(DataResource dataResourceRecord) { LOG.trace("Get schema identifier for '{}'.", dataResourceRecord.getId()); - RelatedIdentifier relatedIdentifier = getRelatedIdentifier(dataResourceRecord, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); - return relatedIdentifier; + return getRelatedIdentifier(dataResourceRecord, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); } /** @@ -1373,6 +1353,32 @@ public static DataResource updateDataResource4SchemaDocument(MetastoreConfigurat return updatedDataResource; } + private static void updateMetadataDocument(MetastoreConfiguration applicationProperties, + DataResource updatedDataResource, + MultipartFile document, + UnaryOperator<String> supplier) { + SchemaRecord schemaRecord = getSchemaRecordFromDataResource(updatedDataResource); + validateMetadataDocument(applicationProperties, document, schemaRecord); + + ContentInformation info; + String fileName; + info = getContentInformationOfResource(applicationProperties, updatedDataResource); + fileName = (info != null) ? info.getRelativePath() : document.getOriginalFilename(); + boolean noChanges = checkDocumentForChanges(info, document); + + if (!noChanges) { + // Everything seems to be fine update document and increment version + LOG.trace("Updating schema document (and increment version)..."); + String version = updatedDataResource.getVersion(); + if (version == null) { + version = "0"; + } + updatedDataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); + addProvenance(updatedDataResource); + ContentDataUtils.addFile(applicationProperties, updatedDataResource, document, fileName, null, true, supplier); + } + } + private static void updateSchemaDocument(MetastoreConfiguration applicationProperties, DataResource updatedDataResource, MultipartFile schemaDocument, @@ -1492,28 +1498,22 @@ private static DataResource mergeAcl(DataResource oldDataResource, DataResource } private static DataResource mergeRights(DataResource oldDataResource, DataResource updatedDataResource) { - if (updatedDataResource != null) { - if (updatedDataResource.getRights() == null) { + if (updatedDataResource != null && updatedDataResource.getRights() == null) { updatedDataResource.setRights(new HashSet<>()); - } } return updatedDataResource; } private static DataResource mergeState(DataResource oldDataResource, DataResource updatedDataResource) { - if (updatedDataResource != null) { - if (updatedDataResource.getState() == null) { + if (updatedDataResource != null && updatedDataResource.getState() == null) { updatedDataResource.setState(oldDataResource.getState()); - } } return updatedDataResource; } private static DataResource mergeResourceType(DataResource oldDataResource, DataResource updatedDataResource) { - if (updatedDataResource != null) { - if (updatedDataResource.getResourceType() == null) { + if (updatedDataResource != null && updatedDataResource.getResourceType() == null) { updatedDataResource.setResourceType(oldDataResource.getResourceType()); - } } return updatedDataResource; } @@ -1579,13 +1579,14 @@ private static DataResource fixRelatedSchemaIfNeeded(DataResource dataResource) RelatedIdentifier relatedIdentifier = getSchemaIdentifier(dataResource); SchemaRecord schemaRecord = getSchemaRecordFromDataResource(dataResource); if (schemaRecord != null) { - if (relatedIdentifier.getIdentifierType() == Identifier.IDENTIFIER_TYPE.INTERNAL) { + if (relatedIdentifier != null && relatedIdentifier.getIdentifierType() == Identifier.IDENTIFIER_TYPE.INTERNAL) { relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); // schemaRecord should never be null for internal schema! relatedIdentifier.setValue(schemaRecord.getAlternateId()); } } else { - throw new UnprocessableEntityException("Schema '" + relatedIdentifier.getValue() + "' is not known!"); + String identifier = relatedIdentifier != null ? relatedIdentifier.getValue() : "is not defined and therefor"; + throw new UnprocessableEntityException("Schema '" + identifier + "' is not known!"); } return dataResource; } @@ -1682,6 +1683,30 @@ public static final Instant getCreationDate(DataResource dataResource) { return creationDate; } + /** + * Set creation date for data resource if and only if creation date doesn't + * exist. + * + * @param dataResource data resource. + * @param creationDate creation date + */ + public static final void setCreationDate(DataResource dataResource, Instant creationDate) { + if (creationDate != null) { + boolean createDateExists = false; + Set<Date> dates = dataResource.getDates(); + for (edu.kit.datamanager.repo.domain.Date d : dates) { + if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { + LOG.trace("Creation date entry found."); + createDateExists = true; + break; + } + } + if (!createDateExists) { + dataResource.getDates().add(Date.factoryDate(creationDate, Date.DATE_TYPE.CREATED)); + } + } + } + /** * Get version of data resource. * @@ -1778,6 +1803,20 @@ public static Page<DataResource> queryDataResources(Specification spec, Pageable return records; } + /** + * Get all authorization identities. + * + * @return List with all identities. + */ + private static List<String> getAllAuthorizationIdentities() { + Authentication authentication = AuthenticationHelper.getAuthentication(); + List<String> authorizationIdentities = AuthenticationHelper.getAuthorizationIdentities(); + for (GrantedAuthority authority : authentication.getAuthorities()) { + authorizationIdentities.add(authority.getAuthority()); + } + return authorizationIdentities; + } + /** * @param aBaseUrl the baseUrl to set */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index 83470f3a..a0a05191 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -192,7 +192,6 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica MultipartFile document, UnaryOperator<String> supplier) { MetadataRecord metadataRecord = null; - MetadataRecord existingRecord; // Do some checks first. if ((recordDocument == null || recordDocument.isEmpty()) && (document == null || document.isEmpty())) { @@ -218,43 +217,6 @@ public static MetadataRecord updateMetadataRecord(MetastoreConfiguration applica } DataResource dataResource = DataResourceRecordUtil.updateDataResource4MetadataDocument(applicationProperties, resourceId, eTag, givenRecord, document, supplier); -// if (!noChanges) { -// // Everything seems to be fine update document and increment version -// LOG.trace("Updating schema document (and increment version)..."); -// String version = dataResource.getVersion(); -// if (version != null) { -// dataResource.setVersion(Long.toString(Long.parseLong(version) + 1L)); -// } -// ContentDataUtils.addFile(applicationProperties, dataResource, document, fileName, null, true, supplier); -// } -// -// } else { -// // validate if document is still valid due to changed record settings. -// metadataRecord = migrateToMetadataRecord(applicationProperties, dataResource, false); -// URI metadataDocumentUri = URI.create(metadataRecord.getMetadataDocumentUri()); -// -// Path metadataDocumentPath = Paths.get(metadataDocumentUri); -// if (!Files.exists(metadataDocumentPath) || !Files.isRegularFile(metadataDocumentPath) || !Files.isReadable(metadataDocumentPath)) { -// LOG.warn("Metadata document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", metadataDocumentPath); -// throw new CustomInternalServerError("Metadata document on server either does not exist or is no file or is not readable."); -// } -// -// try { -// InputStream inputStream = Files.newInputStream(metadataDocumentPath); -// SchemaRecord schemaRecord = MetadataSchemaRecordUtil.getSchemaRecord(metadataRecord.getSchema(), metadataRecord.getSchemaVersion()); -// MetadataSchemaRecordUtil.validateMetadataDocument(applicationProperties, inputStream, schemaRecord); -// } catch (IOException ex) { -// LOG.error("Error validating file!", ex); -// } -// -// } -// if (noChanges) { -// Optional<DataRecord> dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(dataResource.getId()); -// if (dataRecord.isPresent()) { -// dataRecordDao.delete(dataRecord.get()); -// } -// } -// dataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, dataResource, eTag, supplier); return migrateToMetadataRecord(applicationProperties, dataResource, true); } @@ -292,34 +254,9 @@ public static void deleteMetadataRecord(MetastoreConfiguration applicationProper public static DataResource migrateToDataResource(RepoBaseConfiguration applicationProperties, MetadataRecord metadataRecord) { DataResource dataResource; - if (metadataRecord.getId() != null) { - try { - dataResource = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, metadataRecord.getId(), metadataRecord.getRecordVersion()); - dataResource = DataResourceUtils.copyDataResource(dataResource); - } catch (ResourceNotFoundException rnfe) { - LOG.error("Error catching DataResource for " + metadataRecord.getId() + " -> " + rnfe.getMessage()); - dataResource = DataResource.factoryNewDataResource(metadataRecord.getId()); - dataResource.setVersion("1"); - } - } else { - dataResource = new DataResource(); - dataResource.setVersion("1"); - } + dataResource = getDataResource(metadataRecord); dataResource.setAcls(metadataRecord.getAcl()); - if (metadataRecord.getCreatedAt() != null) { - boolean createDateExists = false; - Set<Date> dates = dataResource.getDates(); - for (edu.kit.datamanager.repo.domain.Date d : dates) { - if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { - LOG.trace("Creation date entry found."); - createDateExists = true; - break; - } - } - if (!createDateExists) { - dataResource.getDates().add(Date.factoryDate(metadataRecord.getCreatedAt(), Date.DATE_TYPE.CREATED)); - } - } + DataResourceRecordUtil.setCreationDate(dataResource, metadataRecord.getCreatedAt()); if (metadataRecord.getPid() != null) { PrimaryIdentifier pid = PrimaryIdentifier.factoryPrimaryIdentifier(); pid.setIdentifierType(metadataRecord.getPid().getIdentifierType().value()); @@ -329,39 +266,9 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati LOG.trace("Remove existing identifier"); dataResource.setIdentifier(null); } - boolean relationFound = false; - boolean schemaIdFound = false; - for (RelatedIdentifier relatedIds : dataResource.getRelatedIdentifiers()) { - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { - LOG.trace("Set relation to '{}'", metadataRecord.getRelatedResource()); - relatedIds.setValue(metadataRecord.getRelatedResource().getIdentifier()); - relatedIds.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(metadataRecord.getRelatedResource().getIdentifierType().name())); - relationFound = true; - } - if (relatedIds.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { - updateRelatedIdentifierForSchema(relatedIds, metadataRecord); - schemaIdFound = true; - } - } - if (!relationFound) { - RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, metadataRecord.getRelatedResource().getIdentifier(), null, null); - relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(metadataRecord.getRelatedResource().getIdentifierType().name())); - dataResource.getRelatedIdentifiers().add(relatedResource); - } - if (!schemaIdFound) { - RelatedIdentifier schemaId = updateRelatedIdentifierForSchema(null, metadataRecord); - dataResource.getRelatedIdentifiers().add(schemaId); - } - String defaultTitle = "Metadata 4 metastore"; - boolean titleExists = false; - for (Title title : dataResource.getTitles()) { - if (title.getTitleType() == null && title.getValue().equals(defaultTitle)) { - titleExists = true; - } - } - if (!titleExists) { - dataResource.getTitles().add(Title.factoryTitle(defaultTitle, null)); - } + updateRelatedIdentifierForSchema(dataResource, metadataRecord); + updateRelatedIdentifierForResource(dataResource, metadataRecord); + setTitle(dataResource); // Set ResourceType due to new version ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); @@ -385,49 +292,33 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration applicationProperties, DataResource dataResource, boolean provideETag) { - long nano1 = System.nanoTime() / 1000000; MetadataRecord metadataRecord = DataResourceRecordUtil.migrateToMetadataRecordV2(applicationProperties, dataResource); if ((metadataRecord != null) && provideETag) { metadataRecord.setETag(dataResource.getEtag()); DataRecord dataRecord = null; - long nano2 = System.nanoTime() / 1000000; Optional<DataRecord> dataRecordResult = dataRecordDao.findByMetadataIdAndVersion(dataResource.getId(), metadataRecord.getRecordVersion()); - long nano3 = System.nanoTime() / 1000000; - long nano4 = nano3; - boolean isAvailable = false; - boolean saveDataRecord = false; if (dataRecordResult.isPresent()) { LOG.trace("Get document URI from DataRecord."); dataRecord = dataRecordResult.get(); - nano4 = System.nanoTime() / 1000000; metadataRecord.setMetadataDocumentUri(dataRecord.getMetadataDocumentUri()); metadataRecord.setDocumentHash(dataRecord.getDocumentHash()); metadataRecord.setSchemaVersion(dataRecord.getSchemaVersion()); - isAvailable = true; } else { - saveDataRecord = true; - } - if (!isAvailable) { LOG.trace("Get document URI from ContentInformation."); ContentInformation info; info = getContentInformationOfResource(applicationProperties, dataResource); - nano4 = System.nanoTime() / 1000000; if (info != null) { metadataRecord.setDocumentHash(info.getHash()); metadataRecord.setMetadataDocumentUri(info.getContentUri()); MetadataSchemaRecord currentSchemaRecord = MetadataSchemaRecordUtil.getCurrentSchemaRecord(schemaConfig, metadataRecord.getSchema()); metadataRecord.setSchemaVersion(currentSchemaRecord.getSchemaVersion()); - if (saveDataRecord) { - saveNewDataRecord(metadataRecord); - } + saveNewDataRecord(metadataRecord); } } // Only one license allowed. So don't worry about size of set. if (!dataResource.getRights().isEmpty()) { metadataRecord.setLicenseUri(dataResource.getRights().iterator().next().getSchemeUri()); } - long nano5 = System.nanoTime() / 1000000; - LOG.info("Migrate to MetadataRecord, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, provideETag); } return metadataRecord; @@ -436,13 +327,10 @@ public static MetadataRecord migrateToMetadataRecord(RepoBaseConfiguration appli private static ContentInformation getContentInformationOfResource(RepoBaseConfiguration applicationProperties, DataResource dataResource) { ContentInformation returnValue = null; - long nano1 = System.nanoTime() / 1000000; IContentInformationService contentInformationService = applicationProperties.getContentInformationService(); ContentInformation info = new ContentInformation(); info.setParentResource(dataResource); - long nano2 = System.nanoTime() / 1000000; List<ContentInformation> listOfFiles = contentInformationService.findAll(info, PageRequest.of(0, 100)).getContent(); - long nano3 = System.nanoTime() / 1000000; if (LOG.isTraceEnabled()) { LOG.trace("Found {} files for resource '{}'", listOfFiles.size(), dataResource.getId()); for (ContentInformation ci : listOfFiles) { @@ -455,7 +343,6 @@ private static ContentInformation getContentInformationOfResource(RepoBaseConfig if (!listOfFiles.isEmpty()) { returnValue = listOfFiles.get(0); } - LOG.info("Get content information of resource, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1); return returnValue; } @@ -560,22 +447,59 @@ public static MetadataSchemaRecord getInternalSchemaRecord(MetastoreConfiguratio } /** - * Update/create related identifier to values given by metadata record. + * Update/create related identifier for resource to values given by metadata + * record. + * + * @param dataResource data resource holding related resource. + * @param metadataRecord record holding resource information. + */ + private static void updateRelatedIdentifierForResource(DataResource dataResource, MetadataRecord metadataRecord) { + RelatedIdentifier schemaRelatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + if (schemaRelatedIdentifier == null) { + schemaRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, null, null, null); + dataResource.getRelatedIdentifiers().add(schemaRelatedIdentifier); + } + ResourceIdentifier schemaIdentifier = metadataRecord.getRelatedResource(); + schemaRelatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(schemaIdentifier.getIdentifierType().name())); + schemaRelatedIdentifier.setValue(schemaIdentifier.getIdentifier()); + LOG.trace("Set related identfier for resource to '{}'", schemaRelatedIdentifier); + } + + /** + * Update/create related identifier for schema to values given by metadata + * record. * - * @param relatedIdentifier related identifier (if null create a new one) + * @param dataResource data resource holding related schema. * @param metadataRecord record holding schema information. - * @return updated/created related identifier. */ - private static RelatedIdentifier updateRelatedIdentifierForSchema(RelatedIdentifier relatedIdentifier, MetadataRecord metadataRecord) { - if (relatedIdentifier == null) { - relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, null, null, null); + private static void updateRelatedIdentifierForSchema(DataResource dataResource, MetadataRecord metadataRecord) { + RelatedIdentifier schemaRelatedIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); + if (schemaRelatedIdentifier == null) { + schemaRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, null, null, null); + dataResource.getRelatedIdentifiers().add(schemaRelatedIdentifier); } ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); - relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(schemaIdentifier.getIdentifierType().name())); - relatedIdentifier.setValue(schemaIdentifier.getIdentifier()); - LOG.trace("Set relatedId for schema to '{}'", relatedIdentifier); + schemaRelatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.valueOf(schemaIdentifier.getIdentifierType().name())); + schemaRelatedIdentifier.setValue(schemaIdentifier.getIdentifier()); + LOG.trace("Set relatedId for schema to '{}'", schemaRelatedIdentifier); + } - return relatedIdentifier; + /** + * Set title for data resource if and only if not already set. + * + * @param dataResource data resource + */ + public static void setTitle(DataResource dataResource) { + String defaultTitle = "Metadata 4 metastore"; + boolean titleExists = false; + for (Title title : dataResource.getTitles()) { + if (title.getTitleType() == null && title.getValue().equals(defaultTitle)) { + titleExists = true; + } + } + if (!titleExists) { + dataResource.getTitles().add(Title.factoryTitle(defaultTitle, null)); + } } /** @@ -780,6 +704,24 @@ public static void setDataRecordDao(IDataRecordDao aDataRecordDao) { dataRecordDao = aDataRecordDao; } + private static DataResource getDataResource(MetadataRecord metadataRecord) { + DataResource dataResource; + if (metadataRecord.getId() != null) { + try { + dataResource = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, metadataRecord.getId(), metadataRecord.getRecordVersion()); + dataResource = DataResourceUtils.copyDataResource(dataResource); + } catch (ResourceNotFoundException rnfe) { + LOG.error("Error catching DataResource for " + metadataRecord.getId() + " -> " + rnfe.getMessage()); + dataResource = DataResource.factoryNewDataResource(metadataRecord.getId()); + dataResource.setVersion("1"); + } + } else { + dataResource = new DataResource(); + dataResource.setVersion("1"); + } + return dataResource; + } + private static void saveNewDataRecord(MetadataRecord result) { DataRecord dataRecord; @@ -817,7 +759,6 @@ private static void saveNewDataRecord(DataRecord dataRecord) { } } - public static final void fixMetadataDocumentUri(MetadataRecord metadataRecord) { String metadataDocumentUri = metadataRecord.getMetadataDocumentUri(); metadataRecord diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java index 3ee34000..7f7388f6 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataSchemaRecordUtil.java @@ -67,6 +67,8 @@ import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeAcl; import static edu.kit.datamanager.metastore2.util.MetadataRecordUtil.mergeEntry; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; +import java.time.Instant; +import org.springframework.web.context.request.async.DeferredResult; /** * Utility class for handling json documents @@ -106,44 +108,11 @@ public static MetadataSchemaRecord createMetadataSchemaRecord(MetastoreConfigura MultipartFile document, BiFunction<String, Long, String> getSchemaDocumentById) { MetadataSchemaRecord metadataRecord; - + // Do some checks first. - if (recordDocument == null || recordDocument.isEmpty() || document == null || document.isEmpty()) { - String message = "No metadata record and/or metadata document provided. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } - try { - metadataRecord = Json.mapper().readValue(recordDocument.getInputStream(), MetadataSchemaRecord.class); - } catch (IOException ex) { - String message = "No valid metadata record provided. Returning HTTP BAD_REQUEST."; - if (ex instanceof JsonParseException) { - message = message + " Reason: " + ex.getMessage(); - } - LOG.error("Error parsing json: ", ex); - LOG.error(message); - throw new BadArgumentException(message); - } - - if (metadataRecord.getSchemaId() == null) { - String message = "Mandatory attributes schemaId not found in record. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } else { - String value = URLEncoder.encode(metadataRecord.getSchemaId(), StandardCharsets.UTF_8); - if (!value.equals(metadataRecord.getSchemaId())) { - String message = "Not a valid schema id! Encoded: " + value; - LOG.error(message); - throw new BadArgumentException(message); - } - String toLowerCase; - toLowerCase = metadataRecord.getSchemaId().toLowerCase(Locale.getDefault()); - if (!toLowerCase.equals(metadataRecord.getSchemaId())) { - String message = "Not a valid schema id! Please try '" + toLowerCase + "' instead!"; - LOG.error(message); - throw new BadArgumentException(message); - } - } + checkForAtLeastOneDocumentAvailable(recordDocument, document); + metadataRecord = parseMetadataSchemaRecord(recordDocument); + testForValidSchemaId(metadataRecord.getSchemaId()); // Create schema record SchemaRecord schemaRecord = new SchemaRecord(); schemaRecord.setSchemaId(metadataRecord.getSchemaId()); @@ -243,7 +212,7 @@ public static MetadataSchemaRecord updateMetadataSchemaRecord(MetastoreConfigura DataResource givenRecord = null; if (metadataRecord != null) { givenRecord = MetadataSchemaRecordUtil.migrateToDataResource(applicationProperties, metadataRecord); - } + } DataResource updateDataResource = DataResourceRecordUtil.updateDataResource4SchemaDocument(applicationProperties, resourceId, eTag, givenRecord, schemaDocument, supplier); return migrateToMetadataSchemaRecord(applicationProperties, updateDataResource, true); @@ -281,6 +250,59 @@ public static void deleteMetadataSchemaRecord(MetastoreConfiguration application schemaRecordDao.deleteAll(listOfSchemaIds); } + private static void checkForAtLeastOneDocumentAvailable(MultipartFile recordDocument, + MultipartFile document) { + if (recordDocument == null || recordDocument.isEmpty() || document == null || document.isEmpty()) { + String message = "No metadata record and/or metadata document provided. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + } + + private static MetadataSchemaRecord parseMetadataSchemaRecord(MultipartFile recordDocument) { + MetadataSchemaRecord metadataSchemaRecord; + + try { + metadataSchemaRecord = Json.mapper().readValue(recordDocument.getInputStream(), MetadataSchemaRecord.class); + } catch (IOException ex) { + String message = "No valid metadata record provided. Returning HTTP BAD_REQUEST."; + if (ex instanceof JsonParseException) { + message = message + " Reason: " + ex.getMessage(); + } + LOG.error("Error parsing json: ", ex); + LOG.error(message); + throw new BadArgumentException(message); + } + return metadataSchemaRecord; + } + /** + * Test for valid schemaID. + * Must not contain upper case + * Has to be encoded equal to origin -> no spaces or special characters + * @param schemaId schema identifier + */ + private static void testForValidSchemaId(String schemaId) { + if (schemaId == null) { + String message = "Mandatory attributes schemaId not found in record. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } else { + String value = URLEncoder.encode(schemaId, StandardCharsets.UTF_8); + if (!value.equals(schemaId)) { + String message = "Not a valid schema id! Encoded: " + value; + LOG.error(message); + throw new BadArgumentException(message); + } + String toLowerCase; + toLowerCase = schemaId.toLowerCase(Locale.getDefault()); + if (!toLowerCase.equals(schemaId)) { + String message = "Not a valid schema id! Please try '" + toLowerCase + "' instead!"; + LOG.error(message); + throw new BadArgumentException(message); + } + } + } + /** * Migrate from metadata schema record to data resource. * @@ -306,20 +328,7 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati dataResource.setVersion("1"); } dataResource.setAcls(metadataSchemaRecord.getAcl()); - if (metadataSchemaRecord.getCreatedAt() != null) { - boolean createDateExists = false; - Set<Date> dates = dataResource.getDates(); - for (edu.kit.datamanager.repo.domain.Date d : dates) { - if (edu.kit.datamanager.repo.domain.Date.DATE_TYPE.CREATED.equals(d.getType())) { - LOG.trace("Creation date entry found."); - createDateExists = true; - break; - } - } - if (!createDateExists) { - dataResource.getDates().add(Date.factoryDate(metadataSchemaRecord.getCreatedAt(), Date.DATE_TYPE.CREATED)); - } - } + DataResourceRecordUtil.setCreationDate(dataResource, metadataSchemaRecord.getCreatedAt()); if (metadataSchemaRecord.getPid() != null) { PrimaryIdentifier pid = PrimaryIdentifier.factoryPrimaryIdentifier(); pid.setIdentifierType(metadataSchemaRecord.getPid().getIdentifierType().name()); @@ -329,17 +338,8 @@ public static DataResource migrateToDataResource(RepoBaseConfiguration applicati LOG.trace("Remove existing identifier"); dataResource.setIdentifier(null); } - String defaultTitle = metadataSchemaRecord.getMimeType(); - boolean titleExists = false; - for (Title title : dataResource.getTitles()) { - if (title.getTitleType() == null) { - title.setValue(defaultTitle); - titleExists = true; - } - } - if (!titleExists) { - dataResource.getTitles().add(Title.factoryTitle(defaultTitle, null)); - } + MetadataRecordUtil.setTitle(dataResource); + dataResource.setResourceType(ResourceType.createResourceType(metadataSchemaRecord.getType().name() + DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); dataResource.getFormats().clear(); dataResource.getFormats().add(metadataSchemaRecord.getType().name()); @@ -435,8 +435,6 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecord(RepoBaseConfigu } } - - } finally { if (message != null) { LOG.error(message); throw new BadArgumentException(message); @@ -498,9 +496,9 @@ public static MetadataSchemaRecord migrateToMetadataSchemaRecord(RepoBaseConfigu // label -> description of type (OTHER) // description -> description of type (TECHNICAL_INFO) // comment -> description of type (ABSTRACT) - Iterator<Description> desc_iterator = dataResource.getDescriptions().iterator(); - while (desc_iterator.hasNext()) { - Description nextDescription = desc_iterator.next(); + Iterator<Description> descriptionIterator = dataResource.getDescriptions().iterator(); + while (descriptionIterator.hasNext()) { + Description nextDescription = descriptionIterator.next(); switch (nextDescription.getType()) { case ABSTRACT: metadataSchemaRecord.setComment(nextDescription.getDescription()); @@ -573,17 +571,6 @@ public static void validateMetadataDocument(MetastoreConfiguration metastoreProp } } - private static void mergeSchemaRecord(SchemaRecord oldRecord, MetadataSchemaRecord newSettings) { - LOG.trace("Merge Schema record..."); - Objects.requireNonNull(oldRecord); - Objects.requireNonNull(newSettings); - oldRecord.setDocumentHash(newSettings.getSchemaHash()); - oldRecord.setSchemaDocumentUri(newSettings.getSchemaDocumentUri()); -// oldRecord.setSchemaId(newSettings.getSchemaId() + "/" + newSettings.getSchemaVersion()); - oldRecord.setVersion(newSettings.getSchemaVersion()); - oldRecord.setType(newSettings.getType()); - } - /** * Gets SchemaRecord from identifier. Afterwards there should be a clean up. * @@ -597,11 +584,7 @@ private static void mergeSchemaRecord(SchemaRecord oldRecord, MetadataSchemaReco public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long version) { LOG.trace("getSchemaRecord {},{}", identifier, version); SchemaRecord schemaRecord; - if (identifier == null || identifier.getIdentifierType() == null) { - String message = "Missing resource identifier for schema. Returning HTTP BAD_REQUEST."; - LOG.error(message); - throw new BadArgumentException(message); - } + checkIdentifier(identifier); switch (identifier.getIdentifierType()) { case INTERNAL: String schemaId = identifier.getIdentifier(); @@ -652,6 +635,14 @@ public static SchemaRecord getSchemaRecord(ResourceIdentifier identifier, Long v return schemaRecord; } + private static void checkIdentifier(ResourceIdentifier identifier) { + if (identifier == null || identifier.getIdentifierType() == null) { + String message = "Missing resource identifier for schema. Returning HTTP BAD_REQUEST."; + LOG.error(message); + throw new BadArgumentException(message); + } + } + /** * Remove all downloaded files for schema Record. * diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java index 632b212b..15eb6a3e 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java @@ -42,6 +42,7 @@ default IValidator getInstance() { * @param type Type of the schema. * * @return supports schema type or not. + * @deprecated Should be replaced by 'supportsMimeType'. */ @Deprecated boolean supportsSchemaType(MetadataSchemaRecord.SCHEMA_TYPE type); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java index da2e8de6..c130b150 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImpl.java @@ -77,6 +77,7 @@ /** * Controller for metadata documents. + * @deprecated Should be replaced by API v2 (api/v2/metadata/...) */ @Controller @RequestMapping(value = "/api/v1/metadata") @@ -339,48 +340,12 @@ public ResponseEntity<List<MetadataRecord>> getRecords( } // Search for resource type of MetadataSchemaRecord Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(DataResourceRecordUtil.METADATA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL)); -// Specification<DataResource> spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE)); + // Add authentication if enabled spec = DataResourceRecordUtil.findByAccessRights(spec); - List<String> allRelatedIdentifiersSchema = new ArrayList<>(); - List<String> allRelatedIdentifiersResource = new ArrayList<>(); - -// File file = new File(new URIoa) - if (schemaIds != null) { - for (String schemaId : schemaIds) { - MetadataSchemaRecord currentSchemaRecord; - try { - currentSchemaRecord = MetadataRecordUtil.getCurrentInternalSchemaRecord(metadataConfig, schemaId); - // Test for internal URI -> Transform to global URI. - if (currentSchemaRecord.getSchemaDocumentUri().startsWith("file:")) { - ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(currentSchemaRecord); - currentSchemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); - } - allRelatedIdentifiersSchema.add(currentSchemaRecord.getSchemaDocumentUri()); - } catch (Exception rnfe) { - // schemaID not found set version to 1 - currentSchemaRecord = new MetadataSchemaRecord(); - currentSchemaRecord.setSchemaVersion(1L); - allRelatedIdentifiersSchema.add("UNKNOWN_SCHEMA_ID"); - } - for (long versionNumber = 1; versionNumber < currentSchemaRecord.getSchemaVersion(); versionNumber++) { - MetadataSchemaRecord schemaRecord = MetadataRecordUtil.getInternalSchemaRecord(metadataConfig, schemaId, versionNumber); - // Test for internal URI -> Transform to global URI. - if (schemaRecord.getSchemaDocumentUri().startsWith("file:")) { - ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaRecord); - schemaRecord.setSchemaDocumentUri(schemaIdentifier.getIdentifier()); - } - allRelatedIdentifiersSchema.add(schemaRecord.getSchemaDocumentUri()); - } - } - Specification<DataResource> schemaSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersSchema.toArray(new String[allRelatedIdentifiersSchema.size()])); - spec = spec.and(schemaSpecification); - } - if (relatedIds != null) { - allRelatedIdentifiersResource.addAll(relatedIds); - Specification<DataResource> relResourceSpecification = RelatedIdentifierSpec.toSpecification(allRelatedIdentifiersResource.toArray(new String[allRelatedIdentifiersResource.size()])); - spec = spec.and(relResourceSpecification); - } + + spec = DataResourceRecordUtil.findBySchemaId(spec, schemaIds); + spec = DataResourceRecordUtil.findByRelatedId(spec, relatedIds); if ((updateFrom != null) || (updateUntil != null)) { spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java index b3da72fa..24107c87 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataControllerImplV2.java @@ -109,12 +109,8 @@ public class MetadataControllerImplV2 implements IMetadataControllerV2 { private final ApplicationProperties applicationProperties; - private final ILinkedMetadataRecordDao metadataRecordDao; - private final MetastoreConfiguration metadataConfig; - private final IDataResourceDao dataResourceDao; - private final ISchemaRecordDao schemaRecordDao; /** @@ -133,17 +129,14 @@ public class MetadataControllerImplV2 implements IMetadataControllerV2 { * @param applicationProperties Configuration for controller. * @param metadataConfig Configuration for metadata documents repository. * @param metadataRecordDao DAO for metadata records. - * @param dataResourceDao DAO for data resources. + * @param schemaRecordDao DAO for schema records. */ public MetadataControllerImplV2(ApplicationProperties applicationProperties, MetastoreConfiguration metadataConfig, ILinkedMetadataRecordDao metadataRecordDao, - IDataResourceDao dataResourceDao, ISchemaRecordDao schemaRecordDao) { this.applicationProperties = applicationProperties; this.metadataConfig = metadataConfig; - this.metadataRecordDao = metadataRecordDao; - this.dataResourceDao = dataResourceDao; this.schemaRecordDao = schemaRecordDao; LOG.info("------------------------------------------------------"); LOG.info("------{}", this.metadataConfig); @@ -165,7 +158,6 @@ public ResponseEntity createRecord( HttpServletResponse response, UriComponentsBuilder uriBuilder) throws URISyntaxException { -// long nano1 = System.nanoTime() / 1000000; LOG.trace("Performing createRecord({},...).", recordDocument); DataResource metadataRecord; if (recordDocument == null || recordDocument.isEmpty()) { @@ -183,13 +175,11 @@ public ResponseEntity createRecord( LOG.error("Error parsing json: ", ex); throw new BadArgumentException(message); } -// long nano2 = System.nanoTime() / 1000000; DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(metadataRecord); LOG.debug("Test for existing metadata record for given schema and resource"); RelatedIdentifier schemaIdentifier; -// try { schemaIdentifier = DataResourceRecordUtil.getSchemaIdentifier(metadataRecord); switch (schemaIdentifier.getIdentifierType()) { case INTERNAL: @@ -210,40 +200,17 @@ public ResponseEntity createRecord( default: throw new UnprocessableEntityException("Schema referenced by '" + schemaIdentifier.getIdentifierType().toString() + "' is not supported yet!"); } -// } catch (ResourceNotFoundException rnfe) { -// LOG.debug("Error checking for existing relations.", rnfe); -// throw new UnprocessableEntityException("Schema ID seems to be invalid"); -// } - //Can't test this -// boolean recordAlreadyExists = metadataRecordDao.existsDataResourceByRelatedResourceAndSchemaId( -// getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue(), -// getRelatedIdentifier(metadataRecord, RelatedIdentifier.RELATION_TYPES.HAS_METADATA).getValue()); -// long nano3 = System.nanoTime() / 1000000; -// -// if (recordAlreadyExists) { -// String message = String.format("Conflict! There is already a metadata document with " -// + "the same schema ('%s') and the same related resource ('%s')", -// metadataRecord.getSchemaId(), -// metadataRecord.getRelatedResource().getIdentifier()); -// LOG.error(message); -// return ResponseEntity.status(HttpStatus.CONFLICT).body(message); -// } + DataResource result = DataResourceRecordUtil.createDataResourceRecord4Metadata(metadataConfig, recordDocument, document); LOG.trace("Get dataresource: '{}'", result); String eTag = result.getEtag(); LOG.trace("Get ETag: ' {}'", eTag); // Successfully created metadata record. -// long nano4 = System.nanoTime() / 1000000; LOG.trace("Metadata record successfully persisted. Returning result."); DataResourceRecordUtil.fixSchemaUrl(result); -// long nano5 = System.nanoTime() / 1000000; -// metadataRecordDao.save(new LinkedMetadataRecord(result)); -// long nano6 = System.nanoTime() / 1000000; URI locationUri; locationUri = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(result.getId(), Long.valueOf(result.getVersion()), null, null)).toUri(); - long nano7 = System.nanoTime() / 1000000; -// LOG.info("Create Record Service, {}, {}, {}, {}, {}, {}, {}", nano1, nano2 - nano1, nano3 - nano1, nano4 - nano1, nano5 - nano1, nano6 - nano1, nano7 - nano1); LOG.trace("Sending CREATE event."); messagingService.orElse(new LogfileMessagingService()). @@ -479,24 +446,6 @@ public void contribute(Info.Builder builder) { } } - /** - * Get value of relatedIdentitier with given relation type. - * - * @param result data resource. - * @param relationType type of related identifier. - * @return related identifier. - */ - private RelatedIdentifier getRelatedIdentifier(DataResource result, RelatedIdentifier.RELATION_TYPES relationType) { - RelatedIdentifier relatedIdentifier = null; - for (RelatedIdentifier item : result.getRelatedIdentifiers()) { - if (item.getRelationType().equals(relationType)) { - relatedIdentifier = item; - break; - } - } - return relatedIdentifier; - } - @Bean public RestTemplate restTemplate() { return new RestTemplate(); diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java index 490d0d30..5325fb9d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java @@ -37,6 +37,7 @@ /** * Controller for search entities. + * @deprecated Should be replaced by API v2 (api/v2/metadata/...) */ @Controller @RequestMapping(value = "/api/v1/metadata") diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java index bafa1db0..9ba63e37 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImpl.java @@ -68,6 +68,7 @@ /** * Controller for schema documents. + * @deprecated Should be replaced by API v2 (api/v2/schemas/...) */ @Controller @RequestMapping(value = "/api/v1/schemas") @@ -249,35 +250,10 @@ public ResponseEntity<List<MetadataSchemaRecord>> getRecords(@RequestParam(value return getAllVersions(schemaId, pgbl); } // Search for resource type of MetadataSchemaRecord - ResourceType resourceType = ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); - if (mimeTypes != null) { - boolean searchForJson = false; - boolean searchForXml = false; - for (String mimeType : mimeTypes) { - if (mimeType.contains("json")) { - searchForJson = true; - } - if (mimeType.contains("xml")) { - searchForXml = true; - } - } - if (searchForJson && !searchForXml) { - resourceType = ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); - } - if (!searchForJson && searchForXml) { - resourceType = ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); - } - if (!searchForJson && !searchForXml) { - resourceType = ResourceType.createResourceType("unknown"); - } - } - Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); + Specification<DataResource> spec = DataResourceRecordUtil.findByMimetypes(mimeTypes); // Add authentication if enabled spec = DataResourceRecordUtil.findByAccessRights(spec); - //one of given mimetypes. - if ((mimeTypes != null) && !mimeTypes.isEmpty()) { - spec = spec.and(TitleSpec.toSpecification(mimeTypes.toArray(new String[mimeTypes.size()]))); - } + if ((updateFrom != null) || (updateUntil != null)) { spec = spec.and(LastUpdateSpecification.toSpecification(updateFrom, updateUntil)); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index 53bdcc81..a5a821f6 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -107,7 +107,7 @@ public SchemaRegistryControllerImplV2(ApplicationProperties applicationPropertie this.applicationProperties = applicationProperties; this.schemaConfig = schemaConfig; this.dataResourceDao = dataResourceDao; - DataResourceRecordUtil.setDataResourceDao(dataResourceDao); + DataResourceRecordUtil.setDataResourceDao(this.dataResourceDao); LOG.info("------------------------------------------------------"); LOG.info("------{}", schemaConfig); LOG.info("------------------------------------------------------"); @@ -121,9 +121,6 @@ public ResponseEntity<DataResource> createRecord( HttpServletResponse response, UriComponentsBuilder uriBuilder) { LOG.trace("Performing createRecord({},....", recordDocument); - BiFunction<String, Long, String> getSchemaDocumentById; - getSchemaDocumentById = (schema, version) -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getSchemaDocumentById(schema, version, null, null)).toString(); - DataResource dataResourceRecord = DataResourceRecordUtil.createDataResourceRecord4Schema(schemaConfig, recordDocument, document); LOG.trace("Schema record successfully persisted. Returning result."); String etag = dataResourceRecord.getEtag(); @@ -153,7 +150,6 @@ public ResponseEntity<DataResource> getRecordById( HttpServletResponse hsr) { LOG.trace("Performing getRecordById({}, {}).", schemaId, version); - LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); String etag = schemaRecord.getEtag(); @@ -169,7 +165,6 @@ public ResponseEntity<ContentInformation> getContentInformationById( HttpServletResponse hsr) { LOG.trace("Performing getContentInformationById({}, {}).", schemaId, version); - LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); ContentInformation contentInformation = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaId, version); DataResource minimalDataResource = DataResource.factoryNewDataResource(contentInformation.getParentResource().getId()); URI locationUri; @@ -208,7 +203,6 @@ public ResponseEntity getSchemaDocumentById( HttpServletResponse hsr) { LOG.trace("Performing getSchemaDocumentById({}, {}).", schemaId, version); - LOG.trace("Obtaining schema record with id {} and version {}.", schemaId, version); DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); ContentInformation contentInfo = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaRecord.getId(), Long.valueOf(schemaRecord.getVersion())); MediaType contentType = MediaType.valueOf(contentInfo.getMediaType()); @@ -280,31 +274,8 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche return getAllVersions(schemaId, pgbl); } // Search for resource type of MetadataSchemaRecord - boolean searchForJson = true; - boolean searchForXml = true; - ResourceType resourceType = ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); - if (mimeTypes != null) { - searchForJson = false; - searchForXml = false; - for (String mimeType : mimeTypes) { - if (mimeType.contains("json")) { - searchForJson = true; - } - if (mimeType.contains("xml")) { - searchForXml = true; - } - } - if (searchForJson && !searchForXml) { - resourceType = ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); - } - if (!searchForJson && searchForXml) { - resourceType = ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); - } - if (!searchForJson && !searchForXml) { - resourceType = ResourceType.createResourceType("unknown"); - } - } - Specification<DataResource> spec = ResourceTypeSpec.toSpecification(resourceType); + Specification<DataResource> spec = DataResourceRecordUtil.findByMimetypes(mimeTypes); + // Add authentication if enabled spec = DataResourceRecordUtil.findByAccessRights(spec); if ((updateFrom != null) || (updateUntil != null)) { @@ -343,8 +314,6 @@ public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final LOG.trace("DataResource record successfully persisted. Updating document URI and returning result."); String etag = updatedSchemaRecord.getEtag(); - // Fix Url for OAI PMH entry -// MetadataSchemaRecordUtil.updateMetadataFormat(updatedSchemaRecord); URI locationUri; locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(updatedSchemaRecord); From e0480a314b65cfc2675c68f32015621a0cd7df11 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 13 Nov 2024 09:16:59 +0100 Subject: [PATCH 140/181] Fix migration of PID. --- .../metastore2/runner/Migration2V2Runner.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java index 05cfae11..e2047466 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java @@ -15,12 +15,14 @@ */ package edu.kit.datamanager.metastore2.runner; +import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.PrimaryIdentifier; import edu.kit.datamanager.repo.domain.RelatedIdentifier; import edu.kit.datamanager.repo.domain.ResourceType; import edu.kit.datamanager.repo.domain.Title; @@ -89,6 +91,8 @@ public void saveSchema(String id, long version) { break; } } + // Move PID from alternateIdentifier to identifier + movePidFromAlternateToPrimaryIdentifier(recordByIdAndVersion); // Set resource type to new definition of version 2 ('...'_Schema) ResourceType resourceType = recordByIdAndVersion.getResourceType(); resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); @@ -142,6 +146,8 @@ public DataResource saveMetadata(String id, long version, String format) { break; } } + // Move PID from alternateIdentifier to identifier + movePidFromAlternateToPrimaryIdentifier(recordByIdAndVersion); // Set resource type to new definition of version 2 ('...'_Metadata) ResourceType resourceType = recordByIdAndVersion.getResourceType(); resourceType.setTypeGeneral(ResourceType.TYPE_GENERAL.MODEL); @@ -187,6 +193,22 @@ public DataResource getCopyOfDataResource(DataResource metadataDocument) { } return copy; } + + private void movePidFromAlternateToPrimaryIdentifier(DataResource dataResource) { + Identifier pid = null; + // Move PID from alternateIdentifier to identifier + for (Identifier altIdentifier : dataResource.getAlternateIdentifiers()) { + if (altIdentifier.getIdentifierType() == Identifier.IDENTIFIER_TYPE.DOI) { + PrimaryIdentifier primaryIdentifier = PrimaryIdentifier.factoryPrimaryIdentifier(altIdentifier.getValue()); + dataResource.setIdentifier(primaryIdentifier); + pid = altIdentifier; + break; + } + } + if (pid != null) { + dataResource.getAlternateIdentifiers().remove(pid); + } + } /** * @param baseUrl the baseUrl to set From c504c3a87f5553ed40bedd0fdf0d8f8209aba2ba Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 13 Nov 2024 09:21:41 +0100 Subject: [PATCH 141/181] Fix type of specification for dataresource queries. --- .../datamanager/metastore2/runner/ElasticIndexerRunner.java | 4 ++-- .../datamanager/metastore2/util/DataResourceRecordUtil.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index f314dec2..53f614ec 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -366,7 +366,7 @@ private void migrateToVersion2() throws InterruptedException { * Migrate dataresources of schemas using version 1 to version 2. */ private void migrateAllSchemasToDataciteVersion2() { - Specification spec; + Specification<DataResource> spec; spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataSchemaRecord.RESOURCE_TYPE, ResourceType.TYPE_GENERAL.DATASET)); int pageNumber = 0; int pageSize = 1; @@ -437,7 +437,7 @@ private void migrateSchemaToDataciteVersion2(DataResource schema) { * Migrate dataresources of metadata documents from version 1 to version 2. */ private void migrateAllMetadataDocumentsToDataciteVersion2() { - Specification spec; + Specification<DataResource> spec; spec = ResourceTypeSpec.toSpecification(ResourceType.createResourceType(MetadataRecord.RESOURCE_TYPE, ResourceType.TYPE_GENERAL.DATASET)); int pageNumber = 0; int pageSize = 10; diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index cc1f42a9..20b8e348 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -1792,7 +1792,7 @@ public static final URI getMetadataDocumentUri(String id, String version) { * @return Pageable Object holding all data resources fulfilling the * specification. */ - public static Page<DataResource> queryDataResources(Specification spec, Pageable pgbl) { + public static Page<DataResource> queryDataResources(Specification<DataResource> spec, Pageable pgbl) { Page<DataResource> records = null; try { records = spec != null ? dataResourceDao.findAll(spec, pgbl) : dataResourceDao.findAll(pgbl); From 3f43535534b5aad3c46a6fe10df8611abc2f45b8 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 13 Nov 2024 14:22:31 +0100 Subject: [PATCH 142/181] Refactoring code. --- .../messaging/MetadataResourceMessage.java | 4 +- .../metastore2/domain/ElasticWrapper.java | 5 +- .../metastore2/domain/SchemaRecord.java | 2 +- .../service/MetastoreOAIPMHRepository.java | 30 +++++---- .../runner/ElasticIndexerRunner.java | 46 ++++++-------- .../metastore2/runner/Migration2V2Runner.java | 19 ++++-- .../util/DataResourceRecordUtil.java | 61 +++++++++++-------- .../metastore2/validation/IValidator.java | 2 +- .../web/impl/MetadataSearchController.java | 43 +++++++------ 9 files changed, 110 insertions(+), 102 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java index 6b16d04b..79ce4ecc 100644 --- a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java +++ b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java @@ -160,8 +160,8 @@ public static MetadataResourceMessage createMessage(DataResource dataResource, A if (dataResource != null) { String metadataDocumentUri = DataResourceRecordUtil.getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()).toString(); String schemaDocumentUri = DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue(); - String[] split = schemaDocumentUri.split(DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR); - String schemaId = split[split.length - 1].split("\\?")[0]; + String[] split = schemaDocumentUri.split(DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR, -1); + String schemaId = split[split.length - 1].split("\\?", -1)[0]; properties.put(RESOLVING_URL_PROPERTY, metadataDocumentUri); properties.put(DOCUMENT_TYPE_PROPERTY, schemaId); diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java index db8d2f1e..f5e85c38 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/ElasticWrapper.java @@ -81,9 +81,8 @@ public ElasticWrapper(DataResource resource) { } }); - resource.getDates().stream().filter(d -> (DATE_TYPE.CREATED.equals(d.getType()))).forEachOrdered(d -> { - created = Date.from(d.getValue()); - }); + resource.getDates().stream().filter(d -> DATE_TYPE.CREATED.equals(d.getType())). + forEachOrdered((edu.kit.datamanager.repo.domain.Date d) -> created = Date.from(d.getValue())); lastUpdate = Date.from(resource.getLastUpdate()); } diff --git a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java index 2ad1d225..94d3e98d 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java +++ b/src/main/java/edu/kit/datamanager/metastore2/domain/SchemaRecord.java @@ -54,7 +54,7 @@ public class SchemaRecord implements Serializable { public String getSchemaIdWithoutVersion() { String pureSchemaId = null; if (schemaId != null) { - pureSchemaId = schemaId.split("/")[0]; + pureSchemaId = schemaId.split("/", -1)[0]; } return pureSchemaId; } diff --git a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java index 74b0bf71..388e4197 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java +++ b/src/main/java/edu/kit/datamanager/metastore2/oaipmh/service/MetastoreOAIPMHRepository.java @@ -52,6 +52,7 @@ import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; @@ -97,8 +98,8 @@ public class MetastoreOAIPMHRepository extends AbstractOAIPMHRepository { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(MetastoreOAIPMHRepository.class); - private final MetadataFormatType DC_SCHEMA; - private final MetadataFormatType DATACITE_SCHEMA; + private static final MetadataFormatType DC_SCHEMA; + private static final MetadataFormatType DATACITE_SCHEMA; private final OaiPmhConfiguration pluginConfiguration; @@ -108,9 +109,22 @@ public class MetastoreOAIPMHRepository extends AbstractOAIPMHRepository { private IMetadataFormatDao metadataFormatDao; @Autowired private MetastoreConfiguration metadataConfig; + + static { + DC_SCHEMA = new MetadataFormatType(); + DC_SCHEMA.setMetadataNamespace("http://www.openarchives.org/OAI/2.0/oai_dc/"); + DC_SCHEMA.setSchema("http://www.openarchives.org/OAI/2.0/oai_dc.xsd"); + DC_SCHEMA.setMetadataPrefix("oai_dc"); + + DATACITE_SCHEMA = new MetadataFormatType(); + DATACITE_SCHEMA.setMetadataNamespace("http://datacite.org/schema/kernel-4"); + DATACITE_SCHEMA.setSchema("http://schema.datacite.org/meta/kernel-4.1/metadata.xsd"); + DATACITE_SCHEMA.setMetadataPrefix("datacite"); + } /** * Default constructor. + * @param pluginConfiguration configuration for OAI-PMH. */ @Autowired public MetastoreOAIPMHRepository(OaiPmhConfiguration pluginConfiguration) { @@ -121,19 +135,11 @@ public MetastoreOAIPMHRepository(OaiPmhConfiguration pluginConfiguration) { * Default constructor. * * @param name The repository name. + * @param pluginConfiguration configuration for OAI-PMH. */ private MetastoreOAIPMHRepository(String name, OaiPmhConfiguration pluginConfiguration) { super(name); this.pluginConfiguration = pluginConfiguration; - DC_SCHEMA = new MetadataFormatType(); - DC_SCHEMA.setMetadataNamespace("http://www.openarchives.org/OAI/2.0/oai_dc/"); - DC_SCHEMA.setSchema("http://www.openarchives.org/OAI/2.0/oai_dc.xsd"); - DC_SCHEMA.setMetadataPrefix("oai_dc"); - - DATACITE_SCHEMA = new MetadataFormatType(); - DATACITE_SCHEMA.setMetadataNamespace("http://datacite.org/schema/kernel-4"); - DATACITE_SCHEMA.setSchema("http://schema.datacite.org/meta/kernel-4.1/metadata.xsd"); - DATACITE_SCHEMA.setMetadataPrefix("datacite"); } @Override @@ -332,7 +338,7 @@ private Document getMetadataDocument(DataRecord object, String schemaId) { } else if (object.getSchemaId().equals(schemaId)) { LOGGER.info("Return stored document of resource '{}'.", object.getMetadataId()); try { - URL url = new URL(object.getMetadataDocumentUri()); + URL url = new URI(object.getMetadataDocumentUri()).toURL(); byte[] readFileToByteArray = FileUtils.readFileToByteArray(Paths.get(url.toURI()).toFile()); try (InputStream inputStream = new ByteArrayInputStream(readFileToByteArray)) { IOUtils.copy(inputStream, bout); diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 53f614ec..872be2b1 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -32,7 +32,6 @@ import static edu.kit.datamanager.metastore2.util.DataResourceRecordUtil.queryDataResources; import edu.kit.datamanager.metastore2.web.impl.MetadataControllerImplV2; import edu.kit.datamanager.metastore2.web.impl.SchemaRegistryControllerImplV2; -import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; @@ -56,7 +55,6 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.javers.core.Javers; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; @@ -114,11 +112,7 @@ public class ElasticIndexerRunner implements CommandLineRunner { * Logger. */ private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexerRunner.class); - /** - * DAO for all data resources. - */ - @Autowired - private IDataResourceDao dataResourceDao; + /** * DAO for all schema records. */ @@ -134,22 +128,12 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Autowired private IUrl2PathDao url2PathDao; - /** - * Instance for managing versions. - */ - @Autowired - private Javers javers; /** * Instance of schema repository. */ @Autowired private MetastoreConfiguration schemaConfig; - /** - * Instande of metadata reository. - * - */ - @Autowired - private MetastoreConfiguration metadataConfig; + /** * Optional messagingService bean may or may not be available, depending on a * service's configuration. If messaging capabilities are disabled, this bean @@ -176,7 +160,7 @@ public void run(String... args) throws Exception { .addObject(this) .build(); try { - LOG.trace("Parse arguments: '{}'", args); + LOG.trace("Parse arguments: '{}'", (Object)args); argueParser.parse(args); LOG.trace("Find all schemas..."); List<SchemaRecord> findAllSchemas = schemaRecordDao.findAll(PageRequest.of(0, 1)).getContent(); @@ -339,7 +323,7 @@ private String toSchemaUrl(DataRecord dataRecord, String baseUrl) { /** * Migrate all data resources from version 1 to version 2. * - * @throws InterruptedException + * @throws InterruptedException Process was interrupted. */ private void migrateToVersion2() throws InterruptedException { LOG.info("Start Migrate2DataCite Runner for migrating database from version 1 to version 2."); @@ -384,17 +368,23 @@ private void migrateAllSchemasToDataciteVersion2() { /** * Remove all indexed entries for given schema. (If search is enabled) * + * example: + * POST /metastore-schemaid/_delete_by_query + * { + * "query": { + * "range": { + * "metadataRecord.identifier.id": { + * "gte": 1 + * } + * } + * } + * } + * + * * @param schemaId schema */ private void removeAllIndexedEntries(String schemaId) { // Delete all entries in elastic (if available) - // POST /my-index-000001/_delete_by_query - //{ - // "query": { - // "match": { - // "user.id": "elkbee" - // } - // } LOG.trace("Remove all indexed entries..."); if (searchConfiguration.isSearchEnabled()) { String prefix4Indices = searchConfiguration.getIndex(); @@ -407,7 +397,7 @@ private void removeAllIndexedEntries(String schemaId) { LOG.trace(searchConfiguration.toString()); LOG.trace("Remove all entries for index: '{}'", prefix4Indices + schemaId); SimpleServiceClient client = SimpleServiceClient.create(searchConfiguration.getUrl() + "/" + prefix4Indices + schemaId + "/_delete_by_query"); - String query = "{ \"query\": { \"range\" : { \"metadataRecord.schemaVersion\" : { \"gte\" : 1} } } }"; + String query = "{ \"query\": { \"range\" : { \"metadataRecord.identifier.id\" : { \"gte\" : 1} } } }"; client.withContentType(MediaType.APPLICATION_JSON); try { String postResource = client.postResource(query, String.class); diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java index e2047466..bf560102 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java @@ -78,7 +78,6 @@ public class Migration2V2Runner { * * @param id ID of the schema document. * @param version Version of the schema document. - * @param format Format of the schema document. (XML/JSON) */ public void saveSchema(String id, long version) { LOG.info("Migrate datacite for schema document with id: '{}' / version: '{}'", id, version); @@ -134,6 +133,7 @@ public void saveSchema(String id, long version) { * @param id ID of the metadata document. * @param version Version of the metadata document. * @param format Format of the metadata document. (XML/JSON) + * @return Persisted data resource. */ public DataResource saveMetadata(String id, long version, String format) { LOG.trace("Migrate datacite for metadata document with id: '{}' / version: '{}' and format: '{}'", id, version, format); @@ -184,12 +184,17 @@ public DataResource saveMetadata(String id, long version, String format) { return migratedDataResource; } - - public DataResource getCopyOfDataResource(DataResource metadataDocument) { + /** + * Create a deep copy of a data resource instance. + * @param dataResource Data resource. + * @return Deep copy of data resource. + */ + public DataResource getCopyOfDataResource(DataResource dataResource) { DataResource copy = null; - Optional<DataResource> dataResource = dataResourceDao.findById(metadataDocument.getId()); - if (dataResource.isPresent()) { - copy = DataResourceUtils.copyDataResource(dataResource.get()); + Optional<DataResource> origDataResource; + origDataResource = dataResourceDao.findById(dataResource.getId()); + if (origDataResource.isPresent()) { + copy = DataResourceUtils.copyDataResource(origDataResource.get()); } return copy; } @@ -211,6 +216,8 @@ private void movePidFromAlternateToPrimaryIdentifier(DataResource dataResource) } /** + * Set base URL for accessing documents and records. + * * @param baseUrl the baseUrl to set */ public void setBaseUrl(String baseUrl) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 20b8e348..6ebd6937 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -100,7 +100,6 @@ public class DataResourceRecordUtil { private static final String ERROR_PARSING_JSON = "Error parsing json: "; private static MetastoreConfiguration schemaConfig; - private static String guestToken = null; private static IDataRecordDao dataRecordDao; private static IDataResourceDao dataResourceDao; @@ -139,9 +138,7 @@ public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean curren if (schemaConfig.isAuthEnabled() && !authorizationIdentities.contains(RepoUserRole.ADMINISTRATOR.getValue())) { isAllowed = false; // Check if authorized user still has ADMINISTRATOR rights - Iterator<AclEntry> iterator = aclEntries.iterator(); - while (iterator.hasNext()) { - AclEntry aclEntry = iterator.next(); + for (AclEntry aclEntry : aclEntries) { LOG.trace("'{}' has '{}' rights!", aclEntry.getSid(), aclEntry.getPermission()); if (aclEntry.getPermission().atLeast(PERMISSION.ADMINISTRATE) && authorizationIdentities.contains(aclEntry.getSid())) { isAllowed = true; @@ -168,7 +165,6 @@ public static boolean checkAccessRights(Set<AclEntry> aclEntries, boolean curren * @param applicationProperties Settings of repository. * @param recordDocument Record of the schema. * @param document Schema document. - * @param getSchemaDocumentById Method for creating access URL. * @return Record of registered schema document. */ public static DataResource createDataResourceRecord4Schema(MetastoreConfiguration applicationProperties, @@ -287,7 +283,7 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur MultipartFile recordDocument, MultipartFile document, UnaryOperator<String> supplier) { - DataResource givenDataResource = null; + DataResource givenDataResource; givenDataResource = checkParameters(recordDocument, document, false); return updateDataResource4MetadataDocument(applicationProperties, resourceId, eTag, givenDataResource, document, supplier); @@ -835,10 +831,6 @@ public static long getNoOfSchemaDocuments() { return queryDataResources(spec, pgbl).getTotalElements(); } - public static void setToken(String bearerToken) { - guestToken = bearerToken; - } - /** * Set schema config. * @@ -860,7 +852,7 @@ public static void setDataRecordDao(IDataRecordDao aDataRecordDao) { /** * Set DAO for data record. * - * @param aDataRecordDao the dataRecordDao to set + * @param aDataResourceDao the dataResourceDao to set */ public static void setDataResourceDao(IDataResourceDao aDataResourceDao) { dataResourceDao = aDataResourceDao; @@ -898,7 +890,7 @@ public static final void fixSchemaUrl(DataResource dataresource) { } public static final void fixSchemaUrl(RelatedIdentifier schemaIdentifier) { - if ((schemaIdentifier != null) && (schemaIdentifier.getIdentifierType().equals(Identifier.IDENTIFIER_TYPE.INTERNAL))) { + if (schemaIdentifier != null && schemaIdentifier.getIdentifierType().equals(Identifier.IDENTIFIER_TYPE.INTERNAL)) { String value = schemaIdentifier.getValue(); StringTokenizer tokenizer = new StringTokenizer(schemaIdentifier.getValue(), SCHEMA_VERSION_SEPARATOR); Long version = null; @@ -1012,6 +1004,7 @@ public static RelatedIdentifier getSchemaIdentifier(DataResource dataResourceRec * Transform schema identifier to global available identifier (if neccessary). * * @param dataResourceRecord Metadata record hold schema identifier. + * @param relationType Relation type of the identifier. * @return ResourceIdentifier with a global accessible identifier. */ public static RelatedIdentifier getRelatedIdentifier(DataResource dataResourceRecord, RelatedIdentifier.RELATION_TYPES relationType) { @@ -1044,7 +1037,8 @@ public static final void check4validSchemaId(DataResource metadataRecord) { public static final void check4validId(DataResource metadataRecord, boolean allowUpperCase) { String id = metadataRecord.getId(); - String lowerCaseId = id.toLowerCase(); + String lowerCaseId; + lowerCaseId = id.toLowerCase(Locale.getDefault()); if (allowUpperCase) { lowerCaseId = id; @@ -1087,7 +1081,7 @@ private static void validateMetadataSchemaDocument(MetastoreConfiguration metast throw new BadArgumentException(message); } - IValidator applicableValidator = null; + IValidator applicableValidator; try { applicableValidator = getValidatorForRecord(metastoreProperties, dataResource, document); @@ -1269,7 +1263,8 @@ private static SchemaRecord getSchemaRecordFromDataResource(DataResource dataRes schemaRecord = schemaRecordDao.findByAlternateId(schemaIdentifier.getValue()); break; case INTERNAL: - String[] split = schemaId.split(SCHEMA_VERSION_SEPARATOR); + String[] split; + split = schemaId.split(SCHEMA_VERSION_SEPARATOR, -1); if (split.length == 1) { schemaRecord = schemaRecordDao.findFirstBySchemaIdStartsWithOrderByVersionDesc(schemaId + SCHEMA_VERSION_SEPARATOR); } else { @@ -1388,7 +1383,7 @@ private static void updateSchemaDocument(MetastoreConfiguration applicationPrope // Get schema record for this schema validateMetadataSchemaDocument(applicationProperties, updatedDataResource, schemaDocument); - boolean noChanges = false; + boolean noChanges; String fileName; fileName = (info != null) ? info.getRelativePath() : schemaDocument.getOriginalFilename(); @@ -1446,7 +1441,7 @@ private static DataResource mergeDataResource(DataResource oldDataResource, Data mergePublicationYear(oldDataResource, updatedDataResource); mergePublisher(oldDataResource, updatedDataResource); mergeAcl(oldDataResource, updatedDataResource); - mergeRights(oldDataResource, updatedDataResource); + fixEmptyRights(updatedDataResource); mergeState(oldDataResource, updatedDataResource); mergeResourceType(oldDataResource, updatedDataResource); mergeCreateDate(oldDataResource, updatedDataResource); @@ -1497,23 +1492,29 @@ private static DataResource mergeAcl(DataResource oldDataResource, DataResource return updatedDataResource; } - private static DataResource mergeRights(DataResource oldDataResource, DataResource updatedDataResource) { + /** + * Fix rights to an empty array if no rights are provided. + * + * @param updatedDataResource data resource to check. + * @return Fixed data resource. + */ + private static DataResource fixEmptyRights(DataResource updatedDataResource) { if (updatedDataResource != null && updatedDataResource.getRights() == null) { - updatedDataResource.setRights(new HashSet<>()); + updatedDataResource.setRights(new HashSet<>()); } return updatedDataResource; } private static DataResource mergeState(DataResource oldDataResource, DataResource updatedDataResource) { if (updatedDataResource != null && updatedDataResource.getState() == null) { - updatedDataResource.setState(oldDataResource.getState()); + updatedDataResource.setState(oldDataResource.getState()); } return updatedDataResource; } private static DataResource mergeResourceType(DataResource oldDataResource, DataResource updatedDataResource) { if (updatedDataResource != null && updatedDataResource.getResourceType() == null) { - updatedDataResource.setResourceType(oldDataResource.getResourceType()); + updatedDataResource.setResourceType(oldDataResource.getResourceType()); } return updatedDataResource; } @@ -1572,8 +1573,8 @@ public static boolean checkDocumentForChanges(ContentInformation info, Multipart /** * Migrate schema from INTERNAL type to URL type (if necessary) * - * @param dataResource - * @return + * @param dataResource Data resource which should be fixed. + * @return Fixed data resource. */ private static DataResource fixRelatedSchemaIfNeeded(DataResource dataResource) { RelatedIdentifier relatedIdentifier = getSchemaIdentifier(dataResource); @@ -1596,8 +1597,8 @@ private static DataResource fixRelatedSchemaIfNeeded(DataResource dataResource) * given or check type. * * @param metastoreProperties Configuration for accessing services - * @param metadataRecord metadata of the document. - * @param document documentdataResource + * @param dataResource Data resource record of the document. + * @param document Document of data resource. */ private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, DataResource dataResource, @@ -1610,7 +1611,8 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro } boolean validationSuccess = false; StringBuilder errorMessage = new StringBuilder(); - SchemaRecord findByAlternateId = getSchemaRecordFromDataResource(dataResource); + SchemaRecord findByAlternateId; + findByAlternateId = getSchemaRecordFromDataResource(dataResource); if (findByAlternateId != null) { try { validateMetadataDocument(metastoreProperties, document, findByAlternateId); @@ -1625,7 +1627,10 @@ private static void validateMetadataDocument(MetastoreConfiguration metastorePro errorMessage.append(ex.getMessage()).append("\n"); } } else { - errorMessage.append("No matching schema found for '" + getSchemaIdentifier(dataResource).getValue() + "'!"); + errorMessage.append("No matching schema found for '"); + RelatedIdentifier schemaIdentifier = getSchemaIdentifier(dataResource); + errorMessage = schemaIdentifier != null ? errorMessage.append(schemaIdentifier.getValue()) : errorMessage.append("missing schema identifier"); + errorMessage.append("'!"); } if (!validationSuccess) { LOG.error(errorMessage.toString()); @@ -1818,6 +1823,8 @@ private static List<String> getAllAuthorizationIdentities() { } /** + * Set base URL for accessing instances. + * * @param aBaseUrl the baseUrl to set */ public static void setBaseUrl(String aBaseUrl) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java index 15eb6a3e..f71c5d7c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java +++ b/src/main/java/edu/kit/datamanager/metastore2/validation/IValidator.java @@ -51,7 +51,7 @@ default IValidator getInstance() { * Supports the given MIME type. * * @see https://www.iana.org/assignments/media-types/media-types.xhtml - * @param type Type of the schema. + * @param mimetype Type of the schema. * * @return supports schema type or not. */ diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java index 5325fb9d..e16a2ea9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/MetadataSearchController.java @@ -65,6 +65,16 @@ public MetadataSearchController(SearchConfiguration searchConfiguration) { LOG.info("------------------------------------------------------"); } + /** + * Proxy for elasticsearch. + * + * @deprecated Please use SearchController instead! + * @param body query + * @param schemaIds schemaIds which should be used for search (prefix with 'metastore-') + * @param proxy set by Spring Boot + * @param pgbl page information + * @return Prepared query with post filter for authorization. + */ @PostMapping("/{schemaId}/search") @Operation(deprecated = true, summary = "Search for metadata document/records", @@ -87,17 +97,6 @@ public MetadataSearchController(SearchConfiguration searchConfiguration) { }) @ResponseBody @PageableAsQueryParam - /** - * Proxy for elasticsearch. - * - * @deprecation Please use SearchController instead! - * @param body query - * @param schemaIds schemaIds which should be used for search (prefix with 'metastore-') - * @param proxy set by Spring Boot - * @param pgbl page information - * @return Prepared query with post filter for authorization. - * - */ public ResponseEntity<?> proxy(@RequestBody JsonNode body, @Parameter(description = "Contains all schemaIds prefixed with 'metastore-'" + "to which the records refer as comma-separated values. " @@ -112,7 +111,17 @@ public ResponseEntity<?> proxy(@RequestBody JsonNode body, return proxy.uri(searchConfiguration.getUrl() + "/" + schemaIds + SEARCH_PATH_POSTFIX).post(); } - + /** + * Proxy for elasticsearch. + * + * @throws java.lang.Exception Something went wrong (e.g. elasticsearch is down). + * @deprecated Please use SearchController instead! + * @param body query + * @param proxy set by Spring Boot + * @param pgbl page information + * @return Search result. + * + */ @PostMapping("/search") @Operation(deprecated = true, summary = "Search for metadata document/records", @@ -135,16 +144,6 @@ public ResponseEntity<?> proxy(@RequestBody JsonNode body, }) @ResponseBody @PageableAsQueryParam - /** - * Proxy for elasticsearch. - * - * @deprecation Please use SearchController instead! - * @param body query - * @param proxy set by Spring Boot - * @param pgbl page information - * @return Search result. - * - */ public ResponseEntity<?> proxy(@RequestBody JsonNode body, ProxyExchange<JsonNode> proxy, @Parameter(hidden = true) final Pageable pgbl) throws Exception { From bd980cc9b2e49b074bb3db9ca502b4df6dc9bbaf Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 13 Nov 2024 14:37:26 +0100 Subject: [PATCH 143/181] Remove unused code. --- .../metastore2/util/DataResourceRecordUtil.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 6ebd6937..a1264793 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -304,7 +304,6 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur DataResource mergedDataResource = mergeDataResource(oldDataResource, givenDataResource); updatedDataResource = fixRelatedSchemaIfNeeded(mergedDataResource); - boolean noChanges = false; if (document != null) { updateMetadataDocument(applicationProperties, updatedDataResource, document, supplier); } else { @@ -331,12 +330,6 @@ public static DataResource updateDataResource4MetadataDocument(MetastoreConfigur } } - if (noChanges) { - Optional<DataRecord> dataRecord = dataRecordDao.findTopByMetadataIdOrderByVersionDesc(oldDataResource.getId()); - if (dataRecord.isPresent()) { - dataRecordDao.delete(dataRecord.get()); - } - } oldDataResource = DataResourceUtils.updateResource(applicationProperties, resourceId, updatedDataResource, eTag, supplier); return oldDataResource; From 16f5050ba041f4ac3a36d988bf8a94318d7ddaaf Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 13 Nov 2024 16:07:58 +0100 Subject: [PATCH 144/181] Remove unused code. --- .../runner/Migrate2DataciteTest.java | 184 ++++++++++++++++++ .../migrateToV2/migrationDatabase.mv.db | Bin 0 -> 249856 bytes 2 files changed, 184 insertions(+) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java create mode 100644 src/test/resources/migrateToV2/migrationDatabase.mv.db diff --git a/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java new file mode 100644 index 00000000..d418989d --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java @@ -0,0 +1,184 @@ +/* + * Copyright 2023 Karlsruhe Institute of Technology. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.runner; + +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.kit.datamanager.entities.PERMISSION; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.IDataRecordDao; +import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; +import edu.kit.datamanager.metastore2.domain.MetadataRecord; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import edu.kit.datamanager.util.AuthenticationHelper; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; +import org.javers.core.Javers; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import static com.github.stefanbirkner.systemlambda.SystemLambda.*; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; + +/** + * + * @author hartmann-v + */ +@RunWith(SpringJUnit4ClassRunner.class) +// Or create a test version of Application.class that stubs out services used by the CommandLineRunner +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) //RANDOM_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41417"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:file:./src/test/resources/migrateToV2/migrationDatabase;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/migrationRunner/schema"}) +@TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/migrationRunner/metadata"}) +@TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class Migrate2DataciteTest { + + private static Boolean alreadyInitialized = Boolean.FALSE; + + private MockMvc mockMvc; + + @Autowired + private ElasticIndexerRunner eir; + @Autowired + private WebApplicationContext context; + @Autowired + Javers javers = null; + @Autowired + private ILinkedMetadataRecordDao metadataRecordDao; + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private IDataRecordDao dataRecordDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + @Autowired + private IUrl2PathDao url2PathDao; + @Autowired + private MetastoreConfiguration metadataConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + public Migrate2DataciteTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + // setup mockMvc + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation).uris() + .withPort(41417)) + .build(); + eir.indices = null; + eir.updateDate = null; + eir.updateIndex = false; + if (!isInitialized()) { + System.out.println("------Migrate2DataciteTest--------"); + System.out.println("------" + this.metadataConfig); + System.out.println("------------------------------------------------------"); + + } + } + + @After + public void tearDown() { + } + + + @Test + public void testElasticRunnerMigration() throws Exception { + eir.run("--migrate2DataCite"); + Assert.assertTrue(true); + } + + public static synchronized boolean isInitialized() { + boolean returnValue = alreadyInitialized; + alreadyInitialized = Boolean.TRUE; + + return returnValue; + } + +} diff --git a/src/test/resources/migrateToV2/migrationDatabase.mv.db b/src/test/resources/migrateToV2/migrationDatabase.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..c563e71923bf935787468f804d28d5dc85517d12 GIT binary patch literal 249856 zcmeFa37i~9buivNnvrB(mV8R`H6GcP)|REdr+XcO=jhp;-MMy-J$x|CDXp~HUGJ`J z%Lm8@w($W58!Q_K_&80tOk%JB1L8>H5E2YHe2#650R!d>0lq-~A^G!5zE{=N=S<J^ zNV~!WEm+-MQ^%`U@4b5Us_MPLer|hpYNocgpKWL_g~{t0{Vc;U+iSb`Pw(xoZm-QX zDhrKzKdbPZ*iZyn=C(Iy=Jr(<`uXiwH|FLiXQm-rbE>gW+ufM!uQeE^De=rI>iFMr z3>?S6aSR;Cz;O&5$G~w69LK<M3>?S6aSR;Cz;O&5$H1!r1B1uc|F4E&I1a;c3>?S6 zaSR;Cz;O&5$G~w69LK<M3>?S6aSR;Cz;O&LDF(DX{>t{L#&o~DeYUczG2hP$+xJzj z?U&^$1241vQhoc}%*;YRC&Q@#wo;X4e*41YzD7SMNexCejsR4)PdBbz=x3$v3p2I; zFvlptMfyW037(^_p#mq<6y>{=TJ~4Z;%DHim%;NOMZF$9FQr!e;yL_Wb%FL=Q`Da8 zeu?rQq<qgmgnwUu=Vtso>94itDSvkef1mca_Pz1zm%;ZzD)6;&oWA*GE&Z&mTKb9) zqwj;%36E*NpOes@!EbI}x2EN9-HB&x<zG8IR-3v0cKSa0J@iNE$LX)q-=v?Xe+vCM z;a^rZ>A)K5j5E(V`<&ppp)Kc~e*xt`BN$x!*K{neMv7`M7Ksh2@nA6$O{u|9t`tor zW1$_PST<8sGsSW;lgQ@Nkzz8NDW}z9BpxY7Lfcj$<hRX}jE92J<Ulf03}&+MUn!N^ zuH`mL2V;Ygd?Z#>^TE+bej=F}*v5%spCem&Se*#DWHqW@)#pDmc<$PRdS`O^WE!dr zLRm{+NGhtatcHTwjODk^{dDW%_Fzcw4u-SOe^&7PwV$LTDb%Z;a2brm<3WI;P|QaV z)@(yS5)ssvu5I%SRxp`~tK*hB?Wpjd4U}-Fj=UZyp%@t`92phhk}ksvqj?ShbaS9P zY37sBQZbv?VX#o$fSOVBK-lqMbRrm66OmG?7>pEx$vBX6vS_9O86fHm7SxfFnu%G7 zzc5iKs_C-+OF0sY3t>4LDM#Z`wk*VDu^f@OSXl|j)QFhiBu<18a?m@XEDR=Mo#I3e za;TY7dfQg?1CW0?r{)XU%+~F}t%#}0v1}@(#-LMXN(nk*`A$bNkpWfvJqe_t;Q(b< z<4KZnFk2`BM*-i$vsglWi!x-3gId8{J{vE^jQ1gEv7AyPaeIoTB`AP1rPXv)%am6K zQ0tmZ0Dv;F3Dkr=xl|m?<^kQ>FDW!2mJa9Df*Q$#;K7~IzsdvoY$=CJBnJkIh4NrF zMF1=0fI~D?6iR@&(IojA%jSp6xkzjn0MR?5i;_}7&6iEmAOZ$j%a-yn75~y+6agUy zgIm__BXp?KfrXEbj6w><NWK^xOBM%%Y%r>(vYCNGxtJ|uf#E+FB-t=&(!%YQR;}Ed zXd}oBt_VS|`}`r0N!PiHBt5q$2?WhqVA_To=qOzhA^#R&=KK7enHiIQUX5k*@ngiy zqM}A4EF+hrLR2aX;aH+9C-``oPsC%2sD$H*nApP1@ocP=M)Todq%hcZS|XH|iWG|F zQVzNZykAVFRT%$tZkyDnkpM~wUY5<MPRU_REk>)Qe9~*9Mqxzw?aABOUI7>}9qGUi zE(JzGfPs8*oABL!2JSi!xa&sm+~sY^Y}^H0ZZaFsw{qM0J#kyi!EH9O)wt~fcW$%h z_QY+_TyNYK3sKbS_fvo417&Y)eB!dpuekDc<=0oLlz$AE@IhZ^Ce-DMHb*9s>am(w z6S24=@=UB8O)!bFz>4su@@$z`Wj-!TYB<7&TX@lgl1!`Zshlw3Xe3nv_G_O9NO8P3 zQuGF~h4$EBS5lk+QY?5D{E}1hw;Mf;BrgM!yv0Q+ZBxn<X`<$PqfIE`zZ|IWKUQ_7 z!n~@f3Fi17s~Le|6`2tuWjV^qWkD4{y-P%uG8d09f*cowL`3RNg_?2~$qyq1CXz3Y zCr8m+G#QO$<0^WMXOrlmMlxlV$8X6(0jp<&kxV>=^CSnf6j%zV`Aj5*vLy>qoJ-FQ z+E)yvr}TtWAsI&rrk0o15^|+HuA!8}1@x~xu5PT%gUsKmYfC!%jR^!#3$CnVXj3p~ zEgEjJs1^`Fv1~e*OzH2Se`iYChd#66Trj8VjiN&EnFBqEypmajd>Ly{{o9hWY{{A| z!~lkP6ziYxp}}GK)?hIbK+Q~!s_{fVn?^M!3p#*i9U582K%dHLS#+3TfvhED)G-1= zHld|}3Jgo(ND8f?^)v*r@t#*lli5<iQ7o?})I3^~81SKd>X`YhsjzxApc3a&D$-9Z zIgB(NURZ1=BblNV7Du~fjI^?jHCU41;fq^S2;$5ng1WMayRwNqGn;`vP<S=u7-LyB zFjxi@>cd#pdso)6i1MY)WQ0h|7eQrfaTA*+G&(-b6tXBV8nxoolFPuV*$skM2Xq9r z1V94@Oz>Lk1%vfE&QXNXG;Tc7<;)em&2pkAiP+mLcV$nroEmj#T)KF&&JNlv_qq<V zT$@s!W;xV+Z?hbf@Rwnhd!&mw#j^ZF1_>8s%VA5fblOFeajNKM38Ex{>;+SsX_W)j z=k<WNTV2$Hc3^tKoDf0EmKqyIYec<=1cs<u98=XykO{(k7i5(_fEwrZSAaN*o>d^! zhRurC4q9!E>aRj4-?aFhge78c^Z_ta)QY>--@6P{^!L#3rT-WGKKlLi2j~ydAEG}@ zKSF<m{&Sl0zlR1)81~FkCJDwVY^N#&<1p_hGqGaJvb!{#&j^Kr9M5E>1j`R)lp<eD zhVzN=1lWPV09bZ1Q=t?^MxD^MkVUK@Ygi$eNahR0KL2}Zz}v@N@TM1cCx{Lpg@WyS zWrd~hi2CUNUo?oh0YXM?tTvJKNDfxv1!OCNpF=^L1OwdW@g#~Yha%&DAF8eHURx@e z83wDCt_GAfD@L1&Z3_TaVp!;%y{Y%3QJ*GFIc>Iy;dpFpj2Rdz<_07_JCbJ9Xl6u+ z2;&Jdyqtp_BC%8%ST8>T18iXp&|$4Z`~ft?&$<k;S%v!`cK|FDgt=Nx4rGui-VQ|T z*mkJL?4UV=S`1=jVXe*G&{<^mGGxjJ5mNWKA~hq1m5iK=m1HKG5=XOYnjcCei@7vu zlXoH&ES-pB+!6XAgwSW)5Q>X;LZ&M~JtEV!9<yV;BlE)unG3GS99D(#VtQhDY@(1D zEsiIKhEp<IkXU4hMgpiUGGUdKNT!l_{Q%nuA!JL29Vf6`+355Ugx2qNLaV)^PB?aj z(}80Hly=nWT8Y`&wg|?RI;r#{2(fQ;MQnU991c&6B!zH1$(N!^K_1LUQjv*4vn|_9 z*k(TqS_D-Yq1K&r{~Y1?XD&EyS7#?AyTa^1avP}Hq1&}O69sM2-AOo5)JpNR_xoWg z{wn=7`mgA}rvHY1ivC;r>-68z-=P1V{s)+fzlw13fD29r)NDDGjbYjibwWmTJSmry zRBRY_JPM`ZELR#FODM?{<3L6<JB~=d56nem@mH`2I{4K|IA25Hz3XVfD`Y3c5wKAV z#-&n37|NEC$-FQK)+h&fxhyD;9RS3?J3;&_1mb&+7DQ!YxFAn3ql2u%ih~ow5w18A zohS_nEg+^Q24Jtv9gDdv&fx^}uMrDg<;sHLFgwl;42?}>#uI~Sp(w&CR8aT~>>y#; z25{I|&_)`a=8L~U=z0EV(c=o913gyeZ^ve?^_bmkOPZb7%>NYX;u9`iG&Co&0YSEb zI&sr*NlwWlY-&Pg5^+HqWLP0GmWz%mj?P+^9`58|!3ao4vJ|uH5Pyr{f42+#gq+Hl z_6r@Q06#V?l~Sct7<PO!X?d_Xm>*20M<&3SYEzW7sydP|@S&4{zm5oa&Xs_r0dWKx zREy%MIvgLA#bR`n=VD3N9K{3-$g>f!qhBz6XncdfvQN!_htT6THS483pvM)$7W7zH zyj4$jt;XzVTM{OaI??(K)Vue&41Q0Rg~aesVmzksLV<}C<6x~VGO^L2ggRVwbkJOm zxl9CF1}CI{j|kuzR{}_llwxCpBkb73XewGNq$VPKN=PRaodCedYa@U*ed{F0{sH0X z2aAIzS5O^@vhXg<nXRPYS`Q%w=yF?9=p@Zx{r;nK<p3xR-=v?WzeRtW{to>N{g3o_ z>HkeXOaBx794HOnL?f}+btE`$JW(7B52m@%!Lji?n`ii3lo`roO$xP*gfZOK{@Qu) zX@vcMbiqC;-$|spg483%UF$Ks)-%Ta-$I=`<kmT2#pu+#!Ga)-k4TZU5|7Cln8k-O zOs0@a!iJ7b@E#%KeH#t;l<RO0Nl8vnnbZ)MVAXhuk>f+LT!KxH5uCW&W!%U6I|wCi zGPk>B21Jn?P!5z>L1@QfuJxFm>wUnVK?rqUw2u^7Hb0(=6r+R8KuJjsW}|F^&xLhK z=G4XD3{*-5X=&|?oqhfhq0?>At`+Y@U#<Z4h)mad2r{8#y(9CxG#I3R(8VA<G+G`= zWuuW4n%>FDP9ve58+JS1fmT1bOdE?dXzKd5Vj>?IKs!@yx8NB@&xQ`jl{1la`~5~U zUv><R_W`8kgaH^>vi~=tnlZwL7Q3uv1w~TvA<&4^U}ncb$kzQn|Feih-s~cTY(?EE z#hMKx)dJ-<M}sZXnFDtJKcVirnG0<#S<poY$!R*bW(P*G6maNz&mk<lxQJM=HDwdV z)?ZpBj}_vwC9B!Nwwz)u1|7AJv2HY^DC+JV-11$k(P8j|)as|T!`SQM=n(cnYCZcf zexCfL)9~|@?`Vg%Py7CJ@J&%`51@34TK5bpM^PKDdk8;Iezu68r+ggcr>N84_Tut@ z@we*9z@sO8VC|s|cb|H2({IjvCHTU5PjCC$b~wKMtqb0@V%Ku2{uKQ&-@VIzv+P+v z6{rs<Gs$8yl494Uu7{)5p@qqXsYda@Y$Mbk4B;aHA+%>0nyf<ti(jv<Ozp=%S7v9Y zCTo=iaI3d-rnb;nxOjd6T=4A+t-5jTd6(O3(<`ymu<FXS=Qry6ckG>9*io-6RQ6S- z;aLCNj=9F{%#QlZzRKkEjv@jlP>26LMVp|;FsQSQxqXxK$f+NKH4;xlSMX6%)dDhy z8=S}BhZY+13+4UujX4D277U@K5*vu91OT|b8-SJC)Q(7PN}XPqI}kvSe#~dW*~D;m zL*3QWbzA@E!8zf+%Efby+RR-2qCNA_KMdLejLJY`x-nOoLLElRWkD^Xu3Uh-0_|9^ z+fu|!Z}N@#nf-G$OaOr<^yc1WW^YtyZxBET5=~Ubt*t{U$8f^MEPFA_7g?d76Z={5 zG61$_gB4d{ff|53uN%ta;N&r|3xNBL1-PgaxXF5BdSS9T*_hL*Sw98ifOCFw-|SQa zp|Q^fnxg_ckXzdUi5gs(Y|QUS8f^!51DwzLO(cHq1OliI9Uu^;0E^+`2M|KrRc_^q z+}z~8%G?378QdkPqE??tVn&4;Z^RI5{x9dDR@6DjG6Y705WHTAe+Ke3f79UT2@)&$ z6CfO55I};Ymy1|ChdXqm(HHFgb7t<S`V-j&qu|;RqpA7*)jf^c0&Yq}BnYs^QMHVy zx_iS|JRem%SfX}sXs$7Ze5slwX6<WZzBV^Gi{!o~l{i>M^vmM<L%SL?sTpm4u;i;u zEi~q)p>jvvcr-ndU>i>B-lchP7;AxJZgSUd>j+L&<`+t{XdF=&+DNY*tSDTj^=x*3 zb!u`Re8{4#=B$EQ3je0{dZJOmIZ$O<K|B|Ph{(a&Ycnu)Ojh?V5X#laX?}sgF`7k3 z;$YdKar6G^`sDPke52WzYfRT{-LZ&nfU0MiVio-amv(Ui0`gfO5+a}r7{W_~3j5fd z*FeM)Me7-I$BTM}zYLbaUmCmMS3=VGy$#iA*{u|ZQ*>&>A2ve6DQ$@i|DsU=ekBxu z-z}q$zs(UgKP`ib6ZHw#HiY;WZTRqOvt`I|@|<<JyiCm?A1eA71ttS^upfQ;Pd~A8 zSAcpl@ZROOu6Sj|H&#-CCj;hG^BP10R>jR6H+@QkL!{d17&_gy{)K9#ugBSt?Rmlq z?16dly~|BtUyFg=HF<SoI#bz)S<t~(m1c8tY7%Rk7B0pN#R6?!gh|t^0Atx!2B$!N zz;;)?6VC&Iy#VB`E1W=DR7T)_xcEpk9}N`T3eh(!(=*ct_RZ{{FD;$UIR!AjVuA53 zevHZ43J8b=onVecAXo{`DG+oC1_Y+58T%Rw72xR#YDiD1PgZm~v#u0Og;wWbePOXs z*<~5odSiBBH`J#sIuP5!2QMpxePDV5V#!r-o{*+9mn#LV%oVT(y~Av2|LoLErQTLt zBjyE6mF6a)`Vf#<qkrf8>>Nnhov2guAi)~kPObi(ND14iNpF(WrpYoJ=2?*m!v>?k zC>noRXahAdsyQP^Et0vC-~yU0H0G|JtYHqY&J(*U^9UQE`P~(^Uy>VjxZ>Ott4+2p z3re-gv*EBLOQOh2VTrFam1;O#tsy@CTN-nQgCH;#jZq_C`iS9AeRS{#9vb|n4GVt7 zV}jqX+}4K!rx+N=Ult_eZ$JlrT5*9BTG4=$ap!?=xo(0crj2HDTGM+$LkaAIW!X1Y z4ry1XxV3+f{GqL4J@ALf3m9Rx0YOxQIid}*YU5yx7G!mZUCYqHkBiJ7GQ%wif3Q`l zS#8!7xgk|~POdd&v8nKlN=<GETr<qeRe`HjYE{G^JZ29YwVQ-(>HQ)QViSxxQszHT zfJyt&6YgL2+f^^DKD>qkdyPSz$CrmaEQ32QK94UCk7UVxES86a=WSX$*eA4=Pg~7h zw_YIC23s-k0?^e~f`7FeVz8LOMXfdxP+y=%vgA72<)O^8f7HuE&9Y|`q?T3H5v*}` z1@2#ED&IXAeYH987#V%1!N&Jn3!K_YrsE=k$8*UsTqMi@j4!M<arxU-c#&W(2tl*d zc&i8T+tmt*B(Sy^uvj4pmoKT^QNUsXH@yUQM_#Ml5FATrcXZBSh7rXOmzp7^NBL;A zCBrhWjos1N@RHdboej0I03Ai=@=hye>oh<BKpc5QwBn%&)<H0Jt2N6QNnLP)mi_9m zpE`#|vz!_5_HI8_!r{f4AkAgIwt7HX`m5cBim-X(aU1HZ$A&r!bK>DO=A1Z)r$j91 zbhDLn|65yz8@AFkF?t1spkXRPE6PRP`@Sl$o1*+9LmyrRtHuwky<^?a*FAmWW9zBF z2iBUfzX8LpE&6Asb?}n~^FA3IiD+4Ao72Bu0`Bx{xr{#UURapz-?{UeYp&VRxVD0J zN_Wi6?b?Y<Ys3(SdVI;{K;0o*`&h-%?L3Z_@NZfZpcZzu9=C|tz#NR-9qY{gAC>i( zgAOc(^>`kD{J8~4%l<jB=lmY`&ldo=r%yC7_+i?OgsfNcEhMCM!7;Efce#>~!6I<B zqqY~qFUpL-m^Rj(mD$OiSF^;dI!}n}wPaSjVn4(1b@ul`Yx@CHkF7T`wHqS}8+0A= zz-r}o9_r9dt-t?c9akfPvXZ-e|7s8@hc^6f!%HW9?c_&Jp#q0Cn4Prj9k~tk?{Dwu z8W@q^S%6JAN2HZ1UCw@36bTRlOL6|=T7dPXlT5_E`<QIcSo@`~!E_2lOFeB>wy&WZ z{DE(+#A|Mxm`bU(4dcNC&1(dsO$O!;4KV%wePEbbIvcd!!JTNkeJ9KAG_kO=L+j>t z4P2(SnxnOTShpxl)tunP<93Z8fb6O(iV_wpHBOf64TVD%YJuhJa;2eExvC;H1qF$> zKlbgK1F*UMwUZ5l_#Z64Xw>6@UnI>M^oPhVoM_$NTs(eZ(dLa<<11`Utb`?nQJP`c zzLmvlr3yPYlEO-?(BO~l_RRq}v+&3%24Vg|C`=qklo70(AC0!XKd=`xh94FlSOXfv z-KYNQ)Mrk6;`E0%Qh~cqH77O?W>4u@n?0|C6CA&?z}SNcIc$a1+!6%=y#(imuLn4v zIn5xg|Nnosbyn2OVF<xTWO3`f?H69s+9j=a_*b)IAGiIYGb!CpWw>lAIt`3?3T!!Z zdZc91Vl}{vS44BhxyDzc?e~Z_M>S$RN&~I5gOj;GUIQ)K)<Fi^5}q&3W32iT2u}!Y zcc<|$maR_weRKyrXJJNo;&gL%?Z->pj+=@iZkB8+5}<jsqF90n>jnVt;f*GE7MqBs z%^LPY>$39IYZJK<X*krO`mfj0E40WRTb6$pE>~Sjop-hNJpUKyGS;Qk*2~f5tb^31 zKh0^+PoIRJoBP+}-}|muzj5Q~p^ayqwP`uTB-z1*@q=&RhbRP4VsqUzoN?VTIbCmD zyMvX&Yb~FgtbHw1s$gs7S&E{cZ){sfgSv%_F2{Mxwi-CW{wdi%W!opu*grY@CvX20 z?Vn-$r(%ViwL>AWw!DJ9V!>XqV6RwEY-L4zRieEr(O#8kuS&F6CE3eLVi@7GpA3bt z9Z%@``Amp8ySH(mU#`lE%nICgpz8igqrx=c1R2fwc*)0yeZ1`B6(3*m@l_vR^YL{b z-=Ld5zUdPfpTPPA&L{9bLGTHpPmp{<*eA$7LGcL{Aed9?Uq6vPi?a1rvUfnTcOY!X zMcCe-uzl!dduG|5S+*liwzn(W+g0pk6&q23#l_kMj<MCuF}9jH)<%IGYcI<>GTXr5 zID1*n-VkSp%1-JWZ*PdV^@!u`?eg|^?bOVnL9P0Pnop?vgoaOO`b5SjvObaXiM&q~ ze4^+RC7&4fiLy^rd}75XR()dKCpLUy(<iY$iT6o@PZE8S<dbBdr1+$YPpSgD|L5e! z^*|F=*3yN7My3K|{Y~JlUt!kvC0M^?aSaw8Q!`a`xO=t!k*>@d?{Kx`RGdCD+n6rS zRi@`FHGI)WE2>E)G6vcQ%Md*3pG2IIPeOtEm*bNRpZ+V6C*+e1m;OciNj@d5XXJ|v zmiEQzl8AhAwu(5;1|VmHp5qC|NYSvh+cp4r#>WaiR`Ib_A1C^_u#c<K3N6tx&C{X} ze&$&kWC4f?-p31c!^ew077788)O-@-lQ<vC`J}Lq<>{)A6)B23Z_hV2P_%Yic=MOv zi6@pb{_Ui*&Rl`wTJ7NB(ue;aX1;O~br!V(2gKSTkY|`YW%5*3AZzH3$@+3!lGQ3; zIr0pXr%av-c~;1?N}e_HY-rVx;++2EwVyeXjjNOIruO6+@)XEZBu|Mv2^c(So+r)o zq<Oyf2(88a61G>TVAH6dWpMy7kpuv1ZlA)3&QUNh6awtpevxf5I57<$Bq~_d2=|4f zgRu}Ci~*vph@2K3tj;yI@1KTi!F4@QnD!M}7}bvH_bW!|Fad`Q6L=^xQ>*QttxVSr z^f#N~=4S|`VFD?qL8=f)d5B!*0JXx@-9Y6kS|BsFhCpprM1ce{t4VdyGpu0@t1K$K z4c4aRkKrFBaLYQ}_7H1A-2%4|#yD1_iV1kQsRv&pXa@7Y7K9C>xz2Jm9d<?1@!Srx zvKEZ3+O%Sp;ZZHx9}4OQ*r<JoK&%jmp+OWBuBHctt63msWejnH<xGg{5F<hl7l-4( z@eKm80QGZ9O<~NiaW$q+@ZAn(1;bovRyhlXF_<#bpxM=0AUcJWh3aY!r*jQS5y(4V zt*{b#7ixU1qQ5tqHBNtTR%?2Gk&(@JRy6Ve&C5^^f*!-qld>WomSz22WaWnbt~3~< z&Pu(er%Q}dtC2hs++1Pw_X=!M=<jvjNDm8jLC+tS!-AeCT&dRdeub-WJ%yAF*Q$bE zXBe)pFd*N=egAwibtXmR0g)d9kt+g`69SPF0+AB}krP5q6H7#_h(z3oMB0g@8j(mj zk%$$Mh%1paCpNU^MIyE&QjbLHkx26rsYfDUNCXV2s)0cs1Sldhn|kK~S7ztoVggB* zIZ@WrWv(LV126M}pbwrb)MO)Fl+nA!3_z@Py&g$0^Mo0*&T!Zu6o`jt5Rj|@t>|Ty z8lw|UwbEdKa4{`a>y4WJ4%=3O@h&Q?KB#pb6CZ}BTB$RH(AjD&tmkKIY+bL11qr6t z&(><Ztk=m&6@!?#3eV^S#8s<|jt8zDmIY9dE~Tixzt^59o+5nXc!}`DaS-8&;~v5j z$35J1m|Feu)f9!E$OQ*Hu?r4(Viz3n#4b4CiCu8O6T9G`E~PeY9ENEbo@lj)o@liP zPrTZLx6M!70{_AjP1f)SlXWCKz(hw9;M~sUg#<^=%0TcYa5tm-2P_~bIWDf*%v?_Q zY@bH{TJF*P@U%KB%Y)}(o>MZ?#Dp*+X5-+8I1v|9;ITw=(C74OWOGthZVM-XV9(X! zyYDl|pUc0y_;azBZQp{svzx@QYmXc_wzq?Edqn7I>+!`_xnoRVcjSf}URe*m+#aW& zpg&80j{ZFTB>e^Yi}aW1!}OQwuh5kLaWri1u6cR37~vAa*l?POB}Z9V$q%5AUK0u# zHHchf^W|m(2S<yD6p`CwdwR<_J%Pr_%{8yRERZjR>g?i)(hce(85s{K=y_!PpG9cb zql2|lgCwOV@)OzIcy1&)GA0hE;yFGu!7Do2!LwTjv=@?>sUDF2IfV3|dSVW(#!k%O z3h|Mk+_f@7b<q9ZQT};^@<~@Wc4ACOk0wi8SR6}@@JS|-85;-R_q^_Zo^3^WXQyjV zBJ_U89lhWM%rz`(V%QZN2R^M%-&);Bb*P7+61vqpD!+hGIqQnbw47s$Q3&-}jI(2j zNHUd@!7u1&dc<tgPQ5DlVov5zh$Rpe)*jj2*nJUU^f@<-S}N&;SXbbBM67E)W+!_` z?3YjvA93knqLhiFu%||~9SJCx8;T7j@`{oeVa7_~QewO`QBvat7;w7=Fc!(j%Mi2` zBA<4c_zfqWc>dBunjA*(eri$R6*EbRi;j%uq|(S>ZXhAD%7BuJM1f6g@FFZ=NOg6k z1JLf;%9jxZ?{}qOK0a1p_?#F~hesw#VQwfknvw?v-5a)37PO8`Co=vDLYkYCxz?&W z5wjbd4xCw8s-2K<tL7+3nWC0&xt`wuy2tbM3-tHs7wPZQFVR1se@Op`ewqF;{S(kV zo=1bA2On`xf>u{{*b6=K!wCN$cgSIDK9C-!$3|Oxg%?m$ZZ0>iRoI>Fb!*Myna|-# z@Ovn*iXQ2<O_&y2o4Mg`5mk8J{~~g9`J%31y9Ba^Y9XLmItlKx@dh<S(MDe#TwF$Q z6yq+QD@iS&i2*wrQC`u`9&B4%eguMna5L>4Ve*C_Oj1D80@DzcF1C&1wg<UB@N)Tm zG*BaiF)jWhUA$asb~xQQM79_EE8Ck>jRo*6FxRhC>SA4yYY5WC|68TCU&W>#nV5Qn zDz@&(3fQAyZ>(Rc6GIL*n^o$ZZZ_j2nHPD(h(|0{oTQnY*ov@N0Iz~I;E$**wfY2# z_N`vCcHN2VH&E=B5t}2JA<eA^cW&A^S45HmtGA>GYQ>CsdNr`dQG@ljwU%nE>FXS2 zQpi0^Z4zAPooFdxrmlD7PvZQ64VKJW5_IwPAYaz_p^MA>6ic;qgR>-AK;kXQM!SL~ z(WrlpzR$NVfa?!X1UKa2qbxOAULn0cGuf}9)|PHGWxV((ttIVWA9RTN6W`utCk7~= zrDQ<+*>4nEW@&*Y;6Tx38TxzvlXPsX%`d)-jB{0X?Zw#di&wuEMeDXZI?a8hs{@e0 zJN@PWhV01Z)?Vq>o1;H$E%CD1vAR%USYX7OC^rQj?XQACNDi}nQ>-^wFfu8PIwyw1 zEgdU&IN2R|t=Y5lNY5+-%&*P5_0H<<N9P;e;g9}}-3wU`kIP}k?2p1LJQ(T}p;m)H z#8nyKuZA0JxLIStnlCgZmTL%Nt=iI`aWYW0Rss)RZT93`(i4L&g|!8@ezc4duGTya zZ)?TU7bVP@eW~(c3HDAKtXyw~8!}U6#imrPR|HvT)M{Zxtcn$psWw~sa+xtUwsrzP zIlr|xI`8|p(q9jpuJgW5_g)Xs-~KP}d+p!;(@Pt{dF~QYE?g6^kP#UUmgRIN^>}(2 z0sEiNFEy}mM%x1H+61}KhbRO4wmOpASg+wYjwiiU>M~fNt98B#$F%E8os(J61H!<_ zRSuXo+(gOECWCtW!S5|~Z((O{DS8XL!kPf~DyL9yfBoW8_ZCFxQuJ03NpHP!KI-kE zAK>0Dg(%WwgCXx&I1@!Zy6Q8_H<C%0a3Dn?I39l%!7+&L0GU$eJe6Zv3#zoN+5oj= z6*$poOUwAlm)h3J@{%sLOboPC#|mM9msxe&icNl-g5lQ+FEbjmC>4~j>f0+fL;sff z4GqZJ0p2JiXtki@1H$UFPdLL53W=#Y@<fXfO*KZ)n?Y!#wyr*~>P#P~TDqP!h#UkP zwc{_y_WISQuRdGXan47*{q469P6+BbOOmy9oct0N$VQcdB~eEowR`oK)`UoRGfBh0 zDBGuxttF8^&!UJ?mLG&ZqY-Pe*k?3Db8F!|B<?P|Hq`xY^CF2$m>Tw?#^&KB^{&Z< z-TSLMYBT$G>a(KmT@cPVRCn$JvD}#3srlj7?pecS+3CjA#ljALC%J=d6up=vT-@A0 zHFfdT0#-`a4j!41*WuAG-M$7<$ZOX|*KOBFegH0g4rwkzIm<h5c^542us1im<{wVH zSTDZmWNOnEFL<{q`5Y=0T%?iUtTpo*JjZfq$jU>|`m}iCWKj6?C;r>|oq+Ga@)fY~ zT)V@%5OAKq)l^OEjbw4lTBcJ8U7xg;k$P?=d<3lkDLuB~OB<MFXjMq*dZAZ8n2WwD zgX0lIGM#JceIA_-E;=yaMNtRFRLeXrKo)%sWQgE%k@PsUVe3hP&i+*B3M+A0Lq_QH z2o)MnxACfBol`p0Vze!|)T6Tr{nL}*d$Op}(jrL~cF~6wXzW~ek)Y!i9XcSobGgNv zX%?LhG5Ni6%G0Mv%cy0G2PeHq1H$?isUi-zjC%X2H=i1|YnPo2BSiCbS)ZE<X<K82 zu0Fb(ln_J%r~cqH1(xv^&SkCEHO{7``YqW6qOxN3G<@n)r}z6Qzo}m@L$8`b?nAGd z5>KO7P4!2upL`m>@e&X_tdCp9`{B$GO1favpWq~MndC<*IV2@FzXm0tJOQLZ)6D7R zGh(10dQnmqX#jx|j->{&c&va?c>3LECIE#HhiDn)M=8{{({DX%0C<iyq%qAB@$S>- zDU`2r`UlTWo<Oa(4E-9*`&!HUdQEO`&>riIicd7&bfWZ;mQVxC#?|Kx*_oo_l8f1R zc`(&e{*RuUvoE(=Y{oiV2%fVxem6AYqkJtuYry_wOQDCx*-hc|#sTpbELquWnT9IT zU);c^!1<T=(7?7$=?kv#p|j|RspXCP@pFax41TWMIl5uP>HBYkUzgpq`OK|XY@q6m z=C0k7d-hK4o1U4yYHof3VoRwk)!oxRz8gvf8Y)Ni7Hx?NG*JUxBQ)tg@R5%zDzFQc z@f2c+3hYLCy+-<>0+T7-Exre+;+Aj@RA3KksjDBX7KfWuU@t1@Et&xpm_nJm;>&uy z8t$B-GDqNcoeE5=LeH(usC>_5W^*Ca-VyRp+>ZLI5aD&jAfeJZPvg{b^Oy?Edujzo zouC2>D6=;j1qS@k*`M1647mU5Ypy+T-Ss!z_=Yzgq;H~crf;EdrQbwTf&HkquC&z# z=BrV`uH<58uWLLt)`pO;MFqX{+5wdPh{*CfRr1g)I}=>5hI=k^gDUo1=0;ECZ^!r> z5-ihmLvQrNoA!nd(yH8ZLpRZ?(sQAk=>*t#yOW-MkZz$7_PmqOt<XyE(&kN2$_rcA zV1sL0|8ZWn?~2o@+vwZrJLoslchYy!chmRKZ=v5x-%G!Zemi|1eLwvU`knLx^db5| z`XTyV^uxfsx6z)+$UaiHV@!CJX?M_`IKtk<o4qwb1lpZ|wB9F!yKo!c<Y3@#Xrgz9 zyN5=M;GJgPLT5s{m9+P%=B>CxT}5#l>3A<Lc|`K=ZMcg^(&*c9Nv~vdpSO0}%rp1H z(Dg2p-+?*9`_R3U_9VpZWd4A+5o^<_L(r+-CyfWap`5VQLpZlb26-1ftdVZ-bo(%F z=SZjz79MAx|NaZFq_uU#3HxD*2<wRxZh30sxi_Fy$tCB%e9@H~sXEo5n$#|8H#JG^ zq4rW!)IMsOnxST?tEf3@o?4*xQ&&^hP}foisOzZfQAujg1=HKFJeRtGx{-PV^+xI- zP2EIOH`CNDG<7RYf#w6tliO+P4w`y1P2EXTchS_{G<6S6y@jUUN>lgJ)Z1w4?KE{C zP2EpZ@1Uu7($oVqb%>@Oq^XB!>RmJi5*YoRbHTogU#EBBLvY3mb>XR}HlKquB#OH0 zf{i;~x0$l*p(eU*GQ(UW!0HIrFR&beRR*a4pvZ%I4%#-Tm}pAJb0D5<@H~YjD3)=U zTQGIOk_nVo&@4e;L`oj~-LSQ|^L6J?02e4?pi+UF1S&PC6=-rq(*ly)NUkDLgoFwr za8T4iA3&lI$qK~vh?fy5A_7B$54sGRh!D$x`h+G7(1;L2A)NqE@zD0!TkmAbI+lJJ z*Rb?oEA@fidJkJZ!hzoUAXh#X1HJ5_i+;_&etpYY7M8N+R`O~vH2v^9u=iO-Tg;9C zw>#keIIF;oKIyVceDy%XUhLlQx)noFyWq@|%tNuK=s?q6?mcKryKQqaL>UvoGp-Ef zKIRMTvX^_py&PN+lVm|=pxnL70=w<yo<<wCZQD~I))=_$l~DJ7vn()aFZZu5<)j@9 z3)he#Fc}Nwp7jUz*vq|w4#Bi(4;&J~JyaHOiu(6tAh6e7?!Vm2$r1o6VmRNsJTPT1 zcRxCY(zZQ;m*A8cryw|QT@l!4FZYOhIZ=XeZ;FWU^2&<9w7uL9(Ls>5?MVX132azF z<-V~pFk>(GAMWKOk%v3Y5Rdc{-=il4X6@ykK*tr@wg)I-1y+(#|L$KExXNDcAKc4@ zW$?ntaR|=et_sZA%e@C3{b<{s0yjl@pfgnNh1G$1d%3@GF9#Pr8A;;8P!1wwQPgD@ zeO#yjX98C>5Q{Vsz3)QAyBksBBqFOlh(`8a;(PD%DKvWf&`?Yxyv`sb&7vM(g}|Rf zu*{Q+sk^s-OFXg&?Qoq`Mm`!2Q)}*f2tQBkhwG*Az2O1PPIl7NbNKtzk=HFZ0<;HQ zBeVym`raggi^YSRUf%T7=8v6m@0nEK;3nc0kYD=;>k3tK3FXBVYWv`I#xX+E&I19` z7c;!0zOQ}cu7ly=S&MUYbSjg$kU(DYjwsTj*W-1_(bqAHWOpDi6?l1*>HaI;#yeRg zo=#?xg<>9EWx%e<v>2F-)lHdk0Dg)5l)V9>KR&hD1k~cEOe3{FFh6B)g#LW&46{ET zoUY1NeB16JED_ER(g5VWXPQ8I@Li_`b+!Ah6P5_Rn*ctFTJ@0p{V>Sd-<<jESx=t* z-g9mZQi0!`X~^2&{m*v6$};#y(NaaoQkJxKS*soXJ??|Ga~wo#oUh|PSPg>d8h?w| zCam59R4$SaRzc+dczv*<z<tOEs}@fPZ0PgaNRUf$+Q(vhTHP0T_ADa!g+E*+ThnK} zDA|9w@dn_&gGH5Gym$j%Z*?4FC3`yX<k_Ygbx9i~JFiCK7L0&<JYXO=g-}Q94gDtR z66sMl(}9`5d(SZej9>t9+Zu_rhT&<A8Dw7CL|`sO1fE+EoVNx|ID1*GXdNL4UFy}! ztpF)Sop6nKzYLn`E5UD^`)KI?Ex+AD1zrgn0_vYu6Evf#zd6~MgP}$#Fz()!ftD>M zOhgXP=vX@p9pligED4s02zd#j1F%Qy^4+?xOOR$k8+$x9>>>soKOP@I4~nkqKOofw zP2TsWoA$y?Sc-HDp6;acqYgbFiOL_Z+KVEhyoy2(%mlu1t~p&^>Ot?&R53D59t$JW zo6w~GXvl=pV%gIIA^d?^_HIKRx_^t=p%g~@!sNnKLyykt3`eUeCv7`wy<3A6t3?e- zdpjMN4gA&uYm=xXpo(ZQp4!6H{rZ_E+7{rT?m%O@F;|&FYAo_fSWt_o7Z+R(j1mG3 z6RWveBly)?1R~1zbZ9Yf2XqI{Q5IE70k!Le^A4Z?feY@~`tz++;Dz(d@i?jwvpfL5 z6d`8cj7H+{`6iTJm{nUT9z$lm6G8cb3rr|2k#IE{M&pOZT60H4mNW(twQjUamjOA{ zY}A0Ou+RmNfv1x(fx8fNcWgDG>t%gl3%at@>w~)yq(5(g6tB3nL2X@exdc8n4zVrK zqU#<2m7-R^J$hk3@YK`W9_u@F;qNYd=^`rd^fnXIUp#>bjwM~o;J`kZGAwHutPzFK zUH1^YYPLIUZ-V&t_%{R@`s-m;)n#pfj9Vla>r$+2NLV$aH%l0rtVR3VU)|HFEzAQ8 zU#_7N=&ON0j}(`1NWam#zH&B<r^lme$Hgq}Y-p}A1pw7e&ji-KHs)({le0KVhKT`- z%1$<Zf6ppAAwPpYj#_1xh4A92?1nJHOj?_nUYMJ#?q8UhvmkkX0TBdD5u@2uq?k;R zJ>};9={f}Ag{UQsxyE!&qdAQP=Ng(1g`zgMX#Cs*H(O7F5=so!>N81XTSH+0k;4A0 z4+#s<NDKjHlQa0kv|<|^iPPtGromqt!{Ap!)cCy(-D%maG>21kdcz+!Qo||kfy*>H zz?tSIckKo)#_vum`|SSe)Z{#P`qO^KpS21i`iVw`B<h-(Z3uB~+VJ7m)*;i{whosU z9S$T!MIWQUThP?_Sf4qySOS=PH%4lnEqXLW_I|WF@Kyxvp$koD?>r_^!3{n`)Qgd> zpL+q4UecK0tjKcO9zP~%90_G%H)7{@?#?s{+dn%s13`b0eKTZgRv2tVU%+vtxk>b+ zoAWcR>YZp4)bN+$!HJvJI##MQ%2||SJRaRKAlmz5j{trz9k?p+I}3MMc-c+OZQWQn zR=oT+IxrV_=^~St2hmIu>bP*%dSR?@+IhS{gs`ZK_O}C^6t(`l>Y+<^t)jlR{gI3B z-tns)&+L4Hd6+%Oz05tuL-dU8W;Z@U<9=xTIx|gX(NMBb2CvY`Q8ivp<g;lDv$zdS zDWB4E8U0-x??hMZhl9^`79mR8F1CS+<%B*Tne#L52L4Th1}2)W@;mR;{Q~!)F@NM@ z6IouC-N!uo_5BFSyLXsSS~lpkb*lp}AWJ(f7kiu+y<W`;yaU1cD+`<!Tb>q%+n^k) z`xvJ=NuSHCzKJx#L#BaaX;6A6>d-SgO~n6=D=($hVg!fX0N3jlUUE}e+Vj#eax3=x z;Is!2DxY9XRQ6(=Ii@k(4<Se&W=%+s%3vsfR9gyz;e!augPaMa#T2IraKtP~tNMMI z;vPcKy=;N*-;T*KOnmAHPYM<T(J?UzEp4!R_83O1Z?P`utJ|eE2{{{HGLw+AA*^n+ zn1n$0J0jy=i;{t715}w|bRae<M5s&EgtL!bPfETzOj}OV=TV!s6p>p*)0Q@|+l(Rx zSG>CHT1Xxpw`)zldhA;7LNnr1yg6Uree5hnXk+Bqnu!FN*RHE*OS!8Xxr$~bGH!tB zCafjB{Dm9w<RsssvTNGYV%Unjdfi6)_#eBi=wUiA57$G3P3BK_)0&fkO>cP$U%K4% zk<`WuA6p-|8h+o?yzDi*;r4l;4!5fta8tSox1M*w4d&f&8+j6L&`!eb;yrLvcrV=g zoq`*@``|Y2KDZ$}4Yyrq;O6S=Gp7Y+e{*JF_LX2@_VAj(RUcRzn7d<LVE*Up0t-){ z7})>FDS`da5cPEP4S%}(LJGBeXhYzd-)#t7`_f5)17ABiaNyvkz=3Da3LLom)WCJW zIyG?p6Q>7mcz9#r#+Nq*-tg4sz#BhyM&KZQ@0o#{=qJw(+)TgsoB+6R3I=Yazj1Eh zP4uH7a0YY#mcZ@wZ@0jag%{2XyqP|He&9~}0~f#+_8nWn1Io|02JWGs-Ui0-$NIp= zeCWc!z4Y%c47`ng>7u~f>91`EyTv0H2kxiu-T`awU+oCIlYVAr-~svxCUA&;m<>Eg zALIfL(Jym>chOJr0nPbAaM$ZzJE>##$AcPX|NHA3FN7ZLL73h3n@cBk%+>+3b--*L zFk1)A)&Z+^z-k?^S_iB)0IN;F_AbEeZouqr!0c|o=5D~|ZouYlz~*ki=59dWZb08| zK;Lda-z389U+x-+OkPN#b^+OY0gHP9i+f>U_5vpN0w(tYhNl3V`v9B!0Gs;&nfqYS z_5s$X0jD#7(iy<$3=HB7Aaw?CIs+)31&q!DLT3S=vw+T7z~(F<a~5zp3#gn0OkM?u zoC7@00UGB3i*ta)Il$o@pl}W_I0p!v1N_Ya`sM(8h$^Yz?gyiL2=X=ycVOgg&aN@Z za`#`x_UOo4TC$w{X?)NBB(mJIRo$y&_O6v0W<PkRNtS!kiM={zmzFH|>=@Ylheej> z?QITHI`W<$#mGD3`EQ?jrXGeH{8jm8>PZxs8^_(+A(N*<o|P5I+O}h|u0_5kfwEan ztAHf|vsn@_n<W9WSrRatC4snE5{R26v9d{VPJfd4-5klrk$Bx4iPO!KINiKJ;3jdv zc@j39Ct&cTd7d=Sljix_r${JynS_$p!<)@kb~Wbvg}Nn@Jc=-OZNDNxsIe*7zDmOf z2|8Bo9`C-N1R&c#y??$@HzUbcjF4oq7K*IeY*-@6H(UPL=DP{V3IUnra7eTo35mwm zEHJZS40HoLMHoQW1>O-X&12Mi6V~=o?@<JBh<g8i0$+u|r-7$OY-0s0cp6S+xoUzJ zZqy9$rW@BT^s^E{FRGudRKZ8e_JzrP4M3^XVC3!YD6MEH<ru|+Qq)<dK||DWT%W?q z0z@oF@!}Y!0=_ZGJGkUfAObl~=O7e2gtph-D@_!kU3;%q1wB7klg;<KXy&OmQ6zKR zDg)8lA<DW|wh1BH$ve-o3`B#+>AYB%^*VW}E*R-Gh$KbwKxHgMjK}!}h^DT?CMdj- zE;6;6o?jHCnx03Ls)F7xF<fW#wnVvtqSNC#Ap)1ofJ_kgjSL|PFgG)^u!ASOClH<# z2#*Pb#{|M-0^u>Crb!0Ek|JSMkw^-WR3j1zArh7p39E~wIkBNNFA|oQNIep%M<UHj zq#lWYArUa7ss;vm5TJ;-Y3dzT1i|RMq8NiAHo&!xo~NPI^nn+fd`%xbiK#Jqy2OG{ zbd9hWG}v`gR)X<2^6)@tdO9CA2t^WjgMcKVDI57Ac9>2yid2_@aPc51N<%Q-8{wM% zUV+Hr`k+=I>XJU#5VK1rgw86mq~~XqdR?!FtpF91df1B0GkTq<FFG+X5-00*GU1A> z<AG7a47k|_-zQYxH{dWWC_mNP;CYaut^>Vi8KghphiL`AKXeL31rAg60q~u&9E#jB z1kXz;-zTH+JV^OIABX2*%J&pJSHSQ82<a;z{rix4B|QHfX8V<gsbw41!2v_4`pR43 zc`3DQUk09#{<kTL0+%s<YCS{&gXcbQ*tPaj%6}jFK1c=D0DNl?Q-Q?SDQexN;6gV6 zPe{Mf4^K$H^Dsr7NKt{e0<0&(@AqE{+oq8IAhfsPAQkvA1yA_?*;gp)B>4T;Pr>sb zwS0{aJc<M88{r9ETz(#UUP>(oUZPGpOf3&bDe5#xk3qjqgYWFk6m>d$Uw(`BL_U_` z8~a#>C-_)~?@Or@_T8jCk?%D0eG9aA`a$Z1cSp1*a!7{0pF-(}sS|$sW$lSvaG~%2 zT1in5sb|$jsDI<7)T;jmJZ?Hntv>ZG?TLKYz&G|`15fP32A<f54Lq?A8+d{b8;Sz= zHk)pP`q2|Dv(WP+DR^E=ZTcR-gPv%SfS&&i_}zSv+PwWH?TPeX^qpM;&%@Ma@Y9B$ zNP9!yAG{L~1+U+ZX-}k>z#k|kd44DX4y!WK#Dp*+X5&MNWFju6hDIIHckxDAw-CIp zm5~^{!V$#jhS9p=hF8`@4Bp4-C+N@8pQArdKS_Uq{v!P)`Y`=v`YSZ$e;mbIeWQCI zWVRUL62jPUnu#Sxfs6A4DE#Y$9*92dh{kL0E9@SoBSjCP=$}9#k$>hAN4kBT9m68K zL4716!@E^RMg~w|MC2}5!qnQphRDRWz}cTgXur`F?UIz9$WLT*<GGRK$e1{sis$&u z1h435mpY=oki1Owfb`EHr2o_t(zO~pq1+YXBSE=qWd!BW{oYajd4%#wSCoq}Aw8Nb zaba;RHNq#EL}qL}GpzCn(xx-!Hw>@ZF<SkT2)*BNM=wNDj>O}^SQbJi=Ob{!!9qKU zVOMY*`0OTET<bBr)jKM`fKWN>ipsQ{V~f#jDpicLV~I#Im6Br$Kbjsf+stMxy^X7d zSU#C6CgIe3r@-l7L>PU}4WpJyIw95-xE>MfT94Vu-VysH)Wb(yde~cN_*`x%Hk8OK zN@9c=D}_sm@zO*|jTeB9K`g7uflP2%o!AzNj79SCa;B7ys(A>n9_nk)T7}r4nnna~ z6Jv)Fyq{VWc*RUo;-Vv?IjJ-<m>WoltTLdaB2i!y8@vb$7*aJ;OeT^YflkKtOisYR zj3{`&D+TlMu>!;A#E3dPGEoY1L$T46JSeE<*xCrVb!0k`@mCPiUR)HUxxwkcSvMi! zR*l)+o=F)r$Sv3N8$kDXo_>M;9{nQyeflMc&;3LCNA%0|kLjO)?(sYtgu2ThKqUKt zR5lt(0a2Dy*_if{jE_5}K5IT0Wj$vom?@=Fg<v9?FBJRyFQBIOcxfs-mccYtP7FiK zU0Snbb~N}sI=E%+Fln%$j+E3)Otr{7E7;}Sa2c|Mf`wuvUkr{Vi-SQH*wFIH^Zpm< zU=YZLR2ho^L&k%}NHpcBHkM7Nlf}?BydNLZzMzFrFq#}dvrHxn{{e2a4f{}}R2+mu zUcuO4Bp-<t)qHR?lAlOs2DWhw(?>FqTJW3~L<%5)md>^Q2m}MaWQy&o<DIpEBmqFu zaBMTSjpMclxxQ6>{_mrK8X=v`<&$Xu1_>$)>PTuCQp?y5*k8rQCfP7sRkt^%8VfbJ z70_Q1>SDbr*Mdv(w@PbYaT>djQ4ZU;_El#4r8=<{xtO%b6^VXjJ2oxx4QwH*G^^ZV z!3;|=>U`=h%16WE#&;=2@f-1z{@Jse&Ri*1<w_HxBLmAmzx-_4w~1N_Um`?4Z>;24 z25d9+@Jg=Htij@2TFG-&sUh<GN`b4_q()O&34Vq7D&J&Rib@3xkzsggRB9DgslZE> zlV!PyUib=lAYLg|>fp1rURxQiDpjc>N~n2-sk1zO0h<$xUu0I`$V-^KfTvfS(iAIN zo@QObFDOQP6a7??>dgjz2`d7FUm6nmsR1?xEv2qBx6jP&+OeD4vF~av*!04+J4PFG z^KfgVQ6H^L?Z<bU&r4KlaNFm=`Hsx>>W=)(HRzrPI<~9lS^?`t^0%e6KnCKV;)?ZR zO8~)%U`cTNEkP>~z?1wfLFH?}GGhH(f+iqvG5&LoDmq}eBYI$=5t*B-99Y6(S_TFa z_*;UmK?KsJsTjgAFA2Z_u=(IWFC60>u=Z@|gFlM)o|pERcNs)T^IGq+9$-LueI8-( z_W*;xr+R(9WgcM&AaHxg>-U&<c@OZSyk6l&dHpc!Eq^C1`+R3ocnUzX9eSz4<bdak zsw7rxT8a|ZQtI%BQyQ#NMK85Vv#z~Vt5rN()oKk^OR2LB1?8#NI4+D{8cnf^r?w_n zk<p71<^()jDX@ow=O%cm$!JDXWR}%Z<Z!);Q>sE$Kr@<Btu=7#6{e=tpyp<U6FEj$ zS!q<52Ds*5S(7TfR11qM8<lE<;ljeoCMQ-Z;c9qgQ{d~oQdQtZ05iBEd*cd(v4F=> zTY<6btzMM3mwK0bgkdGr+e_XR9_wAz1Gla61w2#aDu8#TXBd3G6FlZ!1NE-<nD@jU z`gS7JyUt_1Xa<7t#U67n1L4<8ZdA8dS&8eWz2}CyJ<Ipy2=0Ja3A7UB_IsteH7NJ$ z<#f=YLt^zLH8Z{Ih%^%~^Xn2QBr8uK^E*5^M~7hgPR&#cpkTwPhID22yy(<S)wnQ! z9#XBB0HRf}HD~_y)PNTpUhJil=w2o69{}>)x?d~{oUE<Nmi^VUcvTko>SYk}_b^43 zpTplPexW^AU7$VJ6t(BNUqXJ!=X-uL&bR(f?RnB)YtK{u?hsBt?Q!jU<JT|4`2%0o z+S&ZFmVVY&lnxB=e(f1}R4aE*VjPzbeslA>H7$SZPCR2P|JvcP+RXL0LzKAp&>y8A zr@u;nlYXB5DO9%N|6BQrKd_FXf%XH-SFAi?)#^2C*HPyNFBk`}NkMIaUyhcNsd$is zAO*qD+FNzoMyUW+h-3m=D%5ds)(_h%g$S5OBB^3<D;!1%^OZ)pS*?gQp)PC<4n_)t z!L5}lTWi#rDopE&P*;R1gp#RNSy+McuqtG0a;;fyR-{_9!PV-rT&c)4hyucLO|ij9 zJkM+et1-j|*aB9T@Ax`fSt7Ai87zzW3Ej$qOc}(yGN6L}2=)fz!RSOVt|lTS06J0# zYPQItnFba_WQGa0F%A_b3Pm-IY(m&<R90m{6yp)83_J7DvLGvRIm&TzS>ZT_jU^yl zi>iPv#ctTisrhuWP(ViYpbB=DZCkCe54LU(Zp~yfDtaP!t>`(HPZrg!y3MD6I@QK1 zB#M2`rmXfwmm;hB&I5y(zT>6$%wp9!vOZf(WGxo6U{p<IGXsTkF<ZuVvh%^Tl=N&` z0-CfLc`bvEjo=r6757@8NT5}x70%4+z7t{Y`;?(2i=~U0*pp!99=G7fYW3nmTY*X+ z??$CSiiRC<i77PBD7-3*v9c77@?}AcCCX6<u2_yl;t7bCBdZWes)a%mk#sUOQO-nQ zW5I=Nh>39kZgyw9Wb{Od+j^r!Z++{D68nG>RnL^jJ1J2cSWJZ%0u?T}P$ACdi2|X8 z-YAd{T?7>PhAv_f0Z@)463J8&oa4Yor!Fre_!kRJI%Jovj0<V3>e~(=Jf=??E(Y;P z3T+_h+du{YW|AA_9f6E<76FndmI@&-m}kJCZd%?~^<503w%=vc8~}S7KN0mpjSX)L z>1{^U;+U#tf=n=yi3fQkvT=?bKzU=HDG%zk%RmQkNLJ~%4fVl!lTV!U=K<LBIdT1l zlTJS6)YDGixM?%B6FPgl%XXJ@c)ax%bO8ssnm`0Q2~IKOUk^m{g>FQX9?8KTM*+xG zr&xDdRmWkH%s@^TIwc}Zudj2ie6|GCGJy6r-0Fi(k_|A3`reWS5iYYu)H#UO-eDo) zKM98DIv0Ah<n}Z=(3}M(53^Y`<Ubh(<%KTOSu{J|W-vgIEV{ZC4NLK8RFcXGL6yov zM2(bX1<u~^aJV-tht+sA22-{}WT}xt5%wuF1Ic13jyA1R*+_94C$uO_nQXo|SVrE$ zx@0T>EqKZ}2HSC+n+BRW1tz_Hgz-J7U*2$FoAjLOmj6^B)0k&j<2+!4$6F)M(||nZ z+LI^B<%ukz`rgQr4f#(8vizmL^CSm)ToFxk$&7Z|4Ig6B_FgSAV=2f~5W^8JE-Ph8 zPOxPG?251;5F=$i#=s_=3S=8?)wy(VOZlXmfFS}auI7q^ArPlUbpUi+2f4$Xu9ipD zytck*zf2$%q2*bZw$f@m87UVha^0k8UPX*Es+MyR1YE~<(V;bCS>^!0lmqsJb0l4= zFgi%EKzc-yk)Vd9>S!|74eVSbk7&e`cIw~Qn|kVv3~Ze0I9kxMQ1WjA&MkW8T;9pL zI+59ULSyC4z{<C}C<E<^dE#vZKyS<qB|uww>Gb?^&{no?>$~ux?HBLZ2|-;cKU!M- z-LlS1?>O6OvU_`ibTW^cjz;nW*|Ot2#Wtx=JN$u8SOhPF)j6TJ6zCTvq>OYX_!OFg zN;$8!=1S32GKLlN@`M`c#tS%mVX!ODAPp2C)l3eF<hY7$3j>C&VKh6eCFw3R4U2=p zZSAp0kYQ?^=(rj&yO7^(45k@mVCvfj)SU55%_8H_mmGtw(QzNp@f+GtN%s7n2pF~5 z8v#QJ|Aj!QAM2vZ+JGrzfHN>Y?bby0tO&R5$H7U>Z7a2mjgcRQ;vyJ|dvuU3J$6xD z;ncfr{b2{OZNqQ|%$^H6vOO4Tn->Au=id%P`J{_TvRBo%5^w4U^u*HLUSaIGs1ZW` zi-8jE*U{>-61I26IGV4?F8hz($6l|-g;Zd~cK~Sj_5>O^zJ7FokrV6fDHy=o31HpV zUCOsx|1E_s$8sVoM4}v+dc#Uth_YNc!U<wo5@S(GVWVN51qyN~F`B+aY`d`-=xdr_ zjf}#XYjiLW8FgcD4qJtDeOL;|vbl+Ta$r#Z1j{dZ1xqoU4%V!}hJ-GZqHvUu<j^J- zoMkkUO2$pG9Z8iy;2XyRU8gadbaovXrZ@G+8|Hdbe^`)G-pzAtqE$-iC#C%y2p1Pi z5AYnI&GildXhY;;KpC}QlO9NJb(9HE0`!|Ff)}m;{e~AH4!o@NU-Bof`BTcz1Lc3X z8|7OL)02awNG2@d76-4YYz($C%876c*4#=YR#pUwFRM}{%F7}j7X-NV;a~vk#ep^w zh-Ki4qrRPibSSqOjc5-xeu^UG$BKv}z#HOVHeNulIMxYz0lhz|7Obm2l!`Y3%GpGj zsIs_BQH>2|K+yt2B&-n)w(-UdLT}4<Z)mh}L(8&V1nN(DR+m`Vd9u&ZR@%{&B?*|| z20VGTY?auC&=W5}dp+`k67q+E=s(_t=-Zs)Hx>IOBz+)wUKJ9tvZ$iHldupkM^!~F z^NOq{R7vIKHl(jz+W;F9xSVf8`W)=i5vta5c3<z4ixnPrW1Lq;>^gYc3mWz;-bi2W zjr6^tu`}r_K>B&_r0-lQw4or40s4UfZgx>ITMK!c6M9}Uu&`vf1o+^YE{4_tHH$A? zX!o&>2@43(ge)-}SB~;gkp40U+lNslRu*DXLSQ5|#<4K{IHZ3r73pet0rQ|`B|<nN zmj!c9yX4daEK^&5@J6nG(i^#Y>vCsueGQOn+B3Nd&W!;3Kx>rxr$DJUwing5?4D>8 zwb+}ih7xcmeCiTuCFm`$y)+Vy!4Zmq!Q>F-e=U&iN4gkeP1}IJRS<VGD)MZLWqyfO z93jHTBT+cJ!G>XPK>$)ON1~B<S&p$$6^?aA6{VHP%^nf+C{g$G!${wV<jdp9QS=r~ zMq}AH*7(8cJW5d`nKH}cH&E^~=mqwv;@YYpIiRH=yJkKUNughpg(%LY=Z1qfF_fOt z6H;&_1SJ?sP%f<{<iG(m`k|D=1@x~xu5PT%14|id53C)~YN5UQ?__bSMi$Fw3kA4` zRyHOW)Z{>310h++;9hI6p_@dP_O_1jHaa$b1i4^d*y8zCGin6*vxj}Um_w5?$DUkY z^hbd^uW!$tmdu`bGYYNs#+y*W9|I=*T$j~+Dhc<d<7LB#cwUWV^KqRaEllQ+=h(In z1$A-jl}PZ_)Cd;aU8oAEA`U|`>RDtu@7cR`O_QA}jLw@}W_g#Ao`wpb=xwB=kUs$f z_028=WsHz!#nDH|NX`MbC1HE53s%XjY7|2nWFgmKZgz6HHh^8*fzca)(YwO)=z%n~ ztxOCE_5Oy86w=i`{~(OvT`ps2<R;}k4ITht8$6>Dy~&R2_;|>lgpvGpfOOi$wr5mI zc)`wc-Wh}pCA#hj`&C^>M9;37k;)M$(ZLGj9Cn!5lbIyeGCh)5JX;2tQ7%kmKzK#7 z*%Zui&RSJC>(_0ekFJ%cy!OuPu8I>7*jrIE1-0B^<>f4rhJBxoyP(bjWMt#n7~G=; zowkRzNYPH4vo5U}+cacN>=5JDIU5BsM6>jGI<>Wpx&<H`SNL_~l3p8d`w(#Zm7cjB z4h6O6b_Y*{Om=`1OUox;^ZgS**BnlzGuhk-Tox~lQvP9(0(W<l0`_`62?OWBB?~AW z#XuzFPk|V?smsa@FKl!%ZkcG8!eTANWH>$@13R@C4VMKb!hj+R_6tUWQwe-biog+? zmL;2qQ!gEl&;U`Sdt7FD0@I?$v8`%c2Iw&1nGOZnL5O5jG#w!ely!A`4NlAFiH=Yk zwpj%RyR$(UO89d?LQi(l336Gy2tGzc1V@A8tP015BT@`ZUt9u~L=gr~v?QWzJRBCn zvS=Y982ypnM&Dcgoku?pqd(|*^jYU&T?3)o_!nUOT~3YbIXzJTYNs~}fD-;9P{3Sg zRf0fK#?)w>m(^1(LPZzUR-+0S#6jP2%nLAVCBR|LGY*{=Wxa77q>!EfjltHgmW*2N z^N#|0-|ix~^-`o7PiS@;oAo+cTJ9*m6|@=itD$eLf{CV9Z!~x9p4_u{YTq>F2fq|+ z|GbM`T3-)on!&Nzju9hrIIh43WHc65!5}Au;l@b>{)s|Dn29K2q}5h|X8y7^*MN3K z3>Bq#GCP_qlr$Ry*^Pula!4&eyUuVM2)(Lsz#qSW$BzMQ#=xct*nvT(y>t|yoM|v~ zU~7YEh8UbE;Qf_sbO^ROQ6_RM3%_O)MeuQi>x3hf8EnERs^d7{ST;WlCpE{snJ0jf z>%CbOyy4L%fVx^08o*56kFzA4XSj7-5^~bSQWN;b<v5EyzbCFiZT2RdpoD)H@WMyC z@Pb|EIySrz5mix*@M>8FOEhRYVW|vz@?u$zfHP8+iNM_!5EBk@XsF~zpkNzidspX? z`1Q72_pGM7sr$eTyL)2>Z;0%T876yT2Hv?yaL|*+413&}!Jgj}GoUtmV+JVU-wVv} z%`WOb0R}CH2lLrX@-po#9~Py@NP*)Li5N@;a#@Y@A}DY%rW^%l17$^yC`=44DN16S zqw@KDHV<3mC1heKq~Q(-$a<ni;MKOHM;)ySq#Q{0d`#_lMGOdbsyFq~TkmWH+p?R! z4+zxzA~xo<ICdUmjb5jLUhi>HdODW(M8626-sl%ffWC8DY{hEOcV=d<nwwwPfAuxj z9=MM3&j49}#tT^=tAkgH9E-DX?f|YU3TX2^0t;Ft0(&$J7ZYP}A`2XXwg`8ORCI>Y zP1x#0iJc~PvdWN*#vJQjB~b5dZ`A9pYn`e0Dxls0?~1$gAh?DoefMGx==7TQYhRMl z6G19HpZ10l{&}Fhf9XbfC9FUmBiX5n5mpKFu<4!P;LM){q$ek$plwIvk_;P#v4jYV zJcnN4;TTiL9X3L6IFARyv{1UXs_Thh7kVRDZwTp3u={~vy|31hiOOa)gcFZw387Q$ z)j+W>t93o6Cz3_&^d>{0g#Q{K*@ImaV(kRbu~CFYQDhTgHe8N@CRY|%RV_zFm~^9R zEFo}mJS;H4n!OV&?t^`M8(pi1bLwdAhkr#2Ib*#Cs5cVfkY1($W<}UH)Y9~0PhPmd zo7la!H!as2mTXiEaCF{CJ^)nRg9XyLk&H2;)AV&f(=HZBEuSZf7JA-3h7#Zw<P*g= zuK}&#`WtS1!y6CMH_<oK5KWAJ6MY-_gS{T;{i!bYNjO`;;k#di<WcBpk1L)|SH_%F zyBMpZ8(`3eJkOm8INxm(MP^S!sFQvE8)4vFF6kP1J&he|$|j9){T(e>P{jWR=+9s1 z!&Ay6!8r+WvKxn^BFRjw*iuDlI6N{D%?*r?<f0R~Y#cI<i*PzUoXNpqSa5JrcA87u z-2vMcYIlFm;@=mHfR2lSmLL24Zv@1-Ibv_uo;wmd^d}T-kCL{)w*cH~yzkJIfja*| z<SqB+UdLp(2yU$vIKn?3E)7MK6T_*zG%;FK;0y&G6X$t$grj^iS}KB{;B6t~od>JF zHXPrCoLYa<Wf5Ynpc8Uk0dq&L4oy1*x>jfO(|<EU;KRKlFeeYi)R9a&C5E}NVP+uC zrE)^51P-Gy{+!~MT&~eh!I)<bkgjjRA<%?jYOy5u`ENm}?Q%g0*WC%VZXmm3);LoD za9Ut%hiJFT4Se`-MR?Yi!A=hiXw9!*TeOKl+pQ!ZJ=<11Iyp}8htm7}Z$fR{+3Q&H z1$lr?kH{05)JPoS7BFxVhfk@wF#^2PeCmYBJa^T~6t|&~d)$R7>=ktyJ690h2hR>w zyD@XE)aW;?;nVp;Yhkv&oxX#9Gkqt07kxK<5B(PUt@OS0+vvB$lzltG`5nFDJQEAY znUP{NFQ&jTWqxQPA?Cz%tfYrG>4tNg-e%*KI}pY{ebg9t1<)PiR+!r1+qFsq-~Klv zWPY?)WF|OuD3WAL*>pC`6*FRKbXehsiVRCCcIp*2x^ue`>V99+Dzf_gcOra0>jmE~ zEp?)M*PgkL0=6zVFSz2Z!8W>yYyAf1{CA-tF|IGRpW&V2tTdd+NW+O?W?Y#V7|55> zV}-;7&t-+UTL6^SD*&x=IqZ198{z#6E_iRPt|OM+fbD?gmY5+&f&>3O2)Xa*mEI*e zSrT&zt`Hs=Qo=)H%BUbFBZ`nFqvo_hwPIsi$T&i5JAq`AByT|&{>q|Y*cD=T47UZa zomg?L+rWnZtq9FMD~T~a6V8q1#A24uGs0jZE5^helS+*m%wb=y*lq`G3+WdxY&g9a z;nYn@v{lfFU|k_|M<;=%9RgizGy3U&8$zJzozmmVCohiVhGX0Wn~IL5CrbQqiY<=9 zZi!%2+!2BJltQaY@pgnlw}?BQQRoVrI|>bW+9A=kKBKRoOzhe_unv@o`{?`WchK*o zAD|D>57H0O@1h^3|BQY&C=>S~Jm1!<JSYq&<uNA84~`8KvqRyLd}<<{;Rczw(RLR+ zo40;!#Cku%^k2FN0MD3qg{~8(4T##|+O<Zbzy5b1M1HbYM5e~0{6MLY<e2D~5}Oz( zW#f^Q6e*y4t7xh2+?sMZ(vCtOX$Co6C2}rvuu)k%uy4coI}yg8cEPwyYn=pzYlqx% z-vT<Ud`xiL4TozZ2G;x!py9Z$*WpN~M#otWj^D&bhr%g!bf^&KOR0QV4@l?S$~M+( z1YZ3NIP4L+&vJ(l$`8As+@;1&D0c<g9p!fL+M(FBQi5XGE`JcA`1W2=%#})`>9`t6 zjc`NZl03)_Ok~weJT^fp_HYmlcCNKUZ#GJP2%+^c7qseU$hw|GD-F8>=#EaURyUQ` zwL+uE{&yi%-qkBAGh8VzN^s(R0t_Sh_}GvzpbU*>z%iL7JmAWgZE3GFnmPKgzPIc) z+0gkgLgy1M=xkF{Ckk~1)E%WJSna6OwL+uk{y#$~{j*+CI><BWfw3_*k&6%UiA;1z zjwPAld^SlccA8F|eBaKRWD{rhCB?fDlE3JJWP9D6@aqb-JAQRQZNRldwQJo5Mkwlp zwblDi1U2KM^vCFr)1RO}Nk2+|ivA1wFX_kVPt%_PHRGdbH15_%!?|)v4CjaV>>yW2 z!Hwj^7&DP&d1f$`PO7>^$Vtj=OvuLNHz4YEKRZl+eg2Oj<p0hE`R!`ZWpf7yA{~0- zh!5DV0RXXe2s%`0P<#9Mk!_j)+~7utXIlTq(YP44q}FkX4JVc1tW*+mg=jGv87(o< zfuS^))mPQ+%+@gT0btsFEjV<F?gpXAxNV?mRkc5X#^kp>j0vvXoi`H0u01&N@o=q@ zj0Yg4ZS2N27;uiqCsF?&?9hLGPoPtmbMn9hJ2sKYDNHz?7K-c;JHX@=bcO>)#SBAg z+VR_hiycNg0eln@jC&YbAvabSP35?JE}n>wrr8oBh6`+*(arSj=tb{Rr}&+pLU?ft zLyPdLSJDYDuE06)qU+G@C-lUyYdu}Y-)04LBK9v(4?p2D^7fLQ`lu$j31KWUnva*{ zL>2-8hncZlHZcn3UEA23I)K*8wiG}%EFp)k%{rm}O9b_Yj~Z%eurxkW8kfeUcrh&w zrAlcwTpB2j!t%!kwS8pT!fa&0+sO`b_ZR~AZ@hrpv*|Q3EJu?}gpCQ&vACR%XBi<9 z9$=y)3D|P7b)tj4v~7>vvHxkrjD~B!R_W~_z`adDw2_$$2HLpMv+FdUK{)>RCBm_5 zj~qC*_rz^p<%beHr(~pw31LLc#)lHgL|jZkm{?7J1}lEs0kKu?;0Um}+;GDy>p>ZM zoPL7-Ed4q9^YoMS7w9k2U!o7wU#7nT%FyFz*xpD^DYT9n&lV$GLKquPGqL0-D=YZ{ zbi;Q-->Cry!yQHqNG%;Hq6yaqX6sb@1RAHGc^W6Z&Q5~I73w1y8Q03l$N&m@9vS~< z5!&@=D6MFhr1V67BAXk}jU-3L#Nkvt$7d#Z@bIirgVPjaM=x=p*bV8QLrDLrC!`Z^ zimv{-62q<#9|_7`D>J*_JIX(gP(JBO>0(Ssk0wiS+;=QB!Y7$TW^5e%Sn#^J#ks8j zC`V5I3=!X=(m#pN>vrq}m+nNuuHZQEX>GrqzV2ENK_zsncT|1>q0(LPO3Sd19tFRX z#W*_#(|#%?#}s}vtt;v+sI+;Ev-Zd?&A*5+>ZW*kb0n7-b_K3S#JbjVG>H8Y>fs|U zEZMq|)ro*|xuMulBCjZk5oW9uE+xiG6X4;q;8eVw&fNOi(}QLMaZE7&!w6ot-H6`c z6*EbRi;j%uq|(S>ZXhAD%7B6#H8{q_WoN7{5xZ+EUq%$X-&Gj!@v#EK=fnscl$t1o zxuMu-N*)y8$UnA(I|;b8r=0}DR}j)(T$Dj}gVTXCD@(N#5^mL)-EE6r+pOat4e+e6 z<$8Vt=pN70FVNqkU!=cJzeN9l{vrJ%`epjZ^iM$dcpeQx-DMC!^BG8GqsVJ!9J`~# zFK}kfp>bIA0Su`ucC=|fTGi)&0X4P9OH<h~2q=QuOu#y^TWglgwoQ&c|MzG(HLUx7 zY7=k63U)a+jAFgPT`2Pua7#2Z|BEynk9|=$Te&!bfohTSmZTB)OZ$QrTCQc2ll&0~ zG6LuN+MS_g4Ckq&7H!rtT)dDPEI$IlKp)zBj^UlPX)|UT9I3{(aoqMGhrD)u9}Uz9 z87RvkRTl^0ai@vif&j4cAWWdX_xWE!On1<Q>C8gz64l6qacO}JS(A)*T27hh%T@^J zQwCuDKS2F^y*nY2y2t~_uq_R0KLfcN`5ff;LqvZ1J&5+?M~Zr)y&oak`;1rGE9dfA zh(1^}!XxV6uGjNerPUjsQONZqztlpYiB>WPntIvF{Fae(p(*E@#xn9ozjVYygJ88b zK*i2e`}{vf#P+^!#Ac$&6V;KnUCFO4C>`lB9P<AJq2}tY<Y;t|5Dk)zcqZ%-S2leP zmZt-E&7A~6LVrrXLjR2ZKlDG-Kd1kN{ssL@`d9S-rT>-wHT@fyCVq;r`SKEB)98?6 z$~4nkC(KrYAVL?<89H9tPMJnglXsDLg6ju3I-CC$G#<K#4(GbiC~Q!UwcU1emCobs z=+-tC{-2?SKdf`Ohn5YJKs0Xxq+KlQAhe9EfB{<_HVI~BD#HJ^9l`&D1k$IvT;~IP z6*7@rVK8e<hx&Ji2~oTH(dH7pDQ)x(E4x=?nE8-H_m-0SG>N0c#r6J!_i*>T=|~1F z-f*SKEh3T$i>V};!cT8eo0rUVkcIz@#<}EqeifZYavM%VR&@6He~uXSHg`tVb9dOY zvQ*zH==AdFmsYN{&a?ow|6fo~r`<)HQJWQmM65XvAX~r!vb7elQEi|97YGGzy1liQ zjv~J;Xbu5o1ELihmbNT<v>~^lgKqN4sMyw1o0R$`LhWu3s5PXhC%hWD9Z3BZLh3gc zjjk-s*lgiudS`SR`OHqa;Zn=$BJ2Mz!eWOtNn0vtuMg^3Z9|6m-X=8v6*bsly`|UE zZC26qnf+>M>eopB)Gxoa*FW`IfSbFmew48PH+1lV2^2Xz9xcaGS@5d}UVtJK!HZdn zq7L14(#iA+8b>SNvi!UFdEV99^ZZ|&hQGI7{v3{SzV7gP{5(<mw)Xt|)A;wkE7ot^ zczS5#S!ZopzOOR7<NvYu=FyU6Rh{s=k&&6NvL<Ax320tFhRg>^a_1otsTkjj_hQNz z-pj{luQ0}{lB~=!E32qtvOor*K#)Okz+Ua=TC|OVU_l{*qIBDKvwG1V-Rt{YAc%@0 zDxl)(?mzmE?{}sf_uRPW#*KokKYHcMwek*ehJE(_?S1wc?%AeCFV>V^9Q!DFIlJlN z@ZRZ}doE6<51lbw@5acZ>9eOR!_kH584GkB{oLEW^mOgEHClq9vx~tlCc9YdVzZ0G zE-t$;0t}x$1?)0pml3;+*=52mj8tPrNo5t7tX7lNYVwz?R+H6gvRX~oBaJ!B$T!^6 z<IB_GmFfQU^8NQ*x_H(+{mSXXXZ_g_2UY9#=?8}w&N{wsjjWj=<qMYQbB7b(4JPAg zKGRyB&o(DxcRCBCeBSbW&U82)8o`j|W3n^-<b0#R3&y@l+c%sJb%dJtqxD&Oaz2SK z2s!Wj{!GeeWXPAmPs>@JZ|n{&ckDS*eHr91Go9hk8+lQ_*oyXz{PA!$87cW<o6G5R zB)%{?*b2Er4yNrI>iRP9MXrzG+RlP&iB6s`3(gYc=VXw((1&AG{{~SpIvJX2I1^{6 z8&fG?2Ju_w$elWt70u@=#1VzJAXmzlg*}oQGPZgOxJBzrQeRKO){!6h2CzuXQ<D06 z8QNz~XJg;eUD)rhzVGH|2>Z?Z1CyC!lbK_anPZcgW0RR<lbK_anPZcgV`g$KW{xdp zjxA=6E&dxb#}+fk7V88o-V3o>Emo_=YPDFc7OT~oFo7_=^6X@N9Qo6kGji1o%A_Ar zd&&gqXA<Pd8U~JJPkjZQG616Xl(o-SD8@3o;Xgs31`5DM=}c0@KtX3TpA2xu&me;W zi+s<ZcH`20(~G^!!;AM1$5+r{-|t6zb3Fs@g*}inX5kmNds0Rt1H*&^A!p3Mu|!gh z3@j72OR6!8?;(@QFld+x@WKksoT1!#u^L52g_ezm`ZySSl6J}&Rc;GvoW#0IDWl47 z83m*9G?4psIisRuheI$LjVv{(mI89fIG!45H7Wprj1yxTq|~UuD#T&vQ&}7^?vKCc z*e%+v%sJ;?zBQwcw9Txv&8)P|thCLnw9Txv&8)P|thCLnw9Txv&8)P|thCLnw9Txv z&H4tL^$j-b8(3KmtJPt(I;>X5lBJ=*_{bl*#%!wQjjd2elF=EBQ!*;WIGm!&l#EI- z4yVCnl9Ev=#^H1lq}8Zm4txRwj&0N_khrRH(nkKw9Qtx+DQ8qNXAyC7MkRBWh{I7a zCm7iyJB2xpiaGuguBu$Zkq@Uc4FImPjmt8s+UN)R#7HqYuCk4jG0yQ*10dC%^kjEZ zlB3F>gvm)K)l>BV30!?aq$k(a!*Gc(U#ag)2=mpDPH&OYBU{D_l}nZcFf18q@4xF- zZIhX8hZ$ps8&GD99cGLjW{e$Xj2&i-9cGLjW{e$Xj2%A3!;CR2#AU|VWyaWL#@J<D zfXixiS*<S5z-o0_tuCw8WwpAjR+rW4PU!rMNB{Tf{{j7fME@Vt|5=);EK?=ChhC6o z-vXsfM{eMy^csQE-5Ac`?J5<@)RkUCI{ZvsX*AMFHFc%YNVhhop%}RUe(7yAlx<Xr zgSqym5qBG{MWvP<RpvIDM!bzYBS=jJfVtor&ojD{6t}@N<II_7T&VH+)J$EtG-?d* zTWrwG#<9S5;4?&XPGHxEJj<As6J{JHDRjv-Dp;5_9*ZiUxiDcUY7BgDGE#RD=7Q%f zGGeMCfS#zl64O#Jhs<%LXN;@6s1b9U$##N^!^!|mG1(|F$5L<=-sJ>;DQ%3KG(oCA zni?s?5_7?yoo7t1abcozo^heZe?bxbz{Sa>`lY+h#Cud<{y8o!e~We7(|hl$D|EsM zhVD@Jj8Jnr(;3yA>2NgFP0ZmCaCjJ_xkJZgZUFSFJiIboR4`GP8}Sz6KuDRyk7kQY zGbLMe5-`({KVfcFWk{2B5=%CZ{n~%IO}l+fUTl$`9O)^DKG~-9B&D=n={LT(!*s(j zqQ5y(K1Zs-wW29q?*<}|GprP;NKcO@^rUQ_M2at^^zCRqUn(<@$P6ShgWzUHN{C3d zm=)q%el&B2n=`ze;pdED&KT#6NzRz&j9JdqbEc6q&75gz<D6;dOgCrxIWx$a!<>mX zlbkutnKRAHx#OHW!2;d$K6KsfYZ_ai+xpUNbiwUKgU)lFb^P+BOIOZVNOR2jH&^ah zd(PVN@!bzxeB~L7Uu}MM_|@fCk6-=c_fD@2&)hRPMoThSfriPi9>4nh8u06oUq}2p z=GQ4JhG#d$)nY%JJefJ+&og$lbbht@)!|o{UpW~TuioO-TfBN}{9%nY`>b_(bm7wY zm1phK7p51_PEOx9ylZ;@SxlE)xaYxXn?C5L?;SpLb~=N{M_xAYKwjN<))}3?IDP2K zS?D3*#o4j0>xfW+f2Xh9gO$Bu;GTZq;sf_jCudF99qUv6RJbA*`TWxl^5s73^p#8F zvtwi8oxU_4KXBjh;`rgSGy11{ALPW^oLE#ysC78Cvk0*cCDt)!b7GB=&ggXoy)L8I z0KFjA8agH;cw(lJXj2kx3yJ0=j%T*_aZa8i$a5)q9w*NXBJ?q4dJ~W&GczP^!ilp9 zao%(U?~QVEYz*zp>~tutuH`MVlkikG1kJ9Xc|vKnIn8h);sCH6ES@COY;ph?KH=!Z znqZZU(QI3j%rv`{W^0Jmf;oQ~1la!qvmU1z)e;y4g63g_=78d0s88lJyJidr=8&_R z0N0oeosevI;w)el!+DNXj}q!T80JI>H3X^pY&O=d-{PR~1+kzG#QL0A8~hG6%=Rg@ z0XpZ9!@dr76D<?KfO|%@46OjQ6T=(}CI@aBY<)_tH-g(erxtn|`!mLxalJ`EXPQ%e zJi4CNqnlGVu=%|;8X7LYx5w6aDDJU<Ws3XRXe`n@x<9`+oG=Zv(?>a^F4Pu`EuPo0 zJl_}hj^R(meK6I-GKZ6~c<<`Lc+AtdwjSu>9_#&rHg{r$?>&2Bi}arF*&>ZM9F0Z0 zywSuLb$R2FEy^SstD_rnK3aI@+OyWUndN5L=H}Yw=GW%t*XHKe=H?f3vb59S*4p7# z+~J*w!;5iv=izW`?eJc};ng`)R=vY}1DBWM@^V~Wz01pSIT<b|1JeVP41VE6@t$bL zndpI6^Tr3EFnkkB6FiM?4s9WL-?D8XJm1Ea29d_`$vqXe<<IPic<<WtG@d>$)AK^0 z_`Vea2tyD(EHi+D@p4APsSe;$ER819vA7?bz8&5>fgyxCu_*Xx`ibM}9O%Yq?1}US zx*<`HF?J`Sd}BPed{L(94nr`RLoDBN8qCp17kn@$o^Qh&d1P&E>+|et!56`^_N_mG zXL;w^k#{xN^*^xZqo02TeLnsjvU%vvwG%I3*Bg55dee7Vx~IJT4*L63|A<|m{)G?F z=UX0U&$s>OSJ3A>zQLZKeUd#tr+S{g-}oZluid$}`SmZ*E8WC|XS#_ASGtJ_SGtJ_ zS8P^7IyB=y9bs4EQ{fLj6}?{j?-~cLao`#Uu5sWR2d;788V9a%;2H<6ao`#Uu5sWR z2mX)Z0Gl<(j4tN#u#1h)2nMHT7p7OncTX>$9Z#H*?IX<kRrx!?>L1hkM|>{P7}M#) z$qcLKbjFZRA=5cN%&!I5jZ3FvP1m<DX)mZA^XYrceq-((lV<u4isloL`SaI)W-X^- z35LuVkZBKGG3{~tv(|mS)d6cTUpe+nEqD9cy4cFRj_ncd$ewuXVpsgS*n7J!KF3)X zdt=wd66CtrnT#8@b(`GKz2EC%W5c>w)+E)_O(FD#Y}BMTWE(%f@%RlEz3CgWx0c;t z%PYB|J3QzO+08(2$nFGsLpCY08*DmcH)IpxrOS7nx!XK*?}PVWxpW!Ja}S-_XV{re z_J<c9nD!pNZ~D9{wo+fZ{P6Q*i6^5oolCDGtFX~j$Jax}dhTL|<^vZl(1Jy_t3bic zY)&X712<QpmSe|71y`Z)8*UVZv+Gr;B7c+>9hd4ANc>Fk;VaX^<;%l|ucDZa<S7AH zp=mif8F&>6)^Z^EDwKQl7?Z_X7LADuELjb6_*>Jmr`0m1J(63?T<(z-Xdr2G8EMF` zKtq0|a&x&O8EH65$Xy|AK4aQrE6_{QW~G;;&7;>l_H)|Ha=B-%(Jp{&>?1dH(>%Mu zCU|y(4UzPQY$KvKWLprqp&vlf8}i`;y&)em&>Qk$0=c34jL8lC%8K039pt!S-!sx1 zvI&dbU|ELT(Cz8;hHNDxH*_;My&+#`;D&t|vM#>AT^Ap>t&9Cw>tfH)y7;VfeTLOu zD;Nd1VSDegX$9ff(Tq|%PPv<{T#|N`a*t)CVIAeJlJ<DUa!;(FZ6|U^Gb8c@=v~iD zLoRn+#<Vw}-0L%@y?KSU-HdW?%2+PxfsSCKPc}pxf$>`<H7Pr*t)yjZ*;AwJ%=Y~l zq5Ei73$#vB=d+^j29o;vW7<Z{hotUF{nEv|o{(pfrGLE&{rmcLynn~Vj5P?|H}*w# z;b3D|6SfRLuZXRU*Z?`bJiR!cK99K8SAn7v*Uu?h(WS}4!xv|1WCI)4X!}6Qmv8#J zBS)XcMrB9HqB9hTE;{EPSzG%tEE40HE<WQ*7oTyZi_f^y#b>0;edQNvI=Tpr_jC~& zSGov|D_w-fl`cXf{n3AqMQ1$I#b{jVVl>_#{}2|TalP&__B&mi#_w31zUhXAzngA; z=1J@2dk5o7uX(HXUhQ9NAJ!h%KCgXI`-=8WwCDK$SpRk&M|)|&{?TK{*RMNq{S7zX zwDvDIUT~hA;=#_285H}qdU*p|oi{f&BN5gRVR)lfr7^iH=Mh`Fiy%w|9FK_8$~R78 z=Zt3!r`~KdbjH|1a&lv*(B0WMIUE_|=|mr)TMz6>V2=XVAB_x*KrM_4jj=zTjb=l4 zJe!*1iSG}G{x~o!!<;!&-L)+JB#z{1)SA!5!BYQi$-z>EQhk6^=sV#7cjPF;Vr?72 zfHt-(I6soaqu(eVZj>w4LLWpIx*O~)(%$?X&dnyLvuq@sj?z8s_9_i>DoVf7EL8>- z-*%jG!5w(ESsd8d06r*UNBSVZ27RMcb#1ePa}b-Y9xaid{)7|w8ntd0Q7<+(D$Rc5 z)JfVtu#=E?^7O{ZW~)iBol2oho)0=T{IJFIcS)lb4zoICIu1FmOsXn+zNjM0iLK}1 zL>O`6wP;!71imNM=Cl(tH;R>dtGV4B^je7fQLI;<kK=D@nUB5#CZ(gzBSNQfOJ1<? z{2L#l=jU(G(j#q|CsiNwgg_BQ5`fGAHAPM(<Y5BhN3cbYw=y?R0;P{HLn)vb9(;C{ z5n9#*tKvJQf$J5mf$fy4gJRXN2Zchp>YAQkv0NM#7=ul<(5Tf9m4`wSq;V{6*Y+#T zED19q@ziQa%+|J*khldTRx%?o9_T3=4CRGD;S~i6sr)k`5H+lZKx^|wK;Y|n6BLKh zQaT5PYPD9c6?!m4hy-wg%xP}2Um}={Aw$Zev?!-R!eipdLKO{la&E8)84?mOfAs^j zadCW;0|EOu<*!$39+Iv>H1fpOiy^fK6jF;3oJoE-&W)ZQi4bbM&SIr^P^mOG^o>HZ zykUhWOr8Pq4l*MzehywCMjWwKg14Jn_&W0=&-v(4ocVn7Q-0{FPkZ_^Zn^cg+wWL= z2^#xW#p!MF0<*OizCaAFb0ShtJasnnPXTB?y9_jqbL~c@+XYMoV%1o!oJS|wL`nrt zxd{{HrKjq&`asL}0LKNZ%EKX!KLjC)v!x@9i1a|w-l7=q$fB*IU0MFA5Yej@c#NdZ zBpp;2p(#UeR@}@#4MO?TCEXcf_$CPBYPefP&n*{=t~;pO6?b44Dusa`AohlZ&nB=@ zxl%5c&}9p4=JKr+x;>mz-rTPB`ekxxeZ5uaoignOXQ|oh^mYb_kh&~k7g)%Y@Sw7+ zYGCH+==AR8il4##%Ekk!(~G-X`DXy8rOdV_eyBc`H-qObfam38c#>3^V2R>agQc;V zzZJ0jZa%$}OAMsOdbx<rxEe6U7a#?*><0taN0?0;A9rFv;1mW{NykTp6~MN*=yM5j z`<<G~z;Hm9EA8ISCd_HCvW@Xr4DRUDD}(SH+~i>bFNh4!mekd#lxqd`X}5DnkOupN zGdO4$2yv<PlAtx=uq;M?zYX;)S2|0I2ph<`z+1#$6GM%m>VB=X%=otp9rRFxOx|h! zw$<FzY%q{C6?Dua4OrH~Kq-GaG`E*obFH}Q3Xnx2FXF6FsKZwN4yf{*6wW|WwoJNB z2v|+Ikpte!*Ka*I25;r$sjU~j==6)vyhJz5wLBSGec?#D(#IlS%+);^ka6XyG>V1J zc54uer*O*M+Bm57cHl5JDjToB=$zxN4*#N0oRM_E*GU)DZ)YvF-7nT_CF+<D4l9La zx<He6cb4i5@jyW-n{ueu&R6K%!ge^<u;1Eaul8G{6Rgi?971hN<|1dt+~b5}FZ7%L zCMg)3kU`gX3TSR-Mzf>G{H2(%8IHGr<JTv5Npkv3048;=24LjKzYs|MjU}$EM2sqo z9>cGkl<+oL4skS7E;J$Z=s+tXn=m~@@gj&K3=$Y=vBGsl)H`VoMkX<_Vi+1Z6&ixm z<P*~pBDeCVA<EAvOp;twVj<bw540rG+*N5z9n{#H`4<BT?^_88B8(m%kjw4NE_+do z0xFRB8Ibnwl}O{^>%Kow@`!cG2nJa%0a@=|Zsn84e^<koGfc;@3q=!Cy<RY|i-tKU zn6@);ol?;ajG|{5Kv2wyVbKBj+-|7^e~lT|!aicIkqJ*SsarxEwn~ZrQfjpiJGJc{ z@rapUev6r6L<b`oI+f5ucl$*I(BUaq$3l}JB15elP89R_3}ILxWj7{SXQgEH)!d(K zoQwE73z3x;-5D*N=D5s#4oWg?*gsc|u3Tof}sG+CzZfz%*b4^nihO+XI#H=o*g z$#M8MmhE8N8k~LU55Me3*76pR|Dk2bk4EI1dpB2ciNmT?j1rbI234<wF?UcX4FcP> z1{JqZw0y@Z+ctt;#uPw``L_rtL&tZnVhWk~P^un{dk>O670K*;sYf&dvDSB5<u18x z_22-J8~FW*;DHDL%mQL^lf0!10fSZ*C+K6?)a=nby-I1P2}cVPkw_h?O*U<?R~xox zW22-E3&VB?=&xsXmyCD>uBcGB61Y%;*V4{&VW}jVI-4$_z7=%=VYfVh{x_F^J|T+V zlLCDJ-m2Ku(!i;ZwG+=S4~mtbGOz-_QmwcZ%TItl3)_e}r24W^0ZX}jyw3wG(hydP z?wspMJY=LC1}8B<qnsog^!?SKpN);_&<_CpPIl<WM+ylDV!gmwD8L&PF6LsUY<jTr zkio!^;ib@n|Gs3Rb-U7{0T;=N&670aY8R`%i<2$}MXLzw@0(aYEC!{4U2?0o?iwZ2 zz;GmH{oD1z(uo&L4@O2J9KY=rS#>2&F6+?3*Y!gTQx|{82G<{64X)XmoDSEQ0j`bA zaJAzL0kWVOQhx+UeO<DtmXc?}D5-HZTa6sp=yKtu);j!_m)}_^mav6ld#AR$mVY^* z`|C?4W9M@N0=Q*W2rX+d;@}(+S7jH6U9if9BKB?=9@Z9YKz&ds7Rm#^WE3k1t5OVt zMUc;1#HUBOyWiO({zjoQIA7Z*cg0$<)GAYtzuc;kH<dzjU|94H&OLQcuufHGqk`Ht zdqd_mJIz9!B&&6cG?hq=ogyXjzAj$Wu_c7O2%VsIgS}|?JG6v;n`RJC9a`MMz=BGJ zZVw+VEP8868%v@dS*g?Nb`fZ45Oy$xmhBEBq1J8ERx{d2B+)xp8%Jc5j--#U3+dCS z1?cAr>vW~qY{y*oVl+{NcD^QAJ0poR>1GkNt)`pEkuO0BKfPo$U#}r<dU*h^xxl9t zO07;=C}c#*V)mTQ?IWWww<>Qv-Ve~w)CFp`m!Jx$C_^OsnN22!9Mb%gs%gx2Rv?`> zD|&fF&P<{L6{|_g-ON`Zs4p%NR45@fh7l49U$>!cH7u_!;VSPw!)(NZ?6y<-=41gb ztq0QEhV*_kv-Dt1CFz9}iuP`XZ+KI;@;i{>qY4>@sd@fP!UGYK@WMhwm8s+S@@BpU zNq+ArZ?s~UOIRq^g-#`^a@iGxi;{$`!{~LXL_~5njWj4AN6IWvT<qxCYt0(<GBZM~ z+#0}U47!I+7_VZhRYxBeFSUZbe#-{>B&>WrYwNPEs>BJw+DfH~ICUu_uXvUQ)_qb} zLE{;?WXr8mp9pM~x_H*K#aTsd;W7=M4vLxaXrH~?W%DBE8}*Y58zJUBR>b8F>x8!N zLfijUW^Ko&pk!^2>BQz-9k65R*gY@%@^$bv_v(#it9`E1?e+K9@_VoX?^tF9<Z?3^ zgZRd!2q~#%ps<;*!wkG($;gckY=jw)bhKAv#@a3+tcq2_JhfBw2DV<%;mBhCLU*w% z!78~0Y@t~gvSrxylDdTkplGZ(%*sS&#E&x;r-PfoVKp-jZ9fJ?E*0h@w1BJ!lRY?= zE)$MO9a66XVW&?BBS*ds2z_RWPtb1BLGY6VB5VyV8x?FDF1RI3eVJ7Zi3&P)v{Z{m z+4F4AcOoEy^v|s({cPo@OTPo@?_`#~5#OvEGL*@`3;8Q{jf<3-5J2i#4FSlJ?*RcK zy12rX;8YAK8pHjv;%&sJ#LPZRudw8O@Nk@&hw&L@QMn*#Q@j8*Bv3e+&cReXS<BrA zd*7ol+#(k*CKH?Elg*-xg_={%cR&_n{$cmOpFl@5o=j(V-F?rGzw*Mp7uWJQnBvBN zw`5*gjE9(Kz!z*Ig18#i-6{B9IluyBvE)@SA!mE|;-rB8ig@Aag}^B+&Q*|}f551L zcf}_v`sG?{zt-)uIR?HO$(KvW@DN`Ek#T$l8~o`_t$~d$<PDvgzzPi6?Il=1-ZwDK zL1!E0(-1p{UAn%~D(+&blO*EXvgmEA+B?AhD|X*+(kYBy<vdMy(CX}Ar{+Pn=?NI1 z$!fC-+4v|Kprx}4Qz%pR?JU*!9&W))4ku=oX3!hOb{08(CaocLu4bH&BYzik;loRG zLH4<x99<|l6{k|L@EI0nqT%Uy?f`4@&cH9=boGi}z_%?h6ESlby5vv5!4C7TFm#xE zk{QQRncZ}iyANf!do^Xq#>mx_;hvS0!HN$OVt8W8@Z)M_kke;U22$s0%77gCS3()S zxWwJ(#E{|ePN&tZy@KuKqo(vpa^TErwS=y~A5_Yg0|&084~jT!U=a9)Kri7VMb}B# zD&Og}I#?p_lPQL71K)ta*7LapI@*r4D0NnW=fh_C4!)F3TfGEeFRbQ1W^0`U*oD>f zdjZhwgV<7h#xY&S46he~*LNzMp49xA@JmQq4Zp|%f9IA`?t1t;m+t$C%lBV-;K5ft z^zf_K@|OVXPiBGjleO_G@JnT*P{xvb)h3JY1q^6|0@i4Bv*eVpBg^#AnZ=AdWh&Y3 zsG36(pu|U6n9)fw<2+&5$Eu42dhc5ez1f<Uj^3XDdbhJX?(vP_j8S6s;xcghs^qaR zPnZdi!OFY6kt2UUkoWCn$m>%F@=3Ct6{lbXo`ps4s);>+E}-XEi}1FKW!J|-VX5k1 zkQakqhHXr#D{LHaG))FzMvyM8m1hI&mDK>7jUnlPeE@*XK3XSJRMMm&c07_Hgh1?r zK&)c4E>dQKEU9NT8;TtHR{^q*EOCh04xlH;5q2EMsCtGsD8Z8(*hZx?C_3nLi<MH< zHvO_^>rl<r0ZZE;FK^fBvNRq0(O=2nEgbKG_2Elw(rb1xt%!9)_D<~g%c2F@Q2fwp zUT!unNhk&#=?lpZ1Jx_cK*kr6DQ5(xUkywvW*}L*Oo+BuUOq++91HT1-aBr9SMZwG zzV7uu`H1!g?Ty+`X>Zcrti45B%fAMA|Kla|lh|8e(r>>w<ngTPgyTp|`(!Mh_6IRK zdM$*uo4N0-Vt=<}ismidB#qq4zYc;^e54zu%_KWgm1K=*`KdEl$dZ3O+VgQCo_@22 z!%66QBi)TMwu;o6rQSjj{XOs8VX?h^{#?6w*lv}P@VtZF>0Yyq&9LRl`9XZTH0c|# zQ=3WO&ymAZx*OFRmTWKy82d0V^5a(iC&9S?rtpuG>Qgf@r9GP)$t+1Eeu3b{$@`S5 zLe}LU(IAsIt}2tgUS;oGfGzyzz5Z^ocDPsXxQF|_0DCB?Oyc|536DCpV!zkwz=-rQ zw?<uGiN|lyz~qmnFj*9k##|*~YUT=Rk{GBgE^KH1jf8=}yeb3R{%)yquGy$No_VmR zZ<oz_+phQbc)4-&EA4`r?>Qj8euRg>L^#zFdA*hYDZ<(%A1Kk~{xsIANLF(;+*1HL zBeW$kT2*++kNlempT#gZezXl4^NY_GB@@UQ$Hw-6p0pHCc8+uYk@s8qH<LQvzN##( zuD@+G&iRMU`nhu3=;`<p$EsJ_2b}o$^l6-?*ebRpxwjC>J+4p+xu7)JDM?fdPo^qK zX39dtc4G|R=zQ=-^ww|Hep-8*_IB-2?H$@XwV%=6rM+AGS?xXOvfoO0{?n`Syjk+f z`ng`Q<J1upy0d#&b=pp&)DObC<*ra>)hQTzKHU>jp{@K+6OMo4`{uZkKsCpsG$rv{ zS!CGe{M!gKKfEe4t7c`lP&4|iMyqA^nofUzFR*rdy1@&M`xO#1RbPavzn6@ftgZap z3BUg&3%?aLrQu%LGPM-w>_XDFs7WGGmLGC1|0oej`1xXT4<9#Y{k>|_-K*~D=YzxT z?M}aO(5)U?X3H+0;|0g3Nfti=IJLR(k%eSg-a+{OSq0x0i%aFQir5q`V?C`rz?pJI z<_aV-S5<7tgZw)QbKk!z-d(@eciL66>uv7_-tIxLZ#%U@U^l{oW#h5PjZ>T97Gf+q z)~dB6hvsJphyU;g!eJ%FY7Qq7n8apf*&#Rb?;>no*-0!}O|QM*c6u$VquV>xmQ!-t zdcD5S3yzx=>Fa<~o8rR@3AOJgJXJY~QUPgzRZ^yAC#NQffy&atcIJPUFz`*Q;<V$O zYwwlJL!(|iXdL#fy}Hrc$7+ck7A)gbYClE|;YTVG3*SRn_%Q`6r6ST;sH9EJ!jPUM zCMwGd+X`pmu2&Xsf-~`6?S0z&wV%^|Ui*Od3)(Mgzoh-L_Cf7e;7q)i@cAvP^0~WL z^AGf*wR5oDYwddHI`zXw)7;U^VckpkJpbxP;`I9nr+@qV<+PHzG){*UCGlEWV%XmN z`w1gIx+){<=Zn^Mzgsi);z3Y4-0ru^g}PhllJ8c@P(8jhrP$I=+%q;ICtRZVFo#Ym zvkm(a$A6A+{J$zVuBa^y<;n)BdA~q9Mm}?Llf<E{B;;ED=ZQGpyQ(-E_5JgPiS0M# z{avqK+28GYR=?iys=VO%Qg%3A<MfI>;8-J6_i`T~EdQK><%$y1Sgs^m&2pK%Bo-?R z4LOnj1;XODuF7Jw-`{VPD~0+wbJy$pJLdLbtI{l&4tc>DHlm5GXR=%Ti-fI@DcCCZ zkS*PZ*56YSsAeZCmF|X1n2u8x7`8b7ON5n=uFA@$+3z?mcDx^AVx&_(*tNHV-TfvG zCS%3}UnxsNd!cA^>0^8!S#6RG!!HwdeoDd4gp$$_sw7m+(m7d4=u{RMwmknq!qN|} z%F-Q6Z)_hN7}a)p*Qz#)yMC#r?{!)=UT}QsG_Lnavq_RUlQH`%gvr0BV6t3x8h@2k ztNAMkm557XwX*DxBWr8d-8lN$o8e}BSo;m_H?@ywAJu+K`<V7|?YFhZv`=WCgq!hU zB8_(lX~aiw)xFNH)!H$;b$pRrJ<tzZhNbV+8?}m<5sE`OVG23?_zj>=`gvjA;x`EM z|F?qqNhJu|oO1e5N=tA&Wvf%EZ8tY?(5RBcMG>SF8KQjyUyW68++C@HW82voW1DA^ zxB%XO58=tQ`QIdR3Fnd)<x<+K1$!;GZ@0U}Ua_#>*NfY`4YMUi)k)LV<fE9#86D)? zAna*QqGoYQ`y)gq|7!)A(8ATaQQcFv;EBsaStOSSnDRvB@ln$LUr1^H@+tl5UfbV3 zG!71%?Lha+4ZCOT8ryn1AbU6@2+rs66Un6qM#lm3TLduo@fEg3yx8r7?tZ;(cG~4? zdB0)wb;s)(WnE0uC*egjDQ%ANV}vhX{yzDlBre7m;X@~P=+!-CIZNa(%>u?5`*G63 zk0@Fw=S+iprD`792ZjAkx$jq7RV@4I2klmMA5-d*?B_lJYh@w=_<|);2yZq{^=}iZ zKlFW5?e6r?pX;A@&%5Pb!`ZF(8-~~4?(JjvBT+3&CXwba30+Q(nY+gbx&JW>xhqed zR`>j3O)nTFyLeFcJLQ&c7rbq~c&>^iC#exB^QDO`Qgi<kL>a@Q{T8*cW9?QgGibVp zhxR$ARo<=Es%59XtC~-eV5Z<e!ZCWXPxDE_<NtY;c&uzujK^|IXqWed*pny_sqmOa zV9w>Wul@E@;0!&keM<Ws?bF)tYM;?QtNot#``YKUKhXXV&d}pTY(J^Ap_b7rm{t2= zuc4P}`-UHMw#gU1hhn7$2MkMYy+qCcP-_%=q{EfpFStLSB69jxCOL^R(+rQ2>L(%@ zWntXHf(0u}CjUEx?IIe=A{tz`ao9O*wa>TD)y^F_d-ZbLY93lR;hCmgn2M3v%MVao z#`I4UrhhXN)A?zN*eAOIXOzb+iFkL_Jtf6YgyqV@=FMN7<-bc<evcCAPRVZU*ZSD* zdr&`T)%0rf;5^Q;u*7sre9Z<dCx`rnCVm0apCRm3Z9Ad4(;%!QC&tfc{Yn11vK-Dz zG;4KMewMIO?RYhOtfLok?qsiQ9H85;*Zopp?Kgy@zQD@pX@e;o{XN1_mE)D|Ai3(E zlDHKat1RdHVeIdd7XDj>N-i#Br2(kj-YxA`J3&xAryumae)W9+5GQ<g<BoTHopCD} z4{Q!3*1_aIN9a|pMyy7!)2z8>@!Wpf?VsCeZ&w{7*bc}+4KcYWR>l%RtoBy^fFSq* zr7^I|2VLE2I|Xc%I_!JqZfU>n@7UP#PiMm80A6fqnql}u!nCjbK!mKKGsd%JMnYB0 zyt#?&m3o~RG;p%Qb6?~B5PXlXXn(4GRr{LuXWG}bKiB?3`%CQ`+FxmZ4d3G{L<kdw z5a1YY*IPw$YG#=pM@Mh4XD#M&MALzWdW#;kDQ~UX%Ks^;>c_KG)jB}@Hd1F5<HTjP zMG{Mk99#LXYS=X_&VEXWw-F_K(B31l-taBdd>8P-DYf~pY1kh7H8E|a_<9?~lEYhS zVcakFgc`(Qck!LB1q2zvzP_Zd2KDgpR9*_}wZbD___qX+9|0J&A^GGOD_$DwF&j8Y zwRFlfPj8sy)UH1xf;z_q71^X3aW=$A5K~u!a(s$D!T`+j5KfS`Z{@#El<pCQ(#<oe zEozvC@-jjhnN14oS(tK<5u5*W(!L*41Cf`-FMwep2HDSmdzdZ;zrP^x6W>83!;fdp zguTBc*!yHw><!wT7NQUK!tjXVS^0UM5iFj@7iLoK<aZYUT3v)WF!hb7@<$}6z*Kxp z6Onw_F2Q(Ih*|59RQmbhR{pOD*nV{x*ye1?ggRcg5`KxKq~g)r%>OlE&4Wwf7&efD zhS)~h6RyZBX>^X^>Gq?qdMbj1epCA!?OWR4YJaEwz4mSGJKA@(f6)F>`zP&t+CQV4 z_$J}zH?9&l!v@8=%=!C8uX+&>-00FiL+~ZB%M7#5b(feY7-bs2E&n$}9^!<K__#34 z93m%LZ&F_+bUX>Sk}UGyA{Bo~Xn2O2Lr4(J&k155mZcc2Fex}7l_6QMs8VtLmsSM- zmKdauE%}@e{OUFf?e0!1><-0qN{7fk{YW_TZf<SFf|Y!JEP6f?_+%lc=q72DxUy(J zP7hb7Z4{cA@x~`jI8h@Z)hTmM%iKwt8$VGa>zC&5VGI9`$hn`ne|6%M<PuLeqwL(u z|2<LEx2P3Wq)u70iWI+S=tO?<%c53Bds>j4|2ApqMYU-QON(-lo3;3+?+96dZ8U>~ z+O7O|2n$rceYBKRlb=Xh%s@#*EOH}KSHzDF?KT>yvQJ^bQcEQ(^<Bc+yECvhw4#~# z8m5jh^&bdRzxV^gRiqkeE`0tzot<I2d81UkWJwja{vQb!Q^q8TC`k4PMN&y*MDHb| z`A?+6l<}4*Wm&Hx(#_koP}TQ{|0zCxOZGoSDbVI+qo2Ub|FgF7f<qEHyj&cV>MfkB zh*N+HhZ`?8*4EZO@aU~i(~fI2s`+z|{lE13ya(C!`QN#PKA-%*zD%Q=-~J<QcKyUt z>Gh7YPockW{peF}yY1G^+n)XG+mGElyzh+Z(Tg>u7sozIUe0d1IJ|dy=AMg_=|g7> z*Sj(DX!`8w%5ZdHdd31>M?d$r|L^JAZELgyLuVI*T}*bd*u`cShh1EDVFVaHdkWZP z$Sxyx8MDiTT^OmxjFQSKFj=i8tJUN$S*<3k)nv7ru16YkmXU9`r^lD4!z<JM>E-+H zxpeWYdHR*phtK-6Ar7k6?b8nqFPwFJ-5OalL&_H{&*u&&z8g%&(R`-0JfCe&#_n_$ zNcp_w`JCx+JT!tK%g1DA`pNl5fftN@leTX-9qI@*??>yi^yGXJUl4NM_x+ia&&ZH3 zfuEMMJm1(ITJG3$r1~<*VP-nRp*QlPe6bbn8~NkmY%)^v#Wt7I=}3HGa<CP0ha61X zHPrQG;EP-z!?m3S*AksPUlyDt$j`|jccBl*sQwM2VstVz({LuvP&cMhz6|2G%#k~F zEGwGNRfr=BZ$Yk<FAIAlHDqk{6mW~ym!!U)f~_Mz@C{&*n5QK5^)j^2oX*C+rMs}- zUwz++X9)Yv`va4iW0RR<lbK_anPZcgW0RR<lbK_anPX;hEoP1_W{xdpjxGKhGshM) z#}?}ZEZz&TS}j(q#cH)!trn}*nlOPdz4Gj2eH{7InKN?L49cV*QG3b+>1Pt;$QlNY zWKVqsoiYHT_LQ~HS186ZyWu}Upau%SMd?gZ#6Ur3G@lG`#?K&w0*idlpmyWZebbA* z%fpNJ564%~Vc+jZdviSl?u9*&GiKozw|i1XBLl;P10iS3z_CP9jSMUkwo9rpi|-+m z$}nh{3h=@T&77g!d9fNrMunD*hWa=ddy;m_8C7lzYMjKnODUtuZy5!n@idV8bvdJ= zV~0a98jUP9sg?qA$T*%FX*DVUfQ%Dk8l=>yz$(OH=u=r7Fz%1P=g2MEt;{*+UcNP> zj<n6Jw9Txv&8)P|thCLnw9Txv&8)P|thCLnw9Txv&8)P|thCLnw9WbkoAnJg>l;{E z4y)B+wK}X;$C9O?!1%}?xyEd&=8dgTN0QMQjZ-oz#W<Xz%9M;sF%GA}WRj9mDaPS+ z5~S6rVh(%)1CDLfDUi6Ta?(cr%pCf1XDMe?GG`HSaz-U{mWabqF((+=BRhpTj*2<{ z60WLT!jTWBGYtT)vW?3!s@muW`ou^vIj*velQGWmQv)E?o%CdPQj(*}pM=RtC)HE+ z00~@uL8K?w)x&U!Fkh+fO9=DTkWO!r(j!~O3YANi1TZWaY40E1s%<i}?J#5Pa0AMW zvBQk9!;G=RjIqOvvBQk9!;G=RjIqO~c$hI}g}BTZyUZB7%ow|@3vgMjF00k$8Cb0@ ztJP(-x~x{0)#|cZ-3gta@#z0P{Xd}pkLdqn`aerEm1U}g_s|Q{>|3Ce>BtSdlwKoH zx*Nk8yj`V2nYz+zNQa-PD~(1vsiv+p8tK-?G!!Ejz%RXxhO&(+aWL23G~#ZfwW!px zqsrVy(}=f`X9TIK05BI^<9SARlHxX)W}G?mj0-hBpPH!)mqv}@eTxm6**F&14t$1a z&I#=LkY^dQa>9(mB!w=yMg<F##$!?CGZ!WdMU8>)O-AZ2!d&pYMMg|j1ke+eS7KTU z=8!p#^o((J7d2vTGucjXaab9EDJB~w=2!}@!n>T{FQtuflO{;@M^htZSYj^tv-6DU zH7-n4&ND95_%A4;AGkQVRKIlBnRt)t%Rk4Z<!`ZWdwTDEb%jni!O$J*o)KzJXF8*r zGaZhmx`{a)0uB#jG<WE@%ng8km4{b`iwY(Rb0gkD90)0s_|a@}X{Kb0P6B2c@+Zu# zstjq8POhzKkNw&o-=^KZCNH)~Pmc5yM4xO^dXiGwuJjvU++n)m7}4JxDW4<N;9Aj? zu6F~G#~D_NRHUaz6M9lMPa?&aQu=l@pD&deNMr^QnL%(fBPB#6Tg(daEkBw$!_669 z&hT@_FlUT&#w2G<bH*%Z>N(TMnP$$kv~kX~bEca!{hS%(%wf*Nn@P@`=FFMq<=k=3 zonV3Pc^|sr_BD+y&~1H*Ey%s_OJ91{vyNZBbm__&3u*F4{_pKO)}FO?e0=u<7hicM z;MXC)j*j0uy)ry=&*T`*VX%yb$*&&2`urO3>yTeZ{5s~>DJzC&H^tRrKbt(6IpNPU zcC~bVwfWWISC?No85XbJ;?-NcdTab|HQKzh!RgV3OXF9bHQmz}rWenSPv1AZYkL1# zV|@C;Jr7RX^g%y;@9?3sQv;nadD*}Nd3E1ecXFCA;Vh!t>$(|?15EE-xd&@^rt6ON z$>|3!K5+kZf+yc{M*OL8MXcmu>V>cA1*flE8lN4`(0yGRj~}>icyavj*%|%QXTQoR z9STY*p}rv0ju1Mfg!=wyPN+B0Ls~CRAG&fD3yO?pA2b`ok&l6fpn0nMDO3(Am7b{w z5h`tPMW6mUr*Xt-97;6WqX><*&xj0$b0U2m>kkq`$CS8<iDfBH99U?j5I17P8ACfl z914m5d75#(MP8cbR3DG7H%U#pK6DM6-y7(hU4C!+mOT{r!<lW0`_af2>CLe}zn?hs zG?SU`vbrcZERlES={~==3`~fLduQT{GA(yvhwsOMJ?3eEGeZ~m?nD=~*?|?lcl7aC zq<3t0EYdjc$QJE#01Hu<;}6H8Ovj)3I#%PIU%&iJVgk5PU{{+frOlPh=1OLBC9}Db zVTO(7<2vbZU3IvraCk8eHxUlkNr&sY!*$)6vT7Z!^DZyP<>k1%dY6~uaxz>_1_pbS z41VE6aeFi4OmqU<4jUf?p)j1OqlfRO!B`01nOS2YJXas<;=OD5xTnC<Eq`WDc=;|& zLzssAEZ$pQ2o%@0LIAn;%n#E$BVT|fa3{XO^9I4x4)3SlSlkcsRiF^+a7Mt#z&$b? zp99?pd{?A5F!?9SF@``fFUJ^S(M^;|teOCm?wY<RQ}>3x;Da7uK!LvHk+rq0&$Fv_ z8@+1Z`V;sGcdi|ISA$*u1A9LD`B%{A<KMwn5d41P1?+l5k6my2E=%{6x8Fg3f9fBx z>(jsR0s4H)<Lvpi|NIL2e8)G~^RrK~=jT+<)At)+#QU{7*EYZY1$w1hQt(W-q~J=o zq~J=oq~MCJCrF28{HG)AN_t5Ap@%#J#86A&O>Ckr{66rZru5!7u{06h4~cZydk7bt zV)r(-G>Ci0C-<c15Qt%ETzj4dmLe?E^9TxA8s8d2((FEfbqVjsIuJwOkA_nsMRq@# z5~<VsvFQ`3vwJ5XW{=)atQnCmyLVh2h#~jJXzYpf#@LvMa*Q#|4==|Uk1f`lqSr(x zI`7!Vr)L+YSH^cwFP|L_C$8@KlZ~tLccRwZK1BkTV`t?LW5?6Id{11$j;HQnw^wP9 zolbpnHU^?RRB^zFiRcej5z*j%vx?wqL1|!@+^UU0Qzg^DM=a78Aq%l(UWWZ#jm&#F z?Rc;Q>9{!db$b3sHlBau>yl%1NXgav7v~>{qK&bG#uGn&j&9sUj<vND7hdYF<IAC! z-&rV@5G8nfr?$J6e>ryi{(7FbR`K~u^n<SxMB<xe)fQT7@rwiUK?;v)do^O}Ws5K$ z3!gA=ksKhQvqwTA6gq?RwS96|tQAWLj7M(Ets1!?HuAu*=v}SbZIT=8KrYu=aJ_Bz zh6dSdve<mJZjq)Esd1=#iR7w_7xiw92G0uvF|-@(MH>gmla&27%^;pSw77$Tg-wgu z0_?><PGZ7ikpxSfR=10dmV@vMKvLy)hmlb0HfgIFZA2gMgg%NdtcBJ^zW7+s$AVQx z&=o}f@m%O5v~o}=7Rm@}YZNQk^i~XlxIW^uy7>oNmbDl-wgCMU-+(Ru?xYw$H+(L8 z?6f4RlP^L$Uz4nzk;IvFlhhWWi62f8-}oQZP2|XzpoE_mUkNCbP;pY<VvLd))Wtzw z;%mjkjqk9vaggw{sfhM2?7gSPIm`f5lp&J+%qBDQk%v?|6-ejJifzgYreqS;$^p3C z&3qMt`r;Bni8G}5ISnH0QTUois9(la{<T4vjeHQ&Z5K+(UoI#Gm_>Tqklv4GmL32h z9ZnHa$j*>}QiX4LQ@8Rvkl~{W8HTBO{!GFH5t5pPg@`IsKhY|0=4+7T_l~CbdtssS zIc;3!vMWgB!x5aavs5A?d8a}H@Dz|E{5bTKyR~sp>+R4t8?R_J5iF$DqZ`tBHuUVZ zX03-<uvsfAw+1L<K*9r}r*5_C=;Pw0R>&D&ssee<<g@b3VPmAA11mJIUc+gK*r%Qn z{Vh&=14pZ<zxh#W&fxn982SXllg7S&iZ>$KXYY1f)CV}<sGnT0+w&d^{z2$@36Q{6 z??T%}%!r5%MLO|l_mufO)VVrr;VbofUiN3#!PnfYH=3>XIRumG@2}<eU<KZ>%nHc5 zmdO}^#z;f>ftU0o+{I5$<hJGOFavK`^1&ND`%{?l2v4tyH(SECDyxKWiB8c&RNR6N zSE*<XbQduct&&?Plb~lpnxWz$#8kgb&UC1^3cXXNy?}~(tGTUA15h+p9A;%AGvdb? zi*8&KI9wt6mLKbOxKx-2*8;K>(YIK-OgOSphXf?zf24LK$dPXYLZ4aU6Y#@npCk~u zrdKv9PH9kZONhQ{R^fCP^kCpti$>Y=Y|nQhAcFMIttS0!<)=%(1L=z|=_5Q>SRo^R zDr?A4CjTzvuZSopQf5K`sV72B2KOB~@;x9xd|j;Y-eac|a~DFqjTn`fYR}Id7Gr3U z0AlNdhvUqCTl`E1Q8}vuZku=kYRHGe<fQyA=|i^i`(W>T6oy;m;>BcQGf0uREXr7@ zIaT=rOlqwE!|wlc0v*kGGM(LZ_dP%U$_w{iT+5Fkkbk!%#G@DwG0$Le7<DzQd$zds zLc-j-lb(OTsDXFIM(u@uxz^gRb^C0Np@dVR5ML8M9~r!p17z?`AGxJLtI18RfiIuw zO%pL73rM?P83-1T_YF*Q(AkFhG{nwfw?>YAY0==FBoShi?Zf|~x2<Xq@k;o8ze%Ss zdX@9E+JjbS4_(ngw&@8Vd9vE9LN-21259N5!W7EX%&bh+__xf0mmE&aEX|-duTP#> zkke<<8d9gEHbgg5H3T{GcR?3EyhImdpX<rdg=}H#W4aK!<WIoC4)d-sbeKD}8Hekc z-E@_^4`sM}HD$=g$kmkLo|TlriVqTEcw)-%<7#D)(`Ql!Qs-*QfE@W(LK(ie#NFq_ zkm2x7r`4>zqCgDmfSS@L$$>Mgh-gzN`-4i^a^S#~^g$6D{RV+w2=o%7r@M~1Fbb%2 zI;{>qGVhZqhHj%!$K+Tq93gbs06)^A)L8|dj|YUK2k<B~0D!%)n){fobrN6~)@bep zK(h~GOYs@UbQv?eUIbp>sc?Ey^Jl^@A!#-IA_x4PTS~{Thre^_zMr^!|CI+GeAPn_ zzj`fy39$ZT7FgqKO}2o8u0uGKSHsZ>{8HJ#xuyuhZes+3vFo4^6tD`Qn<b}I0D?Vq zW-;SVnTo>zL0a;Rvj8Oy^}&oziW%n#!#-ABB+z@`YUs_@v~=|T1kk&k+1DVax=S;z zj8S6s;xcghs$?gFC(Hy$Qp;+9M2`IZK;E~PAx}hqe^Q{AE!=$!di6qc+3cqByfm^< z%>l=bX<81<2-4Zg=t_XSvKnBsF(e(Z4*;;)N9$yYN(QVz0JE$Q0<ntGI)c#eVkDDt zmQiFOoYb=#WRWBPDnRy;B@QtU?f#?yHd|!-)d5S}ATMv%G4?|kPcrtSzmkDmINk&6 z!<W5Iq1k2O#@IUv;Y;juHnaQCYF=(OE=ec`9q9|n4+GUJ%s|E$k|}3|6@E1^t(bvi z=`tB%d*$V0<XBrf{*m6>Zh%+tn%BPW^*{ND_6F^Z+D~b3(%!7S1qc1T26+GDCG(Tm zTVS>ey&d5m;8`W)c&|;fDLU+U+8@N|=(P}<I07l6{ECUgsvR>$^OkOsMsDR_2f@8M zx%&*$W|AGLDnbxKx|8zL$LIED{`F|j$Ax&ZhQjsE9TwZ$=g+l^hwWCmT<e~9_7MoA z+2)65#ivUXVjYnQKg{1Cl8>aXk)y3J2}m~&5Ocw#9Qko8|C3;x>LB){`qWJ9?<w1( z4sVq}`~ty?+)t@0WL^Fda-!GDp<VZSIMzC-<B&$Lzgw&w?$tZ);eIc`LKHo`GJfVC z;n6%Wj)>GFpDp$Vaz>jfv}?40H0CM^Q!`gklf*z}abY|2ZzK%-<yAqwG~T`JRPb}M zORY{BSPs|fiLW2QDKHUEwM1TT<$sE>_A`p{U9{{p)~ZNWb2dt6gtjC`s|x3Q#=fC9 z5k8AyaD0;ySz+(D+2PWXH^v*s#`a*Hv=mPc{O9~5@3-=ACUv}hRashHf7@uB^ADT# zb7jOW&`rm%>Xo*L_7<N$jnfoc#g-)Z79zRF(<R3nmnJ(UiE81=R3*twStu8pL&orp z&IfKpZ~a#7r?t0fZ`U5x-l4ry`x)(B+Pk%%)!u_H`>llMVa&FyokFwZmGyJIV#ldt z4|iwxu<Ep(MyW5tn=IqGsvp?O|1{zFC%$ivD+yF{JW5j%zm-LX{La6PF!RH!0(f~8 z`$TKRC(Nin0Z*IEW{ALABw$bCdphGGA^h!x-+z*Y---}UX}DLmOf3Z^*-0W%md`~3 zpXX5`l6S8xlBT;?-P6wphuhnoe&e89J+#c0T~-A^Ns1p4kIND5Dz@_PAbkI<g71sP zrE*zCYzmjLo>m?-`;8(Em5|^&_V=o)VmWg$CG$?g-1o1_+~pDPWxOwP<J4xjg;+p- z5!pXOIQ)k{5DqITR&zL!z$6SS%bv6OU4+dmJBcN$>9zOUPOoKkbbF`Ta!O8Huh&)a z+N7@oPHh%j=NEbUZo*TQlPDFC23RF!YIbsJk{GBgZEgg9mN4*5tKzidoNMou%tNDI zJZK#Dt-ZR@+sA5&9TqI(RBAuj<A6nxMHaq?uuv6oM=BzXg-Y7gEDY&MVxqFVu&r<= z?t10+O>idOtG!QqzxH$5&ubshenI<1?U%G));_5H3Y>}e5<b6WRX%t3YW{&<v~~`* zd#zpXT&I56Xqr1ZLTAv?Onh7|F-h7>BAeLv5l;WM!T?Bxq;XnFT^gtBhuc`k!}uVH z*UA#Z_U7MD82Qmv8M!=?y$o2zmUa^Qh)u`|mnc5Wp_9sV*Q3nwpCcUquL_PUYD?q2 zvO#LzFOZIr&z#&OaVRU{;=o?~pC{sY@2cWh9`#-pM>t;N^mZzIz&Us97R|{A2+KdG zV7a2iG?pugR<m3tFNwv<LOF}ET>cA$#cy4e#b&?1-zZlK^>gN~*Y|hK?ZZ~3SuP#& zf-`JH6I;(@B>y5|>thPGialgY_o4Oolmx2T$x2lj4`qR2i}SxkSo!FxtZbV7j^kp- z`(fo=vr|6UwYP)a{btqR1!K#vG_)6rHkUpI9g)>0X_WEHgq@#Kurr~gG=wS%RkL(X zRuVdu1%@rpe~_^BgR8Q1d9-_(rE$GanoW|-nT*+AAx!=~1(W5n)A*~TTFqZUs6<>6 ztCeMQR$G{}xbDW$`)-Dt@nP*ZwBOV|qJ32RE$w64$F<+q9@9RdeG+cQhlw=aA*2zX zjLH<^UV?JM6!JirPJlY;=h)QWR{l2#^Z&Pk`AH>&6{uyA(vny#k)#R|y^oY4L$oIc z7D=cAyrGqd*hjxf<Py#$Ey^WBsQaaI!M8zVb|ZnB#lG<)L?-`h1)0#o)8wIS!4sE< zvPdou%;hHrCrRKyTpk}K?f-?8_KP)vv?jOx?L*_>u-OiDuiUVE#;&ogw*#_=Lkhk! zpT|!m7kh%SSu6&a-y(ofhoQCG2i^U8+w8Q<)$)GB=<ANxHOjh(U6#JgA)MDmTzri1 zMHPmY@HGoxl*GmO5+!5NBv$v7<t&l^;xsk}8$uI5PFna8h2-U&X|k_W%|rX3u-_^7 z{b~z=fIa=7-Ky?mdRLPD+y`K8OCkdJf+bRjIh!~few$GJq3@e&cc*{;T>rd#-YxeU z&ThTmFueYDZy(#>B&ub}B+?uvq07lJ^gc$&{f}A5U3u!Xy5|>bdci2!#e=fnDYtaH z;BD*0b5$%kNsUOEFHLNbn){z1%J|NdUe(&IT4vC64-f5gPOH3ItyRlT9dW9Xhv^c` z6dYI@SN@ZP$N%#x@wlUEQH;lOOH}=;s<1hG5(Od^9@7ZSxxDtZfAbVLLyv2p(tbz# zwD!B&XSC01zo-4a_Brhjv_FJ1^f(dQPx2jpi+!$T^a^IxKG<vMrP@AD`sr+wPhbzl zN=-ceqeKq>uAFqZ^7{q%=Tk&Z-^wH>z_(H8A$DF$FnT4`Ped}x!nkC>f|Vtc{~f}1 zaXQ5!+g-PD*g0&q&$rLj&K)>=^>W*49$JB5dwiF$%-(M86$mc4l<A))O#fykrn3^$ zSgxe_iLhK*7-u<}zdFl*m$3XECDNUe-Po`7vEBEee$J}t)#kzZ=3d3B@;c*NTfuVd zQBH|g{~5ww)wUCwJB^=8a$@`pyBLLCQI<1r*6OVMEMcYE@oM;3M=#<_uU^?WK(}A7 z`=!9zZ>W~P$Vz^v)}|)`q;d532uD?pSG15c#wv+hk+I5hz8}W^K55~<RjB0RLRMNk z+wI-bZnYBx)pPnm-|JV;_YeD(ayRaH$JZIRl2wb%fy6qP{O1V0s?~_q=yjSk*DRjf zZ@c|-JMHbNV+7j)IjA8f7sbk0B8b)A${!E}KcF-QR{5Z-TWzO+jZ%kw&)hBT*ZmzE zi{x}kA#Mv6Tbc&rKO{_3#Ry$2Dow~LI%7OrW+YU_%$u9YUa8lKK?5f%Joh!$55f2N ziuR}4SGBKcf2Mt1`*ZCtw7=B8q5YNi*YG{QLWGbKk1~uzI;gkE=bfr(=wUk0AW}+e z4qN#@B~_)w+6;>rv<{kdQJcncPKv1<CPi(L#IXv$N&;Ytv!4=<pMu{T7L*|Zre)>7 zMnWEcO*q(!ueVXGh&jr(g!5pf2;VBcqKWO#F7$gltxn<|+If&+UJC2A!ZT3#w*ZkJ z0T}FLPChxt(#6(no`HFRjY_9hI)#(qH%xMB*PjtVoy+{iNjW}6A7Oxq(N03w=D$vq z?h%D$oo7-jTbO1sF7celiz9R~Y~8CI4nS=F&q@1!OksHDb%ZPe=+DQd&cjpyV3;lj zzrP^x6W>83!!M+IS@cz4?=K1VKA9DJBE<Ef*rXkc^S$`Zlo2eR#usK%e$V1A0CXPe zI^56=OnoD&{E>~%ik;Z;F-^$tNV^2%Q6XlnLsIGIhg<o-B4GQ~Wnh~ZmkD(|hPx7e ziKL`%>h?DCe@$5P;8Hk-4dkE+<4CW_D`~|6!_)0YFF*C@O>5uO{zm(j_P5&KX@9SM zTl<doUF{#Vf7Jd-`=0jC=qA2NxcQB%#Lcilu`YA|eo?0v0TGRuOg#kEpfJoj*Ig;H z_TLbBh!Z;E<H9g=h@5D>YC91opI5pNu=!i0;tvT8&rov+34-~sh5`GqEX8PrNkNQM zhGfB_O2zeGS`qwPVvs(z<a0joO9Yt~mPkBPE#hZ=#yUjy>Bpjt!nw4f0}~5Yyi4W% zAbLIw2^T*w5#1zC-`uX0Mf+<QqASxj3Qf#-m(^#1&1so?5p&}wYHT$cwH_Q9(f7j^ z{vDBXKXd=;#3#umo^D3jxt0HWqNs0CE2>DHGItXxe$mj0{N$HKt&H}xAUpqU($b4+ z(-xK%<skQX;~S78WC6C(3=(R$^4}pWQ2F-JQc_KRB55%LB@wa6jYwS)M>DkBXkemC zk8EMVQcEQ(^<Bc+yECvhw4#~#8m5jh^&bdRzxV^gRiqkeE`0tzot<I2d81UkWJ%{{ zSK{J75-z5UNfJ?z><@~hlE@Zp{6CQjQ^s4Olx4k&NH=fSLRH@*{-^l(E!qDRr9hjP zd7y!p|7UIE1&25*Xro*ll<F;<tB6y83WpmnHgHh-2OfRu)3oCVFOs`+jUKj+D?Mx- zS9;hwuJo{VB)RExIBXr)o82$5>+k+onh%Gt-*(%ro3}mt*|*ampgi6)j|}SAG%VYU z#Yo{{TwNY}g@@<zqj6a*&~@~4Z~M~IMf_-kMU*pGL^*>+lrvaFIfF%%Ggw49gGH1x zSVTF4MU*pGL^)$fi8EM4IfF%%GgurugT=8kSR6Z6fyrt$S*<32$!axOttPA0bfs86 ziui<5OrC^z=L?a>5@H95*k;lCvc%xE&B@rE&H^c4mRLnn;Bg_WSmZHIC*ns3I4oa^ zSQV{rA(~f0`;y|$Tk+^oN%<1^X~hGbCFBz!xg<K{fwdCy%}i%F^hO@b$M}LUf^tMQ zo)5uyhqH+swKrPdLRhVY`eeTF`V=9mmc{Y5Gw?;Ok40dzv*0=*MwOifX9@CCg_e!# zk<f=@G;-G<%2p>sGn2xOtD?O|s}(`CqxGqRy+-p1a;1D(*dwVS4~V6JTeLnA=A6~X zV3QiK6<CZ0)r-=piq{&g&z#Q22u0$;et-3SH$OwzZ{8o6%p9A{9GlD>o6H=W%p9A{ z9GlD>o6H<DlWQ?^Y%z0eF>`G3-<UbJm^rpsCt$Hoz+$yptX7NFYOz`^R;x9U7!(iX z7wYkJ=8WW!#u9@v>Bn$9G=gC?pPxyPBWoBqzLZZvXH+1v9y0(aqs<1MX}fZ)Z>hc~ z2-F~h{Z3NEAcF$S0B4F3WU${N-!rJ)xOCt2V(;?s;{C(%m3uB-ygwTD(m;T)M6(`c zv#<wp#w`5ec2CNfDHvd4#ta-wBy|<nLn1Yc?|~pY*2tO}EF;69VJg6j2%R%$D1%VW zsL-;}P#*{5lt9%BQAk6z4rY@W?4=r2ehXqtj;ASsHjRZSq+yNY(P)$sjN4K`4xYq# zYRI01OsWC^$T%^kfn?j|j0&tm90+$Nivz~}@%J3NMZ1*+lIC8%HKX3O&8)P|thCLn zw9Txv&8)P|thCLnw9Txv&8)P|thCLnw9TwEqLU%g+!>qo4L0i=Y}PlhvK&^c!)kR{ zt&Sx(QDGJkXw@}R0{I#X@lr!EE(th~NO*~s12KoEs8THrm4`+$A_Q1UMx_{+M5?z{ z%n5wMNQ;lHa?(cr%pCf1XDNvTp#c@lSwx(iQHk*-;&4>V2}btFP7#O7BP8OO1SuI+ zE+I1NMj8NIWgC}eRJCzgBzac_0Dm&ZIeu!$rMi=z>`qE@RQZ!IIr_v%v5uaq2T0&5 z3;fIc0zDz{Z&KIfEBuwEgdr@a3exE<QhH>|Sm7(hy@(Sm8ENmo>sD=(nQey|V}~11 zW{e$Xj2&i-9cGLjW{e$Xj2&i-9cGLjW{e$Xj9DQrGsZ46#x66)F6#nZR;$Zub$JF> ztIKM2S*<Rs6(J#s?RQzN?u1SydGvpu{vXi)NA&+O{hy_oN?`Mq@J<i^ZUjo1j@-bT zs`Vo9RbUv-;O#0inuu%ugivJaN~4iZs;RGlq9|8QLosrJeM@hnDr_|}dZ}U7jkw#$ zLy$+EaWTl2NsYHLj9sp(kvXd7f@?g_=uT4H1{0x&WjW3>F0?U>V%`|uw`iqiHjV|h z1D`dB88Z{RJ`#&x$_X<LlN7q-8Wk*z)+okR67Ls0Z&735dlLjuj?Oqp85cZnk<kxe z8PhT<m;)2OBx79NMU9x-Oa-Qu0hnU4QEsDxt8hyv_)B3<+@uLo{n6A&8J3s}{_H$s zdW{PcmGg`XHU0}aunyy+$FS<lKgXrzZ_(UW!f5M@I^hIEcc^<ts5zbKjHa3nM^oMO zY|1zfV>EZ@xXcZ>aA{N?UKy$jb6K^+(S_-mI1sqgFM*wV57BIKX{Kb0&aGq`@+Zu# zstl<_CZ9aSlFehk_Frz(ZeNoZTcjr`?7bC4f3r>LNlIzE(r<imhnXtJi2mkC`5dVR z*NUcey&H%;&ahIXB0W8t(37%x5-Glv(zm1ee5uSpA~TT441${(DIp@+VpfQ6`O(Z7 zZqD#>hMzNrIb)nNCOKo8GiEtc&zVNfG;^k<jdP}*Gu@o&=gc5y4s#~nOmgNlXU;S) z=Z<skWNmHjc^|s&_BD+y&~1I`HoD;UqCw|5&k~`&v0c>pH&^ahd(PT%8q)WS#jiHM zI{fPLtH-bY@q4FNhG*`Xu&CQSNVj3Kf(;(5+u*^v4IZr9;K8~L9t7OrLBO$uMuT_r z?54Q#*x@EmX7X6!CXW$r@fhKj&D+CckXt-txy8w_c=Z;q-s06;;}7#l_7;z1Z_|jz zlhgMN@0#9!7Skmc?s;(9rVk`!{X=J`GmL1-%LX1;6nkfM`r`DVD`%mHq!DLHh<Sw6 z#y=L!ei*nslKolJb;tU2A+EiD`oZbt`!RcX)}nFk5qQ~)#I>K%Ki&HvC)Vb~qCygX z+2PdABE&kBSjU*ni8V$#qt_Mmx{O|f27cD9p<^<FCuSOnHYL%vkm%BQ{J?#~i{ppS zj%T*_aZa8i$a5)q9w*NXBJ?q4A}sx!z8G;6P8^AtZFtiWytga&+&euxHimX)b~=<+ z*YXzGNqDLof@W9HJfSq(oMt!?aRAtkr=r>906=hf{7tM0CUF?ewl&F2vrB2VhFC3_ z^Or$@{Vy=<ahg#rfk7Z>9!6*mC=Q1DWKOe-4OMu1-&uhJbI4gufNRW#PDr*paTc(O z;XKExM+x;E409rc8iG`PHbZ#w-{PR~1+kzG#QL0A8~hG6%=Rg@0Xk>;mZ+GogWXYT zF)>QDjD;(o80J_oIdIcp>r-mI5!~)Mwb0YppE1@D=A3}eG^hHQheg-TsT+vU>DFjy zh|uXaLZJ_N7<C&9SRypLJsX7~+a2AX-y2St2HHtNu+xz6x*3c`D0j#5d=Wz4af~qJ zyc0}yL4z}#j72DVR}aQxPJ?Uffe0Z_#xjC7cVdO_JsUCYd3w+HY>~zrj>e*0-e}^B zy1eno7G)BR)zOVOA1yp{?OAK08(~+Qn`@h!Uz?j>o10&on_tYyQftqxwZpBr!#fd& z7vu2G!{OH2;k|&vdjV(4s&#m8;PP@@UXIJFcX>H3C&T4rV0wU(!7rRB-V@CPgTSkK z<AYEbzKNv?p2jzawh+8;*|rd#Z(~b?NaOhAo(kLYXZA$AcM*bK+<W@GOwS8};`>$z zAPhnDFns_8<9SEJsSe;$G><0JvA7?bz8&5>fgyxCu_*Xx`ibM}9O%Yq?1}USx*<`H zF?J`Sd}BPed{L(94nr`RLoDBN8qCp17kn@$o^Qh&d1P&E>+|et!56`^_N_mGXL;w^ zk#{xN^*^xZqo02TeLnu3FVpLZ7qIIMJ$Ak6yDZ&P-hK!D{i%P%u228M2k7%HkF)37 z{_`v7^Bvz{&(A)|o}W`aPv38R5%1UTT-*Hm7wDC4V!|`s#Dpu|#Dpu|#Dpt0D<K`4 z@t=;cEAgrD2cL>wul;w81J^ikjRV&>aE$}kIB<;v*En#E1J^ikjRV&>aE$~1M{$5I z7GTPe%;k;48NuN6?85ZQ`0nZDv*U>~vVDYEzbbzxSp8#K|A@~e8e=+rIGJJfoX!~X zDP%gwhxxStyK(7stm*m|Chf&M=9o|4WA^*f_$(&P^dA(>Cm{3Zul>wgPQwxmnK2;K z9=KxK<MwB*`+Tbd)?mJJ?3r5b_O*4fm3bZ8BixZa@z%wz_;s=Oc3pgqvo7|=u8Z}| zb+Lkun~~}J{*2tvz2EC%W5c>w)+E)_%~JG+Y}BMTzG0jE#^X0wEvIkD-dc8pEwAK; z?h~RnWH$r7A-fak4cVl~Zm{W)-H`2WmoDFR=5F)My${}h<<ey=&pmWzpUoyuC;P(- z4@`Rx-#2|;6<et<U4HoavBZ<nna-tGkyY4es^jaSVm)^;L-T<P7ihsE+f|_8W;Q1j zl7XA6P|LC7qJpbX_zgFT!rApIRFOZ*ijGV53M77}`0$l!;qv9-!&gyENAi?_tI)I@ zoeaDR1#3Bwd=<()dW^|pEsMs)1(vLaIsC0@+0$wn(;msKWiI!~3N(<kxr{XASD+!k zQn|U@k&HAPCFHJ<HlH!=u@&egX|vKx(&o|Y9s4=$Wx3q5)@T<%HujMlx@n%>IHNJU z!G=hBL$(po8?r5k+|Umo=?(etf!>f08R!l9FoE3Aea7U5eq}{&=nit+u<w%S4cUan zZm=vvZs_)OdPBC7ksG?1o8FMGGjPK`3|aU2o|yIFbf`}~%U>7!vDU?&p>^?D=lTq* zy;d*^aKrZAWz!16v7;HKcARoITe&3dD&-!_NW(hHT_x@DjOCtKLEBE`j%G&W3DCQq znTA~Mx{PUWK)KgvOndVRZMzxe-juOi(gPjAMxShmI0EChN@`MeR$EES*0QHY*_rM8 zF+%sztQKgUq|RqW-3=u5^~bc0m=8(allrBLcReA`BuoE#75ew}>v;c;iy3PWy6^g> zQ5Oz2b~Rzk@bik;+K3I1)63I~<LUE=Ykd_cI&uA+q7_}5EIfR1mPR(PVU4yAq<s0N zzdLgDX>3$>ge*Ekf#{-h?vb^%AHyOsp6TK<u5|GkSGxF&D_wj>y4+WOk*1@I(0ET5 zp>d^)(74h?Xk6(cG}0ga_gHksGhK|vl`cl({qYZB5gOO)9%H}L#cBMG#p#=FSopi? z=4YO?UcPrQzVw>6YVXzlwf14{aqaWk7qzcw-$Z+k|Bv-==W(=`2J9a_c6|N16W8Bx z<4tS-a^nT($tfP}?3h8ZU#pilOv~Kd*o;J2>sC4&wJJTrt#ZEB?R61^sj#`x#cAam zC$V$JvxZY|HX1r(Y#}+hu~X>oY@8g9jPZ1$kI=0L_9U=Jf$NV(21cM3Muo=MAJ0a! zp*x;U&GE$dheLlH7?xqqoT=_wmVOdP@-%AA=i*?g|F+~{sY0nfz$x^d@PIpVlp#M& z3V{kL9UKHz-Y6b!lq=OjA4C_r8|*C7-uxY6RgfdCHWE%p=^l1_l?FK#rC({5Duaq| zJ5IUa4m{f|4s2`y9~4c~9|YK-Z<MO8ZB}p&V(c8NcBRv(b-T4zb91ADSdFJn()NL! zguIidH%>NNO?vHA3T5(q(5c}EamZDdG%Dd3EXN^%0(z8qe$Z6(d{IS~6I;*2i7?{C zYtgdE34Bki%{X(b)7v<x^>#K4oM;qzw5*L{rQT|8cL%-JpjRl?E6>OAH?_=1UjdU+ zIFX3ZY21<*Y&`$Qhv@nF8?^LDTjojC$2=iW1d#*@@`sutrxNlo0r4Z)qQ_gAn<s(N z$Csg$#mr#mfL>*Umi54@_)clydPQqsJEiKNST*cHp-`^6rsr2I7f0g7U{ftLYW2eb zJv31P8xF8-M0RgxNtg+Vr&dE^wzfr}w;YLEKw>2`60JBABf(H!2ozpXppYiZgh14= z8Un4&7Xg8<=S@%?MoWXX3<}k1tzIjT)4g#hJwqe=C4$KqGNe2TXq?zO4H6y`M;5AR zsFQPpMaYnlfcdK*ppA>;n;Zz($0>ikTJsQk9{zk{>&1}T0}83d2+kxw9OqWrsg(8* zLXFp1tn>~lmF9-NQD~MotT49A86fW<GxFl+;1y!T5nCmAySat0Ge7d2j~>OD&o@8i zho1Vhr$6JCTW`Dlj<uJdv2RtJ-WD$~TWjG9#Naw7BK5>mXEXm4fabHyK+`zaZdAHm zz*Hbsjn&F|bdosPQ3WJ!!bEurG>e|J)dyO(2RJTRRUQs;{2>TYoGl$;M5G6b_7=ss z`G~fTc4hgeLPW1t;4zXqlXOsBgr*F=S#dM}GzjHSmvm={;hP|gtKn`HJ-1vey6&KA zSKNVJs1yc%fY=)rKAXTs<x06&LYEygS(QS!hjYrC+qGW5Ob)HDw+g*eroG@SHCvtD z&HxcomnB4m_G}3c;P<3g4a__po!-4%@iVwz*?1szdU1Cv{|vyil-btA57npgX7IcP z@VuN1Pm(GVEK&Swutb3JTLH`O=F>a5#6W6KiqC~FKniBr4+gG}Fq<|$?!<t=DGaQV zj*ki}fNgQn=Mv=hJ2jPo;ijluY4>(EVNQFMZH&iaa7UkB8HDHHCJz&ML1cKgq^?G# zTq~$gyTy=0*hdmt7|!6JT_D7z)=Prcgu}8J`TaK3vs~#cDI#nj=K`mKzb1wnL)HCS zX_@hF7dq&n2ARCm{B5hbr`cd2Y3j0pQvP;mZZEUuT5;7CAgeW5v?ekJWgS8O4yf{* z6wW|WwoJNB2v|+Ikpte!*Ka*I25;r$sjU~j==6)vyhJz5wLBSGec?#D(#IlS%+);^ zka6XyG>V1Jc54uer*O*MVnbOHVPWGH7@c#x)!|?Ci8GQ8_&Vu=`t7Wxw)@3;twbI3 z!C|GaOc!YK?#@!3As#3wWm68-+W88dTR=c_KG(3{+GDTwTSS-Z^BIRw+mgA+nKAb` z;n)lP4$T8P%?@Tv=D(opI|Vd1Go#tjWByW1*sM$10*+sw+$G8BGXa>?xf+0xBmY7m z^*5HdvJx?>FnSEXa#F&<WI4pqOu5j6(4zybhy=p)5XFliiZDoEq{Rx?)lL(Isx^zj z=5U(4lE|(6X^8SO3X>%BB(ace?gv^DX>LMnwp1EZ2Q~I){>4DT`&L4N2&2ab<Z?T+ z%U%?tfC?mj2Bf`vCDM5Ky6+E^JYrokf<e|xK-PPgTlu8%-_;m$7^Y*`g`$b6UN0Ef zMZ+8vOxqc_PO0byM$xkjASmX<ut)`bZnso|zs3w}VIMKq$b=`E)GZ+nTcyN*DYe>% zo!a(}c*M*vzr{>3qJxnPol0n-yZs^p2=ElFW1&e9k)c)&CyIG|hA=FUvK!0h{^+Z@ zKiN39n)_qGN@bs(GvYB4piCq*Ahc*JZ^F1JW_oC{Ox**iL9!mC=v1449Pn>GwegbU z@NX>J!MHUz`_dnN*^jK{Eg=6x%a9+9$T#<HuHq7hRjC*yEM*L;UI}CFpimkFwrdS4 zZlP%Tj#aj8M171YfE4p@QPm9{-?@sB1M#6$JsS5OBz-E9+4)kBXar)d@3hKYa@*>` z0U|f>`w_td5dcCDm%OD50fSZ*C+K6?)a=nby-I1P2}cVPkw_h?O*U<?R~xoxW22-E z3&VB?=&xsXmyCD>uBcGB61Y%;*D9Q)2-33Y0_s~)7Z7&K1L%Kq3FsF?@A1x&bca_F z`T)FDv8$zlQz2_7o?RXkD?w#o1%9PkaVwUe0DTs=5pziOWupR?a`||l2Ug6m8WRs0 zDTl$~<z#l#7xmV}cpqJ~zZ&$ju`wO`0ifT>4*mE@Apt?G7dQ(Ac%#C_T+EbB4^|#B z7#K3V6ngOAmrS&7S6VdSB8$iRq*#BuSoK|;bTKGeMOc5|#PVS=C=KkATeWr9D47Oy zFlPPR^}^DL7fcUEMk3tKw_9Y@b*|On>-r&vsf#~kv(!Jl8eFqAIUTMq16&)K;cCYh z0%Somr2Yty`nqIOEhW!{QBvb-wi-FG(dEKRt#$Y<FTb-;EMW`9_D*egE&p;r_t%$9 z#?Fn7SSl#1!g*PX5eMgp*ti-#zFn}&g(CKD7#`LZY(RZbC>F{Czho3ErehX^U=if= z7V+s(?(TQ?h`&+j49?f~$z8ElEVati<1e>r<V~f}92ge8gLB^`H&~}Cvr$29o4q0P znw@5$PJXR*i!_x;jh!MT^1d!!)UhRmya=73c7wfW_dB$New$_xPaRs^!N7t_g>DZY zEhJn0xQa(TvQnqj?IO_9AnafWE!!PNLap1Rt!A{5?&HqY#u3@1BkAMPY19JrbA@%f zQf#(kE_*SWC_+15ldPSQ#F=!nh}u@uP2|XzpoE`ZGMcZ~5I4O%fY)5$(+Z_lrz{jQ zqGU09-XU}Q$SBOM%3F_vgodUrP_w-RRX{}<BH7PuGBM<k_5i7x#%yN=(s{F@msjM> zBq~s`nxx##d=-NF;u1lH5@KT*A))Yf8`@UG^4b!v^6oRtMm)%FJEd<<7U0r)AiZr! z??*FB57tzYUPz&6?`HUhH+3t&0~tQ5kYSjb=g%ZO5FrUKEJRe9I*u=I=4+7T_m1*L zD|Werg>qf!RH7=ET|u}gN!U7!UYANlBxln|g937-%mT&5j-I{NtWhsBBgD$B0c^&g zd)S2WDz;j6^l|Z0E7<F|Y@koV%Ga~DF6*jFoDi(7RGNrWmooB-XK7&FCv_Dxo`FlY z+$!~nz*eb?XH8q2Rn!(P)1<{uWp2uDw?%z`^Nsq+g^dvN9xLK<hjl{RccJb7Dzmm@ zQ&6(D$8=(It`69-bnKp&efc{0ntSy|v(-M=>Gt~jYxzA`fp;vk0&=;Tj6r<kQiPOL zGf>#f*I@?Uuw>*$2R6ctM>^W8;?0&2R>dk|p4usT16wcXaAYxmp}W|XV3phgw$LmL z*)r^UN!>yNP&8H?W@RE%a_bf*#G1fiH8T!vKL$iD73L$ffUF0TJvf#w6OKq7Qm+DG zr%wnYN4^aReP)SI&~DK|@RI~0Yz;0O6>J+WxFt+|nN<vl3OaVQREtL0^K8#|A|QhF z&#fl?Y~`m*zXR#-WR|`W->e%ll*zvf`73sfi<FrVK<Zfy0m$+H_jaX0k{s1}YSc(! zA%Vaktz@t_hD+lZZ&YPf)-gXE^f5itcTZ0r)7TN?>gsA~(eCWBJF5c(h)aMZE`dNC z;s<sFV1a}{2R8!3Ha0(SV24d`h)Wy-B+dx?hky8GWoC6{b!Bz+N@#veR#j)deEGeX zFJHdQ%F1;?1Nhg)C9VYHVlU9faK9{n8Zjy{@15qCSn?iV=w-$*9#Q6%bCy>41K@^y zD4c|IAQVrQTlxU+trEk{OEEH;V2%fyIgOc`Q_Z(S7Gr)>{m=7Zpz+53<ccegzVxbN z$M=_Va4^M%|2QWu&F4eZGk9`h(l~EhB?lHD3q@-K0&<20Z=B@eUjZIiYTm*5*;oac zt?$vX2D~d4sOXg{t$t;vM`H|ZH4-)ik;yxm9>Uu|<Z=Tx_y-r225fX8PlC_{tiX`n zUK|4Qya8d3AlfhuLu?Iqg7uYFp$bc#Boo_~6<oH;-2v>sqSw7<5W?tg><0M;txgSg zY7Vl6C%^!W7mF%n!%;9mbE67Abf)avS<3M}+#E}$PSh+-px;QgvxxaK^%_#=V#Wzd z<gS3eaOa%9Ao^TSPG8954ZM-p;WaFXM1!YenR~D%kN506oUXp1=HcBIFcUFz7`o(V z;b4bl7nnLsJ;{u7sc2{|)lHYV`_LJ#T&y!>19Gv>aCD*0pvNZ(F@2();idA<Am-21 z8AzRrbp|Mry9zqPH|Dtej2SW=-s-fPm6y=He1R!_k{mcxE*D`au=h4fItB->sO}Zu zuz@|t&O2%mUQ#r1!dCfCr`3Ta@*W8>>@?sV5U}+umJrOgV=YRJDzI{F-(TA5B`EB% z#oWhitrHY>X7X?x6g2xJwiu5%rpuV>>ptk~TO>|TYWYn1OITV=f1w2UI~Nz1p9=oY ziIXoob^74U)z@5m-OHD9CqS+5&!W~(*2XKxE|#=>36|W;23dU1!-Uq!!y1i>ins_n zvXBLXS<JWxpwdmI$+1zcl<-m(M08TjILjE0vBbYlCJEYmaxv}A*0gl(eHm!)W_HIt zz7d=P#aAy*fu3HIJojZ8GbyCA@NRD?kvk2V_vk#$>je(vlVm$La9(pP9TvUI2=@G$ zpn7(>0N!??WZJM$SS(|h<i%7k!#1YW6*i`DL7ohX8PRka?ORA;4;E9{Y=ERI>={tl z?6Y+eq7s6Ju;Y<TA-Kj~4H_$%t@E6jRF>4Um<@#zxobdWZ<yl{(;Yxhjw6iWD<EY{ zv-XPM$?X~1#>QR&!)E)!MzL%lyJQ(Ebj`&THfV#myj>5LrGvR&@GF_Th4VdtK70t9 z^qM<#-H<-x`~9-?f^25@+Qq!wY$yp@417$_F-SPLAa@;T^#T#d_(F1k4A<$GgHB5# zkThQ=jW!luK86x-EXZBmTP^^v;1$=u@>Q?CL3xexTIF@h>y<YsH!4fHSAf2MZBBd= z_7<Sv-7lu{a4#D%<C>WE%2+(_6EQow9)woSJa(30f45+YrY&6|ja<#W5(FoCNjJ=! zNp_?v!5RhhQzKYVB=;(4&nLNfdd&(PP7)jfvU9iuTSY3(Vt0l_uV!rz3+>I_?RH_< zZj~V8E{5IdR<jM8VM`mkd+~5-(mP<6uOz)cM-ETfIb5#5k_`j_gUtie7Xu?duI641 z!2O5BKTfJo4q{4sRt_galF0lF!?VHrl&V7LayKX-lh-aPlUjG9w(Y<c{#~nAEmVfJ zddD30yAJH32xJo9&rUGvR0_Rrs{=-)2eGvv%qw908U;YUI|XD;APrn8V{&jgH%S0W z$%XCAy_Nv@fkgpq+tuR6cC%5(78=yl%@V4&je4)f=*G>junT6o=YaV75ez{m!ca@( z@oMgM1ll<-C<V*?Y0%18mLnVPDWI`#&RY`DGU6c~xz`h%`7}6wv<;c_^Vb#y6Ucz0 zb9+!dVJV*MGiUgb=c~ClkUDN!RF?XVy{R>}?P0UNU21Dx72d?r>l^I>V?G`}jq?;+ z#TF!YBaz$#l1?EKN|T+GMY-@qu99RXB^tIH=J1Wq9T&oA{YK?Y%A1v&l$(`Xl(#5v zRo<q&U3rIcD-79hB$&TxQOuh~tE6ss3msgCk3x5<!!mB;MzQCFb&Erx2-W3a>?5yv z3W&dnAb#KDMqJ9E9PubmNq9?1hHcKhnSgocqF|QMMm1m2daXvQg}P1L>(?B;+Eq11 zIPO;nU`pSi45sOcFLerd-$d~KRu;S^HKpmjv}JNBNZC#jiIjebT<&HflJND#<Pkn@ z&U&?S)2x+i>aH`~-0bukgPrnFM=hhY%?QWCB(tvoT)r~%l7)c#Ed=*3NN}Gemx{8C z*%XvvJ*~6{XUY|*E074TjBJQO?kxn|+ZNS#)2{SzyNq_M&8lNn2TtF>mAqp#Lc*f) zm__6Am2e9&EIL-ol_ZDetpwq(JRt~6IhG@w$Y2u8QraOJxwjFR7j_bhdedt6+qm1( zJF2l&Zs8(stMz)H5ssS`;q8FSSNMw;g4VvBU@CJG1p;XbE9FcMC*vjwKq<Abow;`q z0AIhTo_6qdyH-R)tzH;3hCRJj*SdXJEippEqMi!vhgn1Tl8OM~tpvh*Bx)&;NP|$y zn;gQBpCk~a^uo4+GjYXLg{Oft@lNGk%5BQKmG>yOEALg_r@UYJfO3cOL2xGCNpQY# zQJi;b6?>o-^sT{Ww^g;aJN03siMG^ISoa*9r*HiTNWY69{i(-`w3NFvq(hF9u$EE` z+nc+M0D1SKK-PB)`etvZg4DvmDGoP#tx~>j=6A?@t7NJkUz(C^X(#Skd?SAZSeWJn zL1nsOKk~zB?%f3Of0rOGsVz;*r45o}Kf}7fZjv~pm4v9}-b2Lk&PB!1sP}g@1lw;) z{i;>p=vQ|vy;tv8Wkxu@lpW637`%KBIII!MN4eVx<PS<9mrzWDT*|Z@a*@3xh^0hB zBy#U15WjIzh*7WCZ<IFj^=(wOdiECD9JV%^rQ(ng&ae@UZ#|37<X3a=Be32pftBwe zo4XIKSCcX*hm%qbcEcrv<D>+`7U$kipuBleD4VF)!6xi@A3|WHQyNr_O{dy#!og(J zc)(lA!qlF3w5jxAejizF5)8u!2%Mjmz?ncPO+%%O$|0Sym86|gf?>;ZcMwSLSQOGN zU2SX*23omYs_Nxtp=uW^YOT|%Fv9WBX}sU_h)q;A1#=etVKw(b0`eCnAd9rq@Rf2c z$CooIFqZ_ily->7($b$?IC{s^!Ogf+`Iz!?<u2uJ<rB(1$|sdiDfcS(Dffe$aVL?+ zEnFJ$*;~2Rsp_pQv{Q#SlFI{i*wS=$tKO(=@Q6@cl@mh9;mdEJ)JZ?bLVv5dj}h?y zPXc}t1#X*DP9I8X2^>$^>TFatn}^|`Q7MbFB1j<_YC9amUEYA>?ur|5Y&$(;Y~@H2 z3V;pZ5S~n%`#6zH7)zR!OR-jQYAv&8w08>KLcZTq3!BvjYVlchQrMcj6cah4gS;CA zdzzEDnGI>*MP%~73&<oOF7F%VnzRLHFAph6CJzARY~^t`Y5#ju+CM*}U#_+7&7n3J zHrtMBl^RA@t7@BS+aY^61Qndd@e|n%4vdZ~%qNJ#oMbC(v-)DR2Rr?G8+F>Ha;e|Y zdMdVdw35oh^hx@{o0Jw~yoccO?Z=6Wl(`r#>0!lkO-g5u{DmlB9N15i7TzUkp;$6a z-8af;XbkfGPN`>?TV+`GQwQx<xep<ALH1K0fVwS-8o(ATAqV$n<6M7=aQ)%O&9%AJ z+uiQ%n!9GH+rZU&uc2AJ&2As2KLXdHWD<D}vjoe@F>`k>VfSCMu)A>Rv|O_b6*aFF zjl!U0cS<eQ$XlCgVY>`VPC_G6;-!f#lB0hg>5SpgezU!?qgTs1a+>CFXl&zFsamd- zOSoQ@#ghazlOT|AjGpMz+)pt6pFa}D(iX)q7F!Ywd1nPrqCf=Vu^s_2m+P;8^dfME z9#B55d`9`K@;T-6$`_O`Dqm6_RKBcy1)QM=h}d2&wV}G!&7-n0s5R7LrLWmeXOq0) zJLD@haKNzO)(h<HfvPp~T{7T`&u849PZK#kl1WaSW}4xVa(y<EkrHDT79dzyGP%zX z*!kB`X0^dI8^g}9)!uDySGEVZRxh>n=1_+do`bwIp%@Wfc7WnM&_7E+|5+x`*=dTf zPxdf4qwKpS5$~>ClX84EkV}b8o4+{ZpCgbTm1;UJ8jXIX2ituI^=-YPmYaiJILAWg z;g<NC4M0u~`3p__j81=^z$@E!5|mC;VJSN?Jfrm|`Rh_T3`%I$;!u8pKq+^;8aAw> z7vS8<Zb=)!uwSp+MMv*9xT8J;W#kOFG(^8h5S2Mz(PQV*fR!@0Ah1$8j|bQ<krsYL z(j{jXveMewZdZ%da>sGX+v=ca^~$@wA)N5J6L-Ah>x`?(`aokKu>mIcAmLZG8nGC^ zxLGk#VY}Zpd)r&>%`(=UO@|!R5R;2!Wh_yN<=)Ddi3XpM8UwvF*irR1&cjBjVb4O< zV!v*08L;I)h=j)#c($c!YWx)f+Cxu>kY#+vFq>y2WMrnzO@vn%bz-UkCo4Sf73MR* z_xO(TUFCbqL(2D+A1FUmex&?Zd06?0@>B3VzC(mCmIwhH!_9iDKu*mp1;^0^7qDk7 z=5a*x0T1<7aL}f>wQ4o@T~gIcvsBd@!1vooon@FO&Z{kwS=h_9n){vtyN3DMPYK`K zh_b!cu90uO!Mjk?UBENnkjg!z!1maOc-Ttv_BN0uhqqK<2Rl8ukKREI;SrXx{n>f= z$O!D~OM2@>4G&LcROqM`9`VB71&I6z3IlCOJ~>8@Q==nh0}fIxUXIY(VMI>t`aTiV zHWO52lWN4-;4?u8UG0_PA^Hda5al6AkhZVpen2|i4U$ebEhM+7VV(fX2xlak6xK5n za*r{a`ypxH-^mq`(P9axVWJMwpF!Qjd@=R=5m7(>9z?SGv7(uD@5e;<?$4@wd+kmO zzCPFuKaa@orC)H<oY~X(!a~xW{N{{;mS@!*bn4+~=Z{EEqEqoXO+@lxyEyPrAw;c1 zR)g0MS93oh%J!jo$~J{5lh(1irRtZ+N~%6uE4iN%Xs({Cj$s3t(h%DijD!nfCCtuY zdb)Y@HP3{Pg#JwVx$=ne3+0!}uarlX$CO_yzfpdx{7(73@&_0uen!xI_>Y8U*r3>u zIek9sRnIB}GrGZ;!Es3(GQ*<NzDvv#ynYaTzb*H3A`gB-M|@rw77jHhT5r-=#rt@Y z-U_nFJwht}Fz?|RY7SL`==_u+`eIp%(F(Hy0s;-ef<-$O>;J-v;4g?lx@XSoe1O+Z zGvD6XYK6lgzfTzu>8l?JFJVrt4PUSl4?c#Gk9_!KrX(LGgRjJuc>Ce>aCzQFz6lZU zlKd>NDKAqeX=?m<jdWa^J_lR)OCsl9=J6HBgX99HD^WOCbH5@T^+tI|<+)SVtRm!R z4IM8}ewpo+(UBI|&OJ(6x-U0vA+;!i%&f&XeMi^=wT%`Kw01T37=b|M+efLSn*2o8 zVg^cJVit`^T@gPzwA;`?nSBZg3oR9_)UOG&S7v}Vw4#}C4Rgmp{S5*28&8;CMXC{E z;nU~oaEAG&jgnzWvr26JZwZPibCN_YNcIPLRzYN>-wQ_bccj9U`4*=#Z&cy=rtO-k z>i5L|<S)M^`=6W&^yYc9AIHl5K{@=wA^CE6sjye9x8PhwI0Yy_Jp5PM($doHH(&Cn z%6UrgRrBYa`(Hu&{HtmDg2yfn?qBpb-wwWRe(7JgX?ou?gY>fXi-O-*|MsFohb~z; z^xWrOdhT)e<Pl^AiS7ppHl9KrPM)^!9`}zN-5>kc9??we!pJSM>D_@lI_4kIfv=}L z_~!q9mU3t*prENV(P)BbqSM5n38slj6Uu;Q(>sSIE=@)>@n|xp31wBA1gz2u5Umx_ zS`m9lYelqHL~BK+MH;hC+1Jc9@09Nz_<jG>>7yt1*U{Ql{&nm2#D#;ZRb%aH_t-kN zRehvSG@+a`zntlgZPOWh(Q-(iU(P^d&-5pbP|liP4*Ra>YK}|GQQ09ox!lOH9M48U z`&{2u;ZyT=v_4%;E+^mupPaXCdm@z6GL%ceQ^)hmd8VtIo`r?_GRR?qu<KeQD~gLD zY2V29+{t((#l=AL`NRS)R1Stj?jQ$bn3}x447iB(QN1>@=(WHnE0;yj66A+7$es6x zbA$f37ZoGU&`b@Fv8!snP%eY`bu==4tn1NorbHZ3_2%pf<+8vdbVCZOB~iC%eM#Nd zlE6B$9a{rkB<3lp`&t>=hy00W>#7O%`+M&?|JmGrv+)2?bBw4tM${Z5YK{>#$B3F^ zM9ndx=9rpXotk5vnq!@sW1am*&9P3+u}%j8osB}YR-M+W(^_>}t4?dx$5bG+Us*<S ze;nEV1dmKPK$-eS)Sgm7+L;77(p?7&_SBZ}DJnqJp3?T&l8!OYZrEq3sE$P8qI@QG zL`TABw4A8m6i+8Z2j=CT9oUT%C;k2IDR=+0>m9&=ecFzW=4uAL=k`D>m<2Cp_k@C4 z2Ee!jAr{PlSR$)h2FSSW5^Buidx)%39n_FSdBF-zuqzH;v_@V~(#uA!>N%bz^iHv$ z%x!@h$Fbp3C@Aw=M$X9d9dTS23rYrda0o`DkuGOdm#7>R^n5L?Mu`GI!LjB$DK$!T z6~v*bz9<eb?ho90&c(_l)H!EfzCH;YX@gp6gIZ~WT4{q?X@gp6gIZ~WT4{q?X@gp6 zgIZ~WT4{q?X@gp6gN_Xb9UBZfHc(oa){1GZnAVDQQ5q5*AK4>QoA`2U3`ut+1@XvB zDJT`A>qC_(1*KwieP=vQDJT`A>yMqZ8fD;sPoRNg8+8f<RAo-u$etkA76(hQpcI^0 zjS~w>!I>itECa_G86zVF94rIJo`b5)B^=q{bfzhQDQ)Atg0eQ+jyl#-OpYmS<Jg09 z{NxId>P}jsJ1NLf=1+pjNoUoPjQ|O#zA!S9tMX~Mz?d!V?{gTl<tm-tBB@6<4+@z} zmZV^?WTf0Sx<px_W*bvujF|zY#u!s$jHxlk)EHxGj4?IFm>OeDjWK2+9%_s!5tABY zlNw`_8e@|V0Vb{0q_vu?0Ik)ewVJe6lh$g|T1{H3IS%48R`A~r{yV|{QSjdj{?k0Z zC{w9=yOxt?-yEq-N2X(?j2e#A-Eb%1?MgcoQl(x)y80niYBbVWMXJ<jq+1)LNoFpf ze(7!0q-~UmgSz%ei@S~bY^N3-Rq8e(E#5{}5LiuB05szoPYask6t@API5^XSGc~>- zFjL1)j2iCA*#=DvI2PD&Y^u==3E1@^Mj5jrf#PbC!k1X11j3~GShVw@nSdd$(Xp-Z zNIpcM8PA&+giwV8{Y2)KAYB3uiE*SCjCXflBg8h7?F18to<T8ruu%X<m!JyX<rw}_ z+88%!oK<_|YbnzbG~>@s3#QjN6R4aPoT>581C2hjKR!`EamA7Nh-!;J$E9Vj(qY>_ zezGp<6WDQ0SGBaT=XCr@&~tou<f{l`I7B%tn4_64HmMr`{j202xU(IM6Eh>;LR=A2 zg80#5acQO$iv|HRP5BdIt1?5H)F+pglzac-YloCem&C;u;SLLTPV~+|!kv)QFooaP z;tuT_Sd0FKg>qP^!PKKUP3r<8k0X?dK+;m987-lhC9q-(Ic+0a&K77o0-KJ&rsF)F zvJz?}UCavgTf9Zfnz^i5%bLBcxyzcjtc{m7e_5L>BXt>R%LpwaUGbKYv5d@RWG^FU z8M(^{o{X1~zl<h|wQPFJ<`@>}p8w$sE?rXS0^RC2>4Mx}e)F5pdCqyKPMkP6qC=kC zIe))-+0t{C&hxH3vwzhQhovq{N9P^)58NY1$L9tmG+IzYEVWo_v(#a!%hC}`J(l{E z3@eU!s?(nl%ZA46enL}SWvRhZ%u<u3j18Stue0iPR=w_hL<yR=?yQZDo$#(&N9Niw ze}COuJLz8GpI+CzwPQ!G_S?aYx^~>XcHP%t5GD^Ba6=xQTsOyS1cY_?y1l9*$8#XO zcW@Ne?vQDE>UizU{+ZMM818HxkJw%QfUo31=!LE6Ico<eymfa1!`BJVJ9E<A_pV!? z1b^z}LyS|Ga~d#eb4HB_qkh1sZI7mmT4Ocjb>F}CU>z0|DbF_Wthpl_CK{Y)U$s-X zbOSCeq&g8U4M0WpKgxI<F&<rkM`IM>(Xc6#jyq-2R$=`?0MrYZ8zWejV$1=AdJ1zR z%ADpJ5$1qM{LfOfr|RS(LcZ#ao{C6Ks_L4W!LBtJoK1F(Y~67AwL38ozaEVYo*#Mk z^m>e^dBzjfq;&<#q4Tm6OSRdxu0cSIU*oaOY3k<K2%me7;juiRXPU~d&9TaPGaNm9 zj#ba&`LSVoJP$TU25%PzwcvGO+x0k2Y)@<zR^#zUPyHD&0n8}S)L@;`V4cihoy=gJ z%wU}iB5Xl9)+aIRtC*Pz%*bG7A~5TdnDuqc`a1S$wV3sJlhH959g|gWGCC$>!(?p0 zWG`TYC5$O%Zzc>x>=;Jac*hBafqkro&wa<^g2xlx<H9plPvy@|&4%khu~glj7-L4? z1k(`aAwToyx)mylY3QK>na0Ep^W%}tmBum0w#Ldjj&Fq5zUA?27v2iwLUkuZ`KWS_ zG;A|P*BsmA`85dsaXOj{TFmHZE-bonn#8JcWl~LKbDFB<+8hVffe8hSEjKJJt$vlJ z`k^3I9{Cpd2`^qc=WPv|{u8}_%2!_!+@JRtYz2Ye&wn9JFX+<rX}_lVF1qQm;P+?# zGfkiM_1lB{iyxr(hyLv)!Tn_q)BESXh~EE2c{g}|_#5zi>BUPcKlpl(23u0#KG>21 zX|N>)(qKypq_Fh_@<B8H^&Fa#5fc7jgggQo6IcpsY>@84t{oe?X>e^LSegj0T_Roj z9E1x<(Q5;?H1KO|lWQ_^aE+mPOk<h{EJaAuvWOPaJhtwEr0KN-)+N05RM42<`N;K& z6zTQYCsGftJ!BK9(`)Pyvlm>C^$C$Ky~d^r8bhwNk!SJznx~C99nAyt!{}(9r_<3C iMokz*rvsa}HaX@Wcvt$T)?IgOs+K)I{73ma|NjB-g?nuP literal 0 HcmV?d00001 From 81cb6f6f28478da0d6092cce911ec541a861e69f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:51:35 +0000 Subject: [PATCH 145/181] Update codecov/codecov-action action to v5 --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 547af910..80737eb6 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -59,6 +59,6 @@ jobs: - name: Build with Gradle (JDK ${{ env.currentBuildVersion }}) run: ./gradlew clean check jacocoTestReport - name: Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: files: ./build/reports/jacoco/test/jacocoTestReport.xml #optional From 850e1aca915acdc0c232410bc28fb0763cbe95bf Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 15 Nov 2024 13:04:20 +0100 Subject: [PATCH 146/181] Add missing property for testing with file based h2 database. --- .../kit/datamanager/metastore2/runner/Migrate2DataciteTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java index d418989d..7cea440f 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java @@ -97,6 +97,7 @@ WithSecurityContextTestExecutionListener.class}) @ActiveProfiles("test") @TestPropertySource(properties = {"server.port=41417"}) +@TestPropertySource(properties = {"spring.jpa.hibernate.ddl-auto=update"}) @TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:file:./src/test/resources/migrateToV2/migrationDatabase;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) @TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/migrationRunner/schema"}) @TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/migrationRunner/metadata"}) From 9698244cecb46b38c565265699b51bb4d001fe5f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:07:22 +0000 Subject: [PATCH 147/181] Update dependency org.springframework:spring-messaging to v6.2.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c839046a..ab9f0b8d 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,7 @@ if (System.getProperty('profile') == 'minimal') { dependencies { // Spring - implementation 'org.springframework:spring-messaging:6.1.14' + implementation 'org.springframework:spring-messaging:6.2.0' implementation 'org.springframework.cloud:spring-cloud-gateway-mvc:4.1.5' // Spring Boot From dd817334a2be5cd48819be91d15601c377542744 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 15 Nov 2024 14:32:45 +0100 Subject: [PATCH 148/181] Setup and cleanup the database for migration test. --- .../runner/Migrate2DataciteTest.java | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java index 7cea440f..cd4a43ba 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java @@ -15,33 +15,21 @@ */ package edu.kit.datamanager.metastore2.runner; -import com.fasterxml.jackson.databind.ObjectMapper; -import edu.kit.datamanager.entities.PERMISSION; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.IDataRecordDao; import edu.kit.datamanager.metastore2.dao.ILinkedMetadataRecordDao; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; import edu.kit.datamanager.metastore2.dao.IUrl2PathDao; -import edu.kit.datamanager.metastore2.domain.MetadataRecord; -import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; -import edu.kit.datamanager.repo.domain.acl.AclEntry; -import edu.kit.datamanager.util.AuthenticationHelper; -import java.io.File; import java.io.IOException; -import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.text.SimpleDateFormat; -import java.util.Comparator; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; +import java.nio.file.StandardCopyOption; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.commons.io.FileUtils; import org.javers.core.Javers; import org.junit.After; import org.junit.AfterClass; @@ -57,7 +45,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.JUnitRestDocumentation; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; @@ -71,12 +58,9 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.ServletTestExecutionListener; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import static com.github.stefanbirkner.systemlambda.SystemLambda.*; +import static org.junit.Assert.assertFalse; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; /** @@ -98,7 +82,7 @@ @ActiveProfiles("test") @TestPropertySource(properties = {"server.port=41417"}) @TestPropertySource(properties = {"spring.jpa.hibernate.ddl-auto=update"}) -@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:file:./src/test/resources/migrateToV2/migrationDatabase;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:file:/tmp/metastore2/migrationRunner/database/migrationDatabase;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) @TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/migrationRunner/schema"}) @TestPropertySource(properties = {"metastore.metadata.metadataFolder=file:///tmp/metastore2/migrationRunner/metadata"}) @TestPropertySource(properties = {"metastore.metadata.schemaRegistries="}) @@ -139,10 +123,27 @@ public Migrate2DataciteTest() { @BeforeClass public static void setUpClass() { + Path dataBaseV1Source = Path.of("./src/test/resources/migrateToV2/migrationDatabase.mv.db"); + Path dataBaseV1Target = Path.of("/tmp/metastore2/migrationRunner/database/migrationDatabase.mv.db"); + StandardCopyOption overwriteExisting = StandardCopyOption.REPLACE_EXISTING; + try { + FileUtils.copyFile(dataBaseV1Source.toFile(), dataBaseV1Target.toFile(), overwriteExisting); + } catch (IOException ex) { + Logger.getLogger(Migrate2DataciteTest.class.getName()).log(Level.SEVERE, null, ex); + } + Assert.assertTrue("Database file does not exist!", dataBaseV1Target.toFile().exists()); } @AfterClass public static void tearDownClass() { + try { + Path pathToBeDeleted = Path.of("/tmp/metastore2/migrationRunner"); + FileUtils.deleteDirectory(pathToBeDeleted.toFile()); + assertFalse("Directory still exists", Files.exists(pathToBeDeleted)); + } catch (IOException ex) { + Logger.getLogger(Migrate2DataciteTest.class.getName()).log(Level.SEVERE, null, ex); + } + Logger.getLogger(Migrate2DataciteTest.class.getName()).log(Level.SEVERE, "Path should be deleted!"); } @Before From 2ede3b9c1289c5e2962e096b92d433b6ed5ec584 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 04:09:31 +0000 Subject: [PATCH 149/181] Update dependency org.springframework.data:spring-data-elasticsearch to v5.4.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c839046a..d5360d5d 100644 --- a/build.gradle +++ b/build.gradle @@ -113,7 +113,7 @@ dependencies { implementation "edu.kit.datamanager:service-base:1.3.2" // elasticsearch (since service-base 1.1.0) - implementation "org.springframework.data:spring-data-elasticsearch:5.3.5" + implementation "org.springframework.data:spring-data-elasticsearch:5.4.0" // DOIP SDK implementation "net.dona.doip:doip-sdk:2.2.0" From f34941dd125ac3a2f61b441e7aab04faebedd339 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 04:09:37 +0000 Subject: [PATCH 150/181] Update plugin io.freefair.lombok to v8.11 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c839046a..265d22be 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' - id 'io.freefair.lombok' version '8.10.2' + id 'io.freefair.lombok' version '8.11' id 'io.freefair.maven-publish-java' version '8.10.2' id 'org.owasp.dependencycheck' version '11.1.0' id 'org.asciidoctor.jvm.convert' version '4.0.3' From 1e0faf80687eb7f9a805a1cfa2b1e1f1aa27973e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 06:33:09 +0000 Subject: [PATCH 151/181] Update plugin io.freefair.maven-publish-java to v8.11 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1b715781..4334d9d1 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.11' - id 'io.freefair.maven-publish-java' version '8.10.2' + id 'io.freefair.maven-publish-java' version '8.11' id 'org.owasp.dependencycheck' version '11.1.0' id 'org.asciidoctor.jvm.convert' version '4.0.3' id 'net.ltgt.errorprone' version '4.1.0' From cd2ebc941ff45f3e2532b349cb49b1094fb7f878 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 18 Nov 2024 11:51:19 +0100 Subject: [PATCH 152/181] Add some tests for better code coverage. --- .../util/DataResourceRecordUtil.java | 12 +- .../test/SchemaRegistryControllerTestV2.java | 34 +++ .../util/DataResourceRecordUtilTest.java | 269 ++++++++++++++++++ 3 files changed, 305 insertions(+), 10 deletions(-) create mode 100644 src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index a1264793..d218ebd3 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -615,22 +615,12 @@ public static DataResource getRecordByIdAndVersion(MetastoreConfiguration metast return findFirst.get(); } - public static ContentInformation getContentInformationByIdAndVersion(MetastoreConfiguration metastoreProperties, - String recordId) throws ResourceNotFoundException { - return getContentInformationByIdAndVersion(metastoreProperties, recordId, null); - } - public static ContentInformation getContentInformationByIdAndVersion(MetastoreConfiguration metastoreProperties, String recordId, Long version) throws ResourceNotFoundException { LOG.trace("Obtaining content information record with id {} and version {}.", recordId, version); return metastoreProperties.getContentInformationService().getContentInformation(recordId, null, version); } - public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, - String recordId) throws ResourceNotFoundException { - return getMetadataDocumentByIdAndVersion(metastoreProperties, recordId, null); - } - public static Path getMetadataDocumentByIdAndVersion(MetastoreConfiguration metastoreProperties, String recordId, Long version) throws ResourceNotFoundException { LOG.trace("Obtaining content information record with id {} and version {}.", recordId, version); @@ -1558,6 +1548,8 @@ public static boolean checkDocumentForChanges(ContentInformation info, Multipart LOG.error("Error reading current file!", ex); throw new BadArgumentException("Error reading schema document!"); } + } else { + throw new CustomInternalServerError("No content information provided!"); } return noChanges; } diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 86de349f..86ae4a8d 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -226,6 +226,40 @@ public void testCreateSchemaRecord() throws Exception { file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); } + @Test + public void testCreateSchemaRecordWithMinimalInput() throws Exception { + String id = "my_dc_minimal_record"; + DataResource record = new DataResource(); + record.setId(id); + // mandatory element title has to be set + setTitle(record, id); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", "application/xml", KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + + @Test + public void testCreateSchemaRecordWithMinimalInputAndNoContentType() throws Exception { + String id = "my_dc_minimal_record"; + DataResource record = new DataResource(); + record.setId(id); + // mandatory element title has to be set + setTitle(record, id); + ObjectMapper mapper = new ObjectMapper(); + + MockMultipartFile recordFile = new MockMultipartFile("record", "record.json", "application/json", mapper.writeValueAsString(record).getBytes()); + MockMultipartFile schemaFile = new MockMultipartFile("schema", "schema.xsd", null, KIT_SCHEMA.getBytes()); + + this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_SCHEMA_PATH). + file(recordFile). + file(schemaFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); + } + @Test public void testCreateSchemaRecordWithAlternateEndpoint() throws Exception { DataResource record = createDataResource4Schema("my_dc_alternate"); diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java new file mode 100644 index 00000000..36e59aa7 --- /dev/null +++ b/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java @@ -0,0 +1,269 @@ +/* + * Copyright 2020 hartmann-v. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 edu.kit.datamanager.metastore2.util; + +import edu.kit.datamanager.entities.Identifier; +import edu.kit.datamanager.exceptions.BadArgumentException; +import edu.kit.datamanager.exceptions.CustomInternalServerError; +import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; +import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; +import edu.kit.datamanager.metastore2.domain.MetadataSchemaRecord; +import edu.kit.datamanager.metastore2.domain.SchemaRecord; +import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; +import edu.kit.datamanager.repo.dao.IContentInformationDao; +import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.ContentInformation; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.RelatedIdentifier; +import edu.kit.datamanager.repo.domain.acl.AclEntry; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.restdocs.JUnitRestDocumentation; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.multipart.MultipartFile; + +/** + * + * @author hartmann-v + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@EntityScan("edu.kit.datamanager") +@EnableJpaRepositories("edu.kit.datamanager") +@ComponentScan({"edu.kit.datamanager"}) +@AutoConfigureMockMvc +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +@ActiveProfiles("test") +@TestPropertySource(properties = {"server.port=41439"}) +@TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:mem:db_schema_drru;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) +@TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/v2/drru/schema"}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class DataResourceRecordUtilTest { + + private static final String TEMP_DIR_4_ALL = "/tmp/metastore2/drru/"; + private static final String TEMP_DIR_4_SCHEMAS = TEMP_DIR_4_ALL + "schema/"; + private MockMvc mockMvc; + @Autowired + private WebApplicationContext context; + + @Autowired + private IDataResourceDao dataResourceDao; + @Autowired + private ISchemaRecordDao schemaRecordDao; + @Autowired + private IContentInformationDao contentInformationDao; + @Autowired + private IAllIdentifiersDao allIdentifiersDao; + + @Autowired + private MetastoreConfiguration schemaConfig; + @Rule + public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + public DataResourceRecordUtilTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + + System.out.println("------DataResourceRecordUtilTest--------------------"); + System.out.println("------" + this.schemaConfig); + System.out.println("------------------------------------------------------"); + contentInformationDao.deleteAll(); + dataResourceDao.deleteAll(); + schemaRecordDao.deleteAll(); + allIdentifiersDao.deleteAll(); + try { + try (Stream<Path> walk = Files.walk(Paths.get(URI.create("file://" + TEMP_DIR_4_SCHEMAS)))) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Paths.get(TEMP_DIR_4_SCHEMAS).toFile().mkdir(); + } catch (IOException ex) { + ex.printStackTrace(); + } + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(springSecurity()) + .apply(documentationConfiguration(this.restDocumentation)) + .build(); + SchemaRecord schemaRecord = new SchemaRecord(); + schemaRecord.setAlternateId("http://example.org/test1"); + schemaRecord.setDocumentHash("anyHash"); + schemaRecord.setSchemaId("test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "1"); + schemaRecord.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); + schemaRecord.setSchemaDocumentUri("anySchemaDocumentUri"); + schemaRecord.setVersion(1l); + schemaRecordDao.save(schemaRecord); + SchemaRecord schemaRecord2 = new SchemaRecord(); + schemaRecord2.setAlternateId("http://example.org/test2"); + schemaRecord2.setDocumentHash("anyHash"); + schemaRecord2.setSchemaId("test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "2"); + schemaRecord2.setType(MetadataSchemaRecord.SCHEMA_TYPE.JSON); + schemaRecord2.setSchemaDocumentUri("anySchemaDocumentUri"); + schemaRecord2.setVersion(2l); + schemaRecordDao.save(schemaRecord2); + } + + @After + public void tearDown() { + } + + /** + * Test of downloadResource method, of class GemmaMapping. + */ + @Test + public void testConstructor() { + System.out.println("testConstructor"); + assertNotNull(new DataResourceRecordUtil()); + } + + @Test + public void testAddProvenanceWithVersion1() throws URISyntaxException { + System.out.println("testAddProvenanceWithVersion1"); + DataResource factoryNewDataResource = DataResource.factoryNewDataResource(); + Set<RelatedIdentifier> relatedIdentifiers = factoryNewDataResource.getRelatedIdentifiers(); + factoryNewDataResource.setVersion("1"); + DataResourceRecordUtil.addProvenance(factoryNewDataResource); + assertEquals(relatedIdentifiers, factoryNewDataResource.getRelatedIdentifiers()); + } + + @Test + public void testMergeIdenticalAcls() throws URISyntaxException { + System.out.println("testMergeIdenticalAcls"); + Set<AclEntry> acls = new HashSet<>(); + Set<AclEntry> mergeAcl = DataResourceRecordUtil.mergeAcl(acls, acls); + assertEquals(acls, mergeAcl); + } + + @Test + public void testFixSchemaUrl() throws URISyntaxException, IOException { + System.out.println("testFixSchemaUrl"); + DataResourceRecordUtil.fixSchemaUrl((RelatedIdentifier) null); + RelatedIdentifier ri1 = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "1", null, null); + ri1.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + RelatedIdentifier ri2 = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "test", null, null); + ri2.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + DataResourceRecordUtil.fixSchemaUrl(ri2); + assertTrue("Found second version", ri2.getValue().endsWith("test2")); + DataResourceRecordUtil.fixSchemaUrl(ri1); + assertTrue("Found first version", ri1.getValue().endsWith("test1")); + } + + @Test(expected = CustomInternalServerError.class) + public void testFixSchemaUrlWrongFormat() throws URISyntaxException, IOException { + System.out.println("testFixSchemaUrl"); + DataResourceRecordUtil.fixSchemaUrl((RelatedIdentifier) null); + RelatedIdentifier ri1 = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "1" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "2", null, null); + ri1.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); + DataResourceRecordUtil.fixSchemaUrl(ri1); + // following line shouldn't be reached + assertFalse(true); + } + + @Test + public void testvalidateRelatedResources4MetadataDocuments() { + System.out.println("testvalidateRelatedResources4MetadataDocuments"); + try { + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(null); + assertTrue(true); + } catch (BadArgumentException bae) { + assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); + assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + } + try { + DataResource isMetadataFor = DataResource.factoryNewDataResource(); + isMetadataFor.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first", null, null)); + isMetadataFor.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(isMetadataFor); + assertTrue(false); + } catch (BadArgumentException bae) { + assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); + } + try { + DataResource hasMetadata = DataResource.factoryNewDataResource(); + hasMetadata.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first", null, null)); + hasMetadata.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "second", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasMetadata); + assertTrue(false); + } catch (BadArgumentException bae) { + assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + } + } + + @Test(expected = CustomInternalServerError.class) + public void testCheckDocumentForChangesWithNoContentInformation() { + boolean result = DataResourceRecordUtil.checkDocumentForChanges(null, null); + } + + @Test(expected = BadArgumentException.class) + public void testCheckDocumentForChangesWithInvalidContentInformation() { + ContentInformation ci = new ContentInformation(); + ci.setContentUri("file:///tmp/somethingTotallyStrange"); + MultipartFile mpf = new MockMultipartFile("hallo.txt", "noContent".getBytes()); + boolean result = DataResourceRecordUtil.checkDocumentForChanges(ci, mpf); + } +} From ff0b0a322f89b979bd87cdd87e590e4fa1e07c95 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:12:16 +0000 Subject: [PATCH 153/181] Update dependency org.javers:javers-core to v7.7.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4334d9d1..7e563925 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ group = 'edu.kit.datamanager' ext { // versions of dependencies springDocVersion = '2.6.0' - javersVersion = '7.6.3' + javersVersion = '7.7.0' keycloakVersion = '19.0.0' errorproneVersion = '2.35.1' // directory for generated code snippets during tests From 110b3f0351cf968883315d0540a495f96590e0b7 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 19 Nov 2024 13:06:32 +0100 Subject: [PATCH 154/181] Refactor code and add tests. --- .../metastore2/runner/Migration2V2Runner.java | 37 +++++--- .../runner/Migrate2DataciteTest.java | 91 +++++++++++++++---- 2 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java index bf560102..508d304a 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java @@ -83,13 +83,9 @@ public void saveSchema(String id, long version) { LOG.info("Migrate datacite for schema document with id: '{}' / version: '{}'", id, version); DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, id, version); DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); + // Remove type from first title with type 'OTHER' - for (Title title : recordByIdAndVersion.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER) { - title.setTitleType(null); - break; - } - } + removeTitleType(recordByIdAndVersion.getTitles()); // Move PID from alternateIdentifier to identifier movePidFromAlternateToPrimaryIdentifier(recordByIdAndVersion); // Set resource type to new definition of version 2 ('...'_Schema) @@ -140,12 +136,8 @@ public DataResource saveMetadata(String id, long version, String format) { DataResource currentDataResource = DataResourceRecordUtil.getRecordByIdAndVersion(metadataConfig, id, version); DataResource recordByIdAndVersion = DataResourceUtils.copyDataResource(currentDataResource); // Remove type from first title with type 'OTHER' - for (Title title : recordByIdAndVersion.getTitles()) { - if (title.getTitleType() == Title.TYPE.OTHER) { - title.setTitleType(null); - break; - } - } + removeTitleType(recordByIdAndVersion.getTitles()); + // Move PID from alternateIdentifier to identifier movePidFromAlternateToPrimaryIdentifier(recordByIdAndVersion); // Set resource type to new definition of version 2 ('...'_Metadata) @@ -184,21 +176,27 @@ public DataResource saveMetadata(String id, long version, String format) { return migratedDataResource; } + /** * Create a deep copy of a data resource instance. + * * @param dataResource Data resource. * @return Deep copy of data resource. */ public DataResource getCopyOfDataResource(DataResource dataResource) { DataResource copy = null; Optional<DataResource> origDataResource; + Objects.requireNonNull(dataResource); + Objects.requireNonNull(dataResource.getId()); origDataResource = dataResourceDao.findById(dataResource.getId()); - if (origDataResource.isPresent()) { + if (origDataResource.isPresent()) { copy = DataResourceUtils.copyDataResource(origDataResource.get()); + } else { + copy = DataResourceUtils.copyDataResource(dataResource); } return copy; } - + private void movePidFromAlternateToPrimaryIdentifier(DataResource dataResource) { Identifier pid = null; // Move PID from alternateIdentifier to identifier @@ -215,9 +213,18 @@ private void movePidFromAlternateToPrimaryIdentifier(DataResource dataResource) } } + protected void removeTitleType(Set<Title> titles) { + for (Title title : titles) { + if (title.getTitleType() == Title.TYPE.OTHER) { + title.setTitleType(null); + break; + } + } + } + /** * Set base URL for accessing documents and records. - * + * * @param baseUrl the baseUrl to set */ public void setBaseUrl(String baseUrl) { diff --git a/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java index cd4a43ba..6a6b4362 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/runner/Migrate2DataciteTest.java @@ -23,10 +23,14 @@ import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; import edu.kit.datamanager.repo.dao.IDataResourceDao; +import edu.kit.datamanager.repo.domain.DataResource; +import edu.kit.datamanager.repo.domain.Title; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.HashSet; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.FileUtils; @@ -80,7 +84,7 @@ TransactionalTestExecutionListener.class, WithSecurityContextTestExecutionListener.class}) @ActiveProfiles("test") -@TestPropertySource(properties = {"server.port=41417"}) +@TestPropertySource(properties = {"server.port=41437"}) @TestPropertySource(properties = {"spring.jpa.hibernate.ddl-auto=update"}) @TestPropertySource(properties = {"spring.datasource.url=jdbc:h2:file:/tmp/metastore2/migrationRunner/database/migrationDatabase;DB_CLOSE_DELAY=-1;MODE=LEGACY;NON_KEYWORDS=VALUE"}) @TestPropertySource(properties = {"metastore.schema.schemaFolder=file:///tmp/metastore2/migrationRunner/schema"}) @@ -98,23 +102,12 @@ public class Migrate2DataciteTest { @Autowired private WebApplicationContext context; @Autowired - Javers javers = null; - @Autowired - private ILinkedMetadataRecordDao metadataRecordDao; - @Autowired private IDataResourceDao dataResourceDao; @Autowired - private IDataRecordDao dataRecordDao; - @Autowired - private ISchemaRecordDao schemaRecordDao; - @Autowired - private IContentInformationDao contentInformationDao; - @Autowired - private IAllIdentifiersDao allIdentifiersDao; - @Autowired - private IUrl2PathDao url2PathDao; - @Autowired private MetastoreConfiguration metadataConfig; + @Autowired + private Migration2V2Runner migrate2V2Runner; + @Rule public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); @@ -143,7 +136,7 @@ public static void tearDownClass() { } catch (IOException ex) { Logger.getLogger(Migrate2DataciteTest.class.getName()).log(Level.SEVERE, null, ex); } - Logger.getLogger(Migrate2DataciteTest.class.getName()).log(Level.SEVERE, "Path should be deleted!"); + Logger.getLogger(Migrate2DataciteTest.class.getName()).log(Level.SEVERE, "Path should be deleted!"); } @Before @@ -169,13 +162,77 @@ public void setUp() { public void tearDown() { } - @Test public void testElasticRunnerMigration() throws Exception { eir.run("--migrate2DataCite"); Assert.assertTrue(true); } + /** + * Test for argument null. + */ + @Test + public void testcopyDataResource() { + Migration2V2Runner m2r = migrate2V2Runner; + DataResource invalidTestResource = DataResource.factoryNewDataResource("invalidId"); + DataResource result = m2r.getCopyOfDataResource(invalidTestResource); + Assert.assertEquals(invalidTestResource, result); + } + + /** + * Test for argument null. + */ + @Test(expected = NullPointerException.class) + public void testcopyDataResourceWithNullArgument() { + Migration2V2Runner m2r = migrate2V2Runner; + DataResource expected = null; + DataResource invalidTestResource = null; + DataResource result = m2r.getCopyOfDataResource(invalidTestResource); + Assert.assertEquals(expected, result); + } + + /** + * Test for argument null. + */ + @Test(expected = NullPointerException.class) + public void testcopyDataResourceWithIdSetToNull() { + Migration2V2Runner m2r = migrate2V2Runner; + DataResource expected = null; + DataResource invalidTestResource = DataResource.factoryNewDataResource(); + invalidTestResource.setId(null); + DataResource result = m2r.getCopyOfDataResource(invalidTestResource); + Assert.assertEquals(expected, result); + } + + /** + * Test for invalid data resources. + */ + @Test + public void testRemoveTitleType() { + Migration2V2Runner m2r = new Migration2V2Runner(); + Set<Title> titles = null; + Set<Title> expected = null; + titles = new HashSet<>(); + expected = new HashSet<>(); + expected.addAll(titles); + m2r.removeTitleType(titles); + Assert.assertEquals(expected, titles); + titles = new HashSet<>(); + titles.add(Title.factoryTitle("any", Title.TYPE.ALTERNATIVE_TITLE)); + expected = new HashSet<>(); + expected.addAll(titles); + m2r.removeTitleType(titles); + Assert.assertEquals(expected, titles); + titles = new HashSet<>(); + titles.add(Title.factoryTitle("any", Title.TYPE.ALTERNATIVE_TITLE)); + titles.add(Title.factoryTitle("title", Title.TYPE.OTHER)); + expected = new HashSet<>(); + expected.addAll(titles); + m2r.removeTitleType(titles); + Assert.assertNotEquals(expected, titles); + + } + public static synchronized boolean isInitialized() { boolean returnValue = alreadyInitialized; alreadyInitialized = Boolean.TRUE; From b8d02c08d186325487b222d526d6e303e0b67ed2 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 19 Nov 2024 13:08:53 +0100 Subject: [PATCH 155/181] Add parameter for defining prefix for indices used in elasticsearch. --- .../runner/ElasticIndexerRunner.java | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 872be2b1..946da115 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -83,6 +83,11 @@ public class ElasticIndexerRunner implements CommandLineRunner { */ @Parameter(names = {"--migrate2DataCite"}, description = "Migrate database from version 1.X to 2.X.") boolean doMigration2DataCite; + /** + * Start migration to version 2 + */ + @Parameter(names = {"--prefixIndices", "-p"}, description = "Prefix used for the indices inside elastic.") + String prefixIndices; /** * *************************************************************************** @@ -156,12 +161,22 @@ public class ElasticIndexerRunner implements CommandLineRunner { @Override @SuppressWarnings({"StringSplitter", "JavaUtilDate"}) public void run(String... args) throws Exception { + // Set defaults for cli arguments. + prefixIndices = "metastore-"; + updateDate = new Date(0); + indices = new HashSet<>(); + JCommander argueParser = JCommander.newBuilder() .addObject(this) .build(); try { LOG.trace("Parse arguments: '{}'", (Object)args); argueParser.parse(args); + LOG.trace("doMigration2DataCite: '{}'", doMigration2DataCite); + LOG.trace("PrefixIndices: '{}'", prefixIndices); + LOG.trace("Update index: '{}'", updateIndex); + LOG.trace("update date: '{}'", updateDate.toString()); + LOG.trace("Indices: '{}'", indices); LOG.trace("Find all schemas..."); List<SchemaRecord> findAllSchemas = schemaRecordDao.findAll(PageRequest.of(0, 1)).getContent(); if (!findAllSchemas.isEmpty()) { @@ -175,12 +190,6 @@ public void run(String... args) throws Exception { DataResourceRecordUtil.setBaseUrl(baseUrl); } if (updateIndex) { - if (updateDate == null) { - updateDate = new Date(0); - } - if (indices == null) { - indices = new HashSet<>(); - } updateElasticsearchIndex(); } if (doMigration2DataCite) { @@ -366,38 +375,25 @@ private void migrateAllSchemasToDataciteVersion2() { } /** - * Remove all indexed entries for given schema. (If search is enabled) + * Remove all indexed entries (indexed with V1) for given schema. (If search is enabled) + * + * example: POST /metastore-schemaid/_delete_by_query { "query": { "range": { + * "metadataRecord.schemaVersion": { "gte": 1 } } } } + * * - * example: - * POST /metastore-schemaid/_delete_by_query - * { - * "query": { - * "range": { - * "metadataRecord.identifier.id": { - * "gte": 1 - * } - * } - * } - * } - * - * * @param schemaId schema */ private void removeAllIndexedEntries(String schemaId) { // Delete all entries in elastic (if available) - LOG.trace("Remove all indexed entries..."); + LOG.trace("Remove all indexed entries for '{}'...", schemaId); if (searchConfiguration.isSearchEnabled()) { - String prefix4Indices = searchConfiguration.getIndex(); - Pattern pattern = Pattern.compile("(.*?)(\\*.*)"); - Matcher matcher = pattern.matcher(prefix4Indices); - if (matcher.find()) { - prefix4Indices = matcher.group(1); - } + String prefix4Indices = prefixIndices; LOG.trace(searchConfiguration.toString()); LOG.trace("Remove all entries for index: '{}'", prefix4Indices + schemaId); SimpleServiceClient client = SimpleServiceClient.create(searchConfiguration.getUrl() + "/" + prefix4Indices + schemaId + "/_delete_by_query"); - String query = "{ \"query\": { \"range\" : { \"metadataRecord.identifier.id\" : { \"gte\" : 1} } } }"; + String query = "{ \"query\": { \"range\" : { \"metadataRecord.schemaVersion\" : { \"gte\" : 1} } } }"; + LOG.trace("Query: '{}'", query); client.withContentType(MediaType.APPLICATION_JSON); try { String postResource = client.postResource(query, String.class); From fb30ece62e833cc68627f16be61cc9dc3c82ef78 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 19 Nov 2024 13:10:21 +0100 Subject: [PATCH 156/181] Restrict indexing of metadata documents to the current version only. --- .../messaging/MetadataResourceMessage.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java index 79ce4ecc..3ab6f14a 100644 --- a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java +++ b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java @@ -103,7 +103,7 @@ public static MetadataResourceMessage createMessage(MetadataRecord metadataRecor MetadataResourceMessage msg = new MetadataResourceMessage(); Map<String, String> properties = new HashMap<>(); if (metadataRecord != null) { - properties.put(RESOLVING_URL_PROPERTY, metadataRecord.getMetadataDocumentUri()); + properties.put(RESOLVING_URL_PROPERTY, removeFilterFromUri(metadataRecord.getMetadataDocumentUri())); properties.put(DOCUMENT_TYPE_PROPERTY, metadataRecord.getSchema().getIdentifier()); msg.setEntityId(metadataRecord.getId()); } @@ -161,9 +161,9 @@ public static MetadataResourceMessage createMessage(DataResource dataResource, A String metadataDocumentUri = DataResourceRecordUtil.getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()).toString(); String schemaDocumentUri = DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue(); String[] split = schemaDocumentUri.split(DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR, -1); - String schemaId = split[split.length - 1].split("\\?", -1)[0]; + String schemaId = removeFilterFromUri(split[split.length - 1]); - properties.put(RESOLVING_URL_PROPERTY, metadataDocumentUri); + properties.put(RESOLVING_URL_PROPERTY, removeFilterFromUri(metadataDocumentUri)); properties.put(DOCUMENT_TYPE_PROPERTY, schemaId); msg.setEntityId(dataResource.getId()); } @@ -179,7 +179,19 @@ public static MetadataResourceMessage createMessage(DataResource dataResource, A msg.setCurrentTimestamp(); return msg; } - + + /** + * Remove version and other stuff added to the URI. + * @param uri URI of the object. + * @return URI without additional parameter. + */ + public static String removeFilterFromUri(String uri) { + String strippedUri = null; + if (uri != null) { + strippedUri = uri.split("\\?", -1)[0]; + } + return strippedUri; + } @Override public String getEntityName() { return "metadata"; From c764e573c2a6594026903f035ca0a21df8461448 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:41:56 +0000 Subject: [PATCH 157/181] Update dependency com.google.errorprone:error_prone_core to v2.36.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4334d9d1..9995b29b 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ ext { springDocVersion = '2.6.0' javersVersion = '7.6.3' keycloakVersion = '19.0.0' - errorproneVersion = '2.35.1' + errorproneVersion = '2.36.0' // directory for generated code snippets during tests snippetsDir = file("build/generated-snippets") } From f8080ac233965c9a155d807477bcb97f081c2357 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Tue, 19 Nov 2024 16:00:08 +0100 Subject: [PATCH 158/181] Fix filtering for schema documents. --- .../util/DataResourceRecordUtil.java | 46 ++++++------ .../impl/SchemaRegistryControllerImplV2.java | 75 +++++++++---------- .../test/MetadataControllerFilterTest.java | 12 +++ .../test/MetadataControllerFilterTestV2.java | 25 +++++-- 4 files changed, 91 insertions(+), 67 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index d218ebd3..af5b141f 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -664,7 +664,6 @@ public static Specification<DataResource> findByAccessRights(Specification<DataR * @return Specification with schemaIds added. */ public static Specification<DataResource> findBySchemaId(Specification<DataResource> specification, List<String> schemaIds) { -// ToDo.... Specification<DataResource> specWithSchema = specification; if (schemaIds != null) { List<String> allSchemaIds = new ArrayList<>(); @@ -685,32 +684,37 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou public static final Specification<DataResource> findByMimetypes(List<String> mimeTypes) { // Search for both mimetypes (xml & json) ResourceType resourceType = null; + final int JSON = 1; // bit 0 + final int XML = 2; // bit 1 // + int searchFor = 0; // 1 - JSON, 2 - XML, 3 - both if (mimeTypes != null) { - int searchFor = 0; // 1 - JSON, 2 - XML, 3 - both for (String mimeType : mimeTypes) { if (mimeType.contains("json")) { - searchFor += 1; + searchFor |= JSON; } if (mimeType.contains("xml")) { - searchFor += 2; + searchFor |= XML; } } - resourceType = switch (searchFor) { - // 1 -> search for JSON only - case 1 -> - ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); - // 2 -> search for XML only - case 2 -> - ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); - // 3 -> Search for both mimetypes (xml & json) - case 3 -> - ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); - // 0 -> Unknown mimetype - default -> - ResourceType.createResourceType("unknown"); - }; - } + } else { + searchFor = JSON | XML; + } + resourceType = switch (searchFor) { + // 1 -> search for JSON only + case JSON -> + ResourceType.createResourceType(DataResourceRecordUtil.JSON_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + // 2 -> search for XML only + case XML -> + ResourceType.createResourceType(DataResourceRecordUtil.XML_SCHEMA_TYPE, ResourceType.TYPE_GENERAL.MODEL); + // 3 -> Search for both mimetypes (xml & json) + case JSON | XML -> + ResourceType.createResourceType(DataResourceRecordUtil.SCHEMA_SUFFIX, ResourceType.TYPE_GENERAL.MODEL); + // 0 -> Unknown mimetype + default -> + ResourceType.createResourceType("unknown"); + }; + return ResourceTypeSpec.toSpecification(resourceType); } @@ -1582,7 +1586,7 @@ private static DataResource fixRelatedSchemaIfNeeded(DataResource dataResource) * given or check type. * * @param metastoreProperties Configuration for accessing services - * @param dataResource Data resource record of the document. + * @param dataResource Data resource record of the document. * @param document Document of data resource. */ private static void validateMetadataDocument(MetastoreConfiguration metastoreProperties, @@ -1809,7 +1813,7 @@ private static List<String> getAllAuthorizationIdentities() { /** * Set base URL for accessing instances. - * + * * @param aBaseUrl the baseUrl to set */ public static void setBaseUrl(String aBaseUrl) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java index a5a821f6..e54f03cb 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java +++ b/src/main/java/edu/kit/datamanager/metastore2/web/impl/SchemaRegistryControllerImplV2.java @@ -18,8 +18,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; -import edu.kit.datamanager.entities.PERMISSION; -import edu.kit.datamanager.entities.RepoUserRole; import edu.kit.datamanager.exceptions.ResourceNotFoundException; import edu.kit.datamanager.metastore2.configuration.ApplicationProperties; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; @@ -29,13 +27,9 @@ import edu.kit.datamanager.metastore2.web.ISchemaRegistryControllerV2; import edu.kit.datamanager.repo.dao.IDataResourceDao; import edu.kit.datamanager.repo.dao.spec.dataresource.LastUpdateSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.PermissionSpecification; -import edu.kit.datamanager.repo.dao.spec.dataresource.ResourceTypeSpec; import edu.kit.datamanager.repo.dao.spec.dataresource.StateSpecification; import edu.kit.datamanager.repo.domain.ContentInformation; import edu.kit.datamanager.repo.domain.DataResource; -import edu.kit.datamanager.repo.domain.ResourceType; -import edu.kit.datamanager.util.AuthenticationHelper; import edu.kit.datamanager.util.ControllerUtils; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; @@ -73,7 +67,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import java.util.function.UnaryOperator; import java.util.logging.Level; @@ -85,13 +78,13 @@ @Tag(name = "Schema Registry") @Schema(description = "Schema Registry") public class SchemaRegistryControllerImplV2 implements ISchemaRegistryControllerV2 { - + private static final Logger LOG = LoggerFactory.getLogger(SchemaRegistryControllerImplV2.class); - + private final ApplicationProperties applicationProperties; - + private final MetastoreConfiguration schemaConfig; - + private final IDataResourceDao dataResourceDao; /** @@ -112,7 +105,7 @@ public SchemaRegistryControllerImplV2(ApplicationProperties applicationPropertie LOG.info("------{}", schemaConfig); LOG.info("------------------------------------------------------"); } - + @Override public ResponseEntity<DataResource> createRecord( @RequestPart(name = "record") final MultipartFile recordDocument, @@ -134,14 +127,14 @@ public ResponseEntity<DataResource> createRecord( java.util.logging.Logger.getLogger(SchemaRegistryControllerImplV2.class.getName()).log(Level.SEVERE, null, ex); } } - + LOG.trace("Schema record successfully persisted."); URI locationUri; locationUri = SchemaRegistryControllerImplV2.getSchemaDocumentUri(dataResourceRecord); LOG.trace("Set locationUri to '{}'", locationUri); return ResponseEntity.created(locationUri).eTag("\"" + etag + "\"").body(dataResourceRecord); } - + @Override public ResponseEntity<DataResource> getRecordById( @PathVariable(value = "schemaId") String schemaId, @@ -149,14 +142,14 @@ public ResponseEntity<DataResource> getRecordById( WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing getRecordById({}, {}).", schemaId, version); - + DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); String etag = schemaRecord.getEtag(); - + LOG.trace("Returning result."); return ResponseEntity.ok().eTag("\"" + etag + "\"").body(schemaRecord); } - + @Override public ResponseEntity<ContentInformation> getContentInformationById( @PathVariable(value = "schemaId") String schemaId, @@ -164,7 +157,7 @@ public ResponseEntity<ContentInformation> getContentInformationById( WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing getContentInformationById({}, {}).", schemaId, version); - + ContentInformation contentInformation = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaId, version); DataResource minimalDataResource = DataResource.factoryNewDataResource(contentInformation.getParentResource().getId()); URI locationUri; @@ -175,7 +168,7 @@ public ResponseEntity<ContentInformation> getContentInformationById( contentInformation.setVersioningService(null); return ResponseEntity.ok().body(contentInformation); } - + @Override public ModelAndView getLandingPageById(@PathVariable(value = "schemaId") String id, @RequestParam(value = "version", required = false) Long version, @@ -189,12 +182,12 @@ public ModelAndView getLandingPageById(@PathVariable(value = "schemaId") String versionString = version.toString(); } redirectUrl = "redirect:" + redirectUrl.replace(MetadataControllerImpl.PLACEHOLDER_VERSION, versionString); - + LOG.trace("Redirect to '{}'", redirectUrl); - + return new ModelAndView(redirectUrl); } - + @Override public ResponseEntity getSchemaDocumentById( @PathVariable(value = "schemaId") String schemaId, @@ -202,7 +195,7 @@ public ResponseEntity getSchemaDocumentById( WebRequest wr, HttpServletResponse hsr) { LOG.trace("Performing getSchemaDocumentById({}, {}).", schemaId, version); - + DataResource schemaRecord = DataResourceRecordUtil.getRecordByIdAndVersion(schemaConfig, schemaId, version); ContentInformation contentInfo = DataResourceRecordUtil.getContentInformationByIdAndVersion(schemaConfig, schemaRecord.getId(), Long.valueOf(schemaRecord.getVersion())); MediaType contentType = MediaType.valueOf(contentInfo.getMediaType()); @@ -212,14 +205,14 @@ public ResponseEntity getSchemaDocumentById( LOG.trace("Schema document at path {} either does not exist or is no file or is not readable. Returning HTTP NOT_FOUND.", schemaDocumentPath); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Schema document on server either does not exist or is no file or is not readable."); } - + return ResponseEntity. ok(). contentType(contentType). header(HttpHeaders.CONTENT_LENGTH, String.valueOf(schemaDocumentPath.toFile().length())). body(new FileSystemResource(schemaDocumentPath.toFile())); } - + public ResponseEntity<List<DataResource>> getAllVersions( String id, Pageable pgbl @@ -241,12 +234,12 @@ public ResponseEntity<List<DataResource>> getAllVersions( } catch (ResourceNotFoundException rnfe) { LOG.info("Schema ID '{}' is unkown. Return empty list...", id); } - + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), totalNoOfElements); - + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); } - + @Override public ResponseEntity validate(@PathVariable(value = "schemaId") String schemaId, @RequestParam(value = "version", required = false) Long version, @@ -258,7 +251,7 @@ public ResponseEntity validate(@PathVariable(value = "schemaId") String schemaId LOG.trace("Metadata document validation succeeded. Returning HTTP NOT_CONTENT."); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } - + @Override public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "schemaId", required = false) String schemaId, @RequestParam(value = "mimeType", required = false) List<String> mimeTypes, @@ -285,7 +278,7 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche DataResource.State[] states = {DataResource.State.FIXED, DataResource.State.VOLATILE}; List<DataResource.State> stateList = Arrays.asList(states); spec = spec.and(StateSpecification.toSpecification(stateList)); - + LOG.debug("Performing query for records."); Page<DataResource> records = DataResourceRecordUtil.queryDataResources(spec, pgbl); List<DataResource> recordList = records.getContent(); @@ -295,12 +288,12 @@ public ResponseEntity<List<DataResource>> getRecords(@RequestParam(value = "sche LOG.trace("---> " + item.toString()); } } - + String contentRange = ControllerUtils.getContentRangeHeader(pgbl.getPageNumber(), pgbl.getPageSize(), records.getTotalElements()); - + return ResponseEntity.status(HttpStatus.OK).header("Content-Range", contentRange).body(recordList); } - + @Override public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final String schemaId, @RequestPart(name = "record", required = false) MultipartFile schemaRecord, @@ -311,7 +304,7 @@ public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, response)).toString(); String eTag = ControllerUtils.getEtagFromHeader(request); DataResource updatedSchemaRecord = DataResourceRecordUtil.updateDataResource4SchemaDocument(schemaConfig, schemaId, eTag, schemaRecord, document, getById); - + LOG.trace("DataResource record successfully persisted. Updating document URI and returning result."); String etag = updatedSchemaRecord.getEtag(); @@ -320,7 +313,7 @@ public ResponseEntity<DataResource> updateRecord(@PathVariable("schemaId") final LOG.trace("Set locationUri to '{}'", locationUri); return ResponseEntity.ok().location(locationUri).eTag("\"" + etag + "\"").body(updatedSchemaRecord); } - + @Override public ResponseEntity deleteRecord(@PathVariable("schemaId") final String schemaId, WebRequest request, @@ -329,25 +322,25 @@ public ResponseEntity deleteRecord(@PathVariable("schemaId") final String schema UnaryOperator<String> getById; getById = t -> WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).getRecordById(t, null, request, hsr)).toString(); String eTag = ControllerUtils.getEtagFromHeader(request); - + MetadataSchemaRecordUtil.deleteMetadataSchemaRecord(schemaConfig, schemaId, eTag, getById); - + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } - + @Override public void contribute(Info.Builder builder) { LOG.trace("Check for SchemaRepo actuator information..."); - + URL basePath = schemaConfig.getBasepath(); Map<String, String> details = ActuatorUtil.testDirectory(basePath); - + if (!details.isEmpty()) { details.put("No of schema documents", Long.toString(DataResourceRecordUtil.getNoOfSchemaDocuments())); builder.withDetail("schemaRepo", details); } } - + /** * Get URI for accessing schema document via schemaId and version. * diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java index 252ac19b..87578cd4 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTest.java @@ -164,6 +164,18 @@ public void setUp() throws Exception { prepareRepo(); } + @Test + public void testFindAllSchemaRecords() throws Exception { + ObjectMapper map = new ObjectMapper(); + MvcResult res = this.mockMvc.perform(get("/api/v1/schemas/") + .header("Accept", MetadataSchemaRecord.METADATA_SCHEMA_RECORD_MEDIA_TYPE)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + MetadataSchemaRecord[] result = map.readValue(res.getResponse().getContentAsString(), MetadataSchemaRecord[].class); + Assert.assertEquals("No of schema records:", MAX_NO_OF_SCHEMAS * 2, result.length); + } + @Test public void testFindSchemaRecordsBySchemaId() throws Exception { ObjectMapper map = new ObjectMapper(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java index 6ea3d6fc..ab01ccc6 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java @@ -8,10 +8,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import edu.kit.datamanager.entities.Identifier; import edu.kit.datamanager.entities.PERMISSION; -import edu.kit.datamanager.entities.repo.RelatedIdentifier; import edu.kit.datamanager.metastore2.configuration.MetastoreConfiguration; import edu.kit.datamanager.metastore2.dao.ISchemaRecordDao; -import edu.kit.datamanager.metastore2.domain.ResourceIdentifier; import edu.kit.datamanager.metastore2.util.DataResourceRecordUtil; import edu.kit.datamanager.repo.dao.IAllIdentifiersDao; import edu.kit.datamanager.repo.dao.IContentInformationDao; @@ -141,7 +139,7 @@ public class MetadataControllerFilterTestV2 { private static final String XML_SCHEMA_ID = "xml_schema_"; private static final String RELATED_RESOURCE = "resource_"; private static final String INVALID_MIMETYPE = "application/invalid"; - + private static final String API_BASE_PATH = "/api/v2"; private static final String ALTERNATE_API_SCHEMA_PATH = API_BASE_PATH + "/schemas"; private static final String API_SCHEMA_PATH = ALTERNATE_API_SCHEMA_PATH + "/"; @@ -191,6 +189,23 @@ public void testFindSchemaRecordsBySchemaId() throws Exception { } } + @Test + public void testFindAllSchemaRecords() throws Exception { + ObjectMapper map = new ObjectMapper(); + MvcResult res = this.mockMvc.perform(get(API_SCHEMA_PATH) + .header("Accept", DataResourceRecordUtil.DATA_RESOURCE_MEDIA_TYPE)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + DataResource[] result = map.readValue(res.getResponse().getContentAsString(), DataResource[].class); + + Assert.assertEquals("No of schema records:", MAX_NO_OF_SCHEMAS * 2, result.length); + for (DataResource schemaRecord : result) { + Assert.assertTrue("Resource type should end with '_Schema'", + schemaRecord.getResourceType().getValue().endsWith(DataResourceRecordUtil.SCHEMA_SUFFIX)); + } + } + @Test public void testFindRecordsBySchemaId() throws Exception { ObjectMapper map = new ObjectMapper(); @@ -534,8 +549,8 @@ public void ingestMetadataDocument(String schemaId, String resource) throws Exce record.getRelatedIdentifiers().add(relatedIdentifier); record.getTitles().add(Title.factoryTitle("Document for schemaID: " + schemaId)); Set<AclEntry> aclEntries = new HashSet<>(); - aclEntries.add(new AclEntry("SELF",PERMISSION.READ)); - aclEntries.add(new AclEntry("test2",PERMISSION.ADMINISTRATE)); + aclEntries.add(new AclEntry("SELF", PERMISSION.READ)); + aclEntries.add(new AclEntry("test2", PERMISSION.ADMINISTRATE)); record.setAcls(aclEntries); ObjectMapper mapper = new ObjectMapper(); From c69e20c711fcdc89b54bc4d35525c50da89f588f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:16:17 +0000 Subject: [PATCH 159/181] Update dependency org.springframework.restdocs:spring-restdocs-mockmvc to v3.0.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4334d9d1..5a320b80 100644 --- a/build.gradle +++ b/build.gradle @@ -122,7 +122,7 @@ dependencies { // Additional libraries for tests testImplementation "com.google.guava:guava:33.3.1-jre" - testImplementation "org.springframework.restdocs:spring-restdocs-mockmvc:3.0.2" + testImplementation "org.springframework.restdocs:spring-restdocs-mockmvc:3.0.3" testImplementation "org.springframework.boot:spring-boot-starter-test" testImplementation "org.springframework:spring-test" testImplementation "org.springframework.security:spring-security-test" From 7d829f1d4d59388f90cd4616ab381cab84862ab4 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 20 Nov 2024 08:41:40 +0100 Subject: [PATCH 160/181] Allow multiple data resources as related identifier. --- .../util/DataResourceRecordUtil.java | 48 ++++++------- .../util/DataResourceRecordUtilTest.java | 71 ++++++++++++++++--- 2 files changed, 84 insertions(+), 35 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index af5b141f..1f3006e9 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -683,9 +683,9 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou public static final Specification<DataResource> findByMimetypes(List<String> mimeTypes) { // Search for both mimetypes (xml & json) - ResourceType resourceType = null; + ResourceType resourceType; final int JSON = 1; // bit 0 - final int XML = 2; // bit 1 + final int XML = 2; // bit 1 // int searchFor = 0; // 1 - JSON, 2 - XML, 3 - both if (mimeTypes != null) { @@ -936,42 +936,38 @@ public static void validateRelatedResources4MetadataDocuments(DataResource dataR if (dataResource != null) { Set<RelatedIdentifier> relatedResources = dataResource.getRelatedIdentifiers(); - // Check if related resource already exists (only one related resource of type isMetadataFor allowed) + // Check if related resource already exists (only one related resource of type hasMetadata is allowed) for (RelatedIdentifier item : relatedResources) { - switch (item.getRelationType()) { - case IS_METADATA_FOR: - noOfRelatedData++; - break; - case HAS_METADATA: - noOfRelatedSchemas++; - break; - default: + if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { + noOfRelatedSchemas++; + } + if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { + noOfRelatedData++; } } } checkNoOfRelatedIdentifiers(noOfRelatedData, noOfRelatedSchemas); } - + /** Validate related identifiers. + * There has to be exactly one schema (hasMetadata) + * and at *least* one related data resource. + * @param noOfRelatedData No of related data resources. + * @param noOfRelatedSchemas No of related schemas. + */ private static void checkNoOfRelatedIdentifiers(int noOfRelatedData, int noOfRelatedSchemas) { - String message; - message = "Invalid related resources! Expected '1' related resource found '%d'. Expected '1' related schema found '%d'!"; - - if (noOfRelatedData != 1 || noOfRelatedSchemas != 1) { + if ((noOfRelatedSchemas != 1) || (noOfRelatedData == 0)) { String errorMessage = ""; - if (noOfRelatedData == 0) { - errorMessage = "Mandatory attribute relatedIdentifier of type 'isMetadataFor' was not found in record. \n"; - } - if (noOfRelatedData > 1) { - errorMessage = "Mandatory attribute relatedIdentifier of type 'isMetadataFor' was provided more than once in record. \n"; - } if (noOfRelatedSchemas == 0) { - errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'hasMetadata' was not found in record. \n"; - } + errorMessage = "Mandatory attribute relatedIdentifier of type 'hasMetadata' was not found in record. \n"; + } if (noOfRelatedSchemas > 1) { - errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'hasMetadata' was provided more than once in record. \n"; + errorMessage = "Mandatory attribute relatedIdentifier of type 'hasMetadata' was provided more than once in record. \n"; + } + if (noOfRelatedData == 0) { + errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'isMetadataFor' was not found in record. \n"; } errorMessage = errorMessage + "Returning HTTP BAD_REQUEST."; - LOG.error(message); + LOG.error(errorMessage); throw new BadArgumentException(errorMessage); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java index 36e59aa7..a7b61725 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java @@ -229,28 +229,81 @@ public void testvalidateRelatedResources4MetadataDocuments() { System.out.println("testvalidateRelatedResources4MetadataDocuments"); try { DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(null); - assertTrue(true); + assertTrue(false); + } catch (BadArgumentException bae) { + assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); + assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + } + try { + DataResource hasNoRelatedIdentifier = DataResource.factoryNewDataResource(); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasNoRelatedIdentifier); + assertTrue(false); } catch (BadArgumentException bae) { assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); } try { - DataResource isMetadataFor = DataResource.factoryNewDataResource(); - isMetadataFor.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first", null, null)); - isMetadataFor.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second", null, null)); - DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(isMetadataFor); + DataResource hasNeitherSchemaNorDataResource = DataResource.factoryNewDataResource(); + hasNeitherSchemaNorDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_CITED_BY, "citation", null, null)); + hasNeitherSchemaNorDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DOCUMENTED_BY, "documentation", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasNeitherSchemaNorDataResource); + assertTrue(false); + } catch (BadArgumentException bae) { + assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); + assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + } + try { + DataResource hasTwoDataResourcesButNoSchema = DataResource.factoryNewDataResource(); + hasTwoDataResourcesButNoSchema.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first data", null, null)); + hasTwoDataResourcesButNoSchema.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second data", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasTwoDataResourcesButNoSchema); + assertTrue(false); + } catch (BadArgumentException bae) { + assertFalse("Multiple 'isMetadataFor' should be allowed!", bae.getMessage().contains("isMetadataFor")); + assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + } + try { + DataResource hasTwoSchemasAndNoDataResource = DataResource.factoryNewDataResource(); + hasTwoSchemasAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); + hasTwoSchemasAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "second schema", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasTwoSchemasAndNoDataResource); assertTrue(false); } catch (BadArgumentException bae) { + assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); } try { - DataResource hasMetadata = DataResource.factoryNewDataResource(); - hasMetadata.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first", null, null)); - hasMetadata.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "second", null, null)); - DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasMetadata); + DataResource hasOneSchemaAndNoDataResource = DataResource.factoryNewDataResource(); + hasOneSchemaAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasOneSchemaAndNoDataResource); + assertTrue(false); + } catch (BadArgumentException bae) { + assertFalse("Error should not contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); + } + try { + DataResource hasSchemaAndTwoDataResources = DataResource.factoryNewDataResource(); + hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); + hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first data", null, null)); + hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second data", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasSchemaAndTwoDataResources); + assertTrue(true); + } catch (BadArgumentException bae) { + assertFalse("Error should not contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + assertFalse("Multiple 'isMetadataFor' should be allowed!", bae.getMessage().contains("isMetadataFor")); + assertTrue(false); + } + try { + DataResource hasTwoSchemasAndTwoDataResources = DataResource.factoryNewDataResource(); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "second schema", null, null)); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first data", null, null)); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second data", null, null)); + DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasTwoSchemasAndTwoDataResources); assertTrue(false); } catch (BadArgumentException bae) { assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + assertFalse("Multiple 'isMetadataFor' should be allowed!", bae.getMessage().contains("isMetadataFor")); } } From b217e7f8dd51b4c90a95b18925901a92cb2241e1 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 20 Nov 2024 13:01:16 +0100 Subject: [PATCH 161/181] Add constants for the essential relation types. Switch relation type for identifier pointing to the previous version from 'IS_DERIVED_FROM' to 'IS_NEW_VERSION_OF'. --- .../runner/ElasticIndexerRunner.java | 2 +- .../metastore2/runner/Migration2V2Runner.java | 8 +-- .../util/DataResourceRecordUtil.java | 28 +++++---- .../metastore2/util/MetadataRecordUtil.java | 6 +- .../metastore2/test/CreateSchemaUtil.java | 4 +- .../test/MetadataControllerFilterTestV2.java | 20 +++--- .../test/MetadataControllerTestV2.java | 60 +++++++++--------- ...rollerTestWithAuthenticationEnabledV2.java | 4 +- .../test/SchemaRegistryControllerTestV2.java | 20 +++--- .../util/DataResourceRecordUtilTest.java | 62 +++++++++---------- .../util/MetadataRecordUtilTest.java | 4 +- 11 files changed, 111 insertions(+), 107 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java index 946da115..d4c7338c 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/ElasticIndexerRunner.java @@ -451,7 +451,7 @@ private void migrateMetadataDocumentsToDataciteVersion2(DataResource metadataDoc // Get resource type of schema.... String format = null; - RelatedIdentifier identifier = DataResourceRecordUtil.getRelatedIdentifier(copy, RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM); + RelatedIdentifier identifier = DataResourceRecordUtil.getRelatedIdentifier(copy, DataResourceRecordUtil.RELATED_NEW_VERSION_OF); if (identifier != null) { String schemaUrl = identifier.getValue(); Optional<Url2Path> findByUrl = url2PathDao.findByUrl(schemaUrl); diff --git a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java index 508d304a..366d3a69 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java +++ b/src/main/java/edu/kit/datamanager/metastore2/runner/Migration2V2Runner.java @@ -95,13 +95,13 @@ public void saveSchema(String id, long version) { // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { - item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + item.setRelationType(DataResourceRecordUtil.RELATED_SCHEMA_TYPE); } } // Add provenance if (version > 1) { String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(SchemaRegistryControllerImplV2.class).getSchemaDocumentById(id, version - 1l, null, null)).toString(); - RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); + RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_NEW_VERSION_OF, schemaUrl, null, null); recordByIdAndVersion.getRelatedIdentifiers().add(provenance); LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); } else { @@ -147,7 +147,7 @@ public DataResource saveMetadata(String id, long version, String format) { // Migrate relation type from 'isDerivedFrom' to 'hasMetadata' for (RelatedIdentifier item : recordByIdAndVersion.getRelatedIdentifiers()) { if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { - item.setRelationType(RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + item.setRelationType(DataResourceRecordUtil.RELATED_SCHEMA_TYPE); String replaceFirst = item.getValue().replaceFirst("/api/v1/schemas/", "/api/v2/schemas/"); item.setValue(replaceFirst); } @@ -155,7 +155,7 @@ public DataResource saveMetadata(String id, long version, String format) { // Add provenance if (version > 1) { String schemaUrl = baseUrl + WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(MetadataControllerImplV2.class).getMetadataDocumentById(id, version - 1l, null, null)).toString(); - RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, schemaUrl, null, null); + RelatedIdentifier provenance = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_NEW_VERSION_OF, schemaUrl, null, null); recordByIdAndVersion.getRelatedIdentifiers().add(provenance); LOG.trace("Add provenance to datacite record: '{}'", schemaUrl); } else { diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java index 1f3006e9..559e30e8 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtil.java @@ -82,6 +82,10 @@ public class DataResourceRecordUtil { public static final String RESOURCE_TYPE = "application/vnd.datacite.org+json"; + + public static final RelatedIdentifier.RELATION_TYPES RELATED_DATA_RESOURCE_TYPE = RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR; + public static final RelatedIdentifier.RELATION_TYPES RELATED_SCHEMA_TYPE = RelatedIdentifier.RELATION_TYPES.HAS_METADATA; + public static final RelatedIdentifier.RELATION_TYPES RELATED_NEW_VERSION_OF = RelatedIdentifier.RELATION_TYPES.IS_NEW_VERSION_OF; /** * Mediatype for fetching a DataResource. */ @@ -359,7 +363,7 @@ public static void replaceIsDerivedFrom(DataResource newDataResource) { toUri(). toString(); for (RelatedIdentifier item : newDataResource.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM)) { + if (item.getRelationType().equals(DataResourceRecordUtil.RELATED_NEW_VERSION_OF)) { String oldUrl = item.getValue(); item.setValue(urlToPredecessor); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); @@ -368,7 +372,7 @@ public static void replaceIsDerivedFrom(DataResource newDataResource) { } } if (!foundOldIdentifier) { - RelatedIdentifier newRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, urlToPredecessor, null, null); + RelatedIdentifier newRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_NEW_VERSION_OF, urlToPredecessor, null, null); newRelatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); newDataResource.getRelatedIdentifiers().add(newRelatedIdentifier); } @@ -675,7 +679,7 @@ public static Specification<DataResource> findBySchemaId(Specification<DataResou } } if (!allSchemaIds.isEmpty()) { - specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, allSchemaIds.toArray(String[]::new))); + specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, allSchemaIds.toArray(String[]::new))); } } return specWithSchema; @@ -728,7 +732,7 @@ public static final Specification<DataResource> findByMimetypes(List<String> mim public static Specification<DataResource> findByRelatedId(Specification<DataResource> specification, List<String> relatedIds) { Specification<DataResource> specWithSchema = specification; if ((relatedIds != null) && !relatedIds.isEmpty()) { - specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, relatedIds.toArray(String[]::new))); + specWithSchema = specWithSchema.and(RelatedIdentifierSpec.toSpecification(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, relatedIds.toArray(String[]::new))); } return specWithSchema; } @@ -938,10 +942,10 @@ public static void validateRelatedResources4MetadataDocuments(DataResource dataR // Check if related resource already exists (only one related resource of type hasMetadata is allowed) for (RelatedIdentifier item : relatedResources) { - if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.HAS_METADATA) { + if (item.getRelationType() == DataResourceRecordUtil.RELATED_SCHEMA_TYPE) { noOfRelatedSchemas++; } - if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR) { + if (item.getRelationType() == DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE) { noOfRelatedData++; } } @@ -958,13 +962,13 @@ private static void checkNoOfRelatedIdentifiers(int noOfRelatedData, int noOfRel if ((noOfRelatedSchemas != 1) || (noOfRelatedData == 0)) { String errorMessage = ""; if (noOfRelatedSchemas == 0) { - errorMessage = "Mandatory attribute relatedIdentifier of type 'hasMetadata' was not found in record. \n"; + errorMessage = "Mandatory attribute relatedIdentifier of type '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "' was not found in record. \n"; } if (noOfRelatedSchemas > 1) { - errorMessage = "Mandatory attribute relatedIdentifier of type 'hasMetadata' was provided more than once in record. \n"; + errorMessage = "Mandatory attribute relatedIdentifier of type '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "' was provided more than once in record. \n"; } if (noOfRelatedData == 0) { - errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type 'isMetadataFor' was not found in record. \n"; + errorMessage = errorMessage + "Mandatory attribute relatedIdentifier of type '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "' was not found in record. \n"; } errorMessage = errorMessage + "Returning HTTP BAD_REQUEST."; LOG.error(errorMessage); @@ -980,7 +984,7 @@ private static void checkNoOfRelatedIdentifiers(int noOfRelatedData, int noOfRel */ public static RelatedIdentifier getSchemaIdentifier(DataResource dataResourceRecord) { LOG.trace("Get schema identifier for '{}'.", dataResourceRecord.getId()); - return getRelatedIdentifier(dataResourceRecord, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + return getRelatedIdentifier(dataResourceRecord, DataResourceRecordUtil.RELATED_SCHEMA_TYPE); } /** @@ -996,7 +1000,7 @@ public static RelatedIdentifier getRelatedIdentifier(DataResource dataResourceRe Set<RelatedIdentifier> relatedResources = dataResourceRecord.getRelatedIdentifiers(); - // Check if related resource already exists (only one related resource of type isMetadataFor allowed) + // Check if related resource already exists (only one related resource of type DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE allowed) for (RelatedIdentifier item : relatedResources) { if (item.getRelationType().equals(relationType)) { relatedIdentifier = item; @@ -1736,7 +1740,7 @@ public static final void setSchemaAndVersion(MetadataRecord metadataRecord, Data } public static final void setRelatedResource(MetadataRecord metadataRecord, DataResource dataResource) { - RelatedIdentifier relatedId = getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedId = getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); if (relatedId != null) { ResourceIdentifier resourceIdentifier = ResourceIdentifier.factoryInternalResourceIdentifier(relatedId.getValue()); if (relatedId.getIdentifierType() != null) { diff --git a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java index a0a05191..53beaf7b 100644 --- a/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java +++ b/src/main/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtil.java @@ -454,9 +454,9 @@ public static MetadataSchemaRecord getInternalSchemaRecord(MetastoreConfiguratio * @param metadataRecord record holding resource information. */ private static void updateRelatedIdentifierForResource(DataResource dataResource, MetadataRecord metadataRecord) { - RelatedIdentifier schemaRelatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier schemaRelatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); if (schemaRelatedIdentifier == null) { - schemaRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, null, null, null); + schemaRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, null, null, null); dataResource.getRelatedIdentifiers().add(schemaRelatedIdentifier); } ResourceIdentifier schemaIdentifier = metadataRecord.getRelatedResource(); @@ -475,7 +475,7 @@ private static void updateRelatedIdentifierForResource(DataResource dataResource private static void updateRelatedIdentifierForSchema(DataResource dataResource, MetadataRecord metadataRecord) { RelatedIdentifier schemaRelatedIdentifier = DataResourceRecordUtil.getSchemaIdentifier(dataResource); if (schemaRelatedIdentifier == null) { - schemaRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, null, null, null); + schemaRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, null, null, null); dataResource.getRelatedIdentifiers().add(schemaRelatedIdentifier); } ResourceIdentifier schemaIdentifier = MetadataSchemaRecordUtil.getSchemaIdentifier(schemaConfig, metadataRecord); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java index ec19486a..23456317 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/CreateSchemaUtil.java @@ -527,7 +527,7 @@ public static MvcResult ingestOrUpdateXmlMetadataDocumentV2(MockMvc mockMvc, Str if (versionAsString != null) { record.setVersion(versionAsString); } - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); relatedIdentifier.setValue("any"); relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); record.getAcls().add(new AclEntry(AuthenticationHelper.ANONYMOUS_USER_PRINCIPAL, PERMISSION.READ)); @@ -560,7 +560,7 @@ public static MvcResult ingestOrUpdateXmlMetadataDocumentV2(MockMvc mockMvc, Str String etag = result.getResponse().getHeader("ETag"); String body = result.getResponse().getContentAsString(); record = mapper.readValue(body, DataResource.class); - relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_SCHEMA_TYPE); if ((schemaId != null) && schemaId.startsWith("http")) { relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } else { diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java index ab01ccc6..e79f2ddc 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerFilterTestV2.java @@ -382,7 +382,7 @@ public void testFindSchemaRecordsByMultipleMimeTypesIncludingInvalidMimeType() t @Test public void testFindRecordsByResourceId() throws Exception { for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { - edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE + i, null, null); + edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, RELATED_RESOURCE + i, null, null); relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) .param("resourceId", relatedIdentifier.getValue())) @@ -394,9 +394,9 @@ public void testFindRecordsByResourceId() throws Exception { Assert.assertEquals((MAX_NO_OF_SCHEMAS - i + 1) * 2, result.length); for (DataResource item : result) { - Assert.assertEquals(relatedIdentifier.getValue(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue()); - Assert.assertEquals(relatedIdentifier.getIdentifierType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getIdentifierType()); - Assert.assertEquals(relatedIdentifier.getRelationType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getRelationType()); + Assert.assertEquals(relatedIdentifier.getValue(), DataResourceRecordUtil.getRelatedIdentifier(item, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).getValue()); + Assert.assertEquals(relatedIdentifier.getIdentifierType(), DataResourceRecordUtil.getRelatedIdentifier(item, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).getIdentifierType()); + Assert.assertEquals(relatedIdentifier.getRelationType(), DataResourceRecordUtil.getRelatedIdentifier(item, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).getRelationType()); } } } @@ -467,7 +467,7 @@ public void testFindRecordsByInvalidResourceId() throws Exception { @Test public void testFindRecordsByMultipleResourceIdsIncludingInvalidResourceId() throws Exception { for (int i = 1; i <= MAX_NO_OF_SCHEMAS; i++) { - edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE + i, null, null); + edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, RELATED_RESOURCE + i, null, null); relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); MvcResult res = this.mockMvc.perform(get(API_METADATA_PATH) .param("resourceId", relatedIdentifier.getValue()) @@ -480,9 +480,9 @@ public void testFindRecordsByMultipleResourceIdsIncludingInvalidResourceId() thr Assert.assertEquals((MAX_NO_OF_SCHEMAS - i + 1) * 2, result.length); for (DataResource item : result) { - Assert.assertEquals(relatedIdentifier.getValue(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getValue()); - Assert.assertEquals(relatedIdentifier.getIdentifierType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getIdentifierType()); - Assert.assertEquals(relatedIdentifier.getRelationType(), DataResourceRecordUtil.getRelatedIdentifier(item, edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).getRelationType()); + Assert.assertEquals(relatedIdentifier.getValue(), DataResourceRecordUtil.getRelatedIdentifier(item, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).getValue()); + Assert.assertEquals(relatedIdentifier.getIdentifierType(), DataResourceRecordUtil.getRelatedIdentifier(item, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).getIdentifierType()); + Assert.assertEquals(relatedIdentifier.getRelationType(), DataResourceRecordUtil.getRelatedIdentifier(item, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).getRelationType()); } } } @@ -541,9 +541,9 @@ public void registerSchemaDocument(String schemaType, String schemaId) throws Ex */ public void ingestMetadataDocument(String schemaId, String resource) throws Exception { DataResource record = new DataResource(); - edu.kit.datamanager.repo.domain.RelatedIdentifier schemaIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.HAS_METADATA, schemaId, null, null); + edu.kit.datamanager.repo.domain.RelatedIdentifier schemaIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, schemaId, null, null); schemaIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); - edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(edu.kit.datamanager.repo.domain.RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, resource, null, null); + edu.kit.datamanager.repo.domain.RelatedIdentifier relatedIdentifier = edu.kit.datamanager.repo.domain.RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, resource, null, null); relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); record.getRelatedIdentifiers().add(schemaIdentifier); record.getRelatedIdentifiers().add(relatedIdentifier); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java index 99743c71..be49ff29 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestV2.java @@ -343,7 +343,7 @@ public void testCreateRecordWithRelatedResourceOfTypeUrl() throws Exception { String id = "testCreateRecordWithRelatedResourceOfTypeUrl"; String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4XmlDocument(id, schemaId); - RelatedIdentifier relatedResourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedResourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); relatedResourceIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); ObjectMapper mapper = new ObjectMapper(); @@ -359,8 +359,8 @@ public void testCreateRecordWithRelatedResourceOfTypeUrl() throws Exception { andReturn(); DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); - RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); - RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(result, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); + RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(result, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals("Value of related resource should be unchanged!", relatedIdentifier1.getValue(), relatedIdentifier2.getValue()); @@ -390,7 +390,7 @@ public void testCreateRecordWithUrlSchemaNull() throws Exception { String schemaId = null; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { + if (item.getRelationType().equals(DataResourceRecordUtil.RELATED_SCHEMA_TYPE)) { item.setValue(null); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -415,7 +415,7 @@ public void testCreateRecordWithInvalidUrl() throws Exception { String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { + if (item.getRelationType().equals(DataResourceRecordUtil.RELATED_SCHEMA_TYPE)) { item.setValue(invalidSchemaUrl); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -440,7 +440,7 @@ public void testCreateRecordWithInvalidUrlSchema() throws Exception { String urlWithInvalidSchema = getSchemaUrl(SCHEMA_ID).replace(SCHEMA_ID, INVALID_SCHEMA); DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { + if (item.getRelationType().equals(DataResourceRecordUtil.RELATED_SCHEMA_TYPE)) { item.setValue(urlWithInvalidSchema); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -475,7 +475,7 @@ public void testCreateRecordWithAnyValidUrl() throws Exception { String schemaUrl = "http://anyurl.example.org/shouldNotExist"; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.HAS_METADATA)) { + if (item.getRelationType().equals(DataResourceRecordUtil.RELATED_SCHEMA_TYPE)) { item.setValue(schemaUrl); item.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } @@ -549,7 +549,7 @@ public void testCreateRecordWithIdTwice() throws Exception { file(recordFile). file(metadataFile)).andDo(print()).andExpect(status().isCreated()).andReturn(); for (RelatedIdentifier item : record.getRelatedIdentifiers()) { - if (item.getRelationType().equals(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR)) { + if (item.getRelationType().equals(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE)) { item.setValue(RELATED_RESOURCE_2.getIdentifier()); } } @@ -861,7 +861,7 @@ public void testCreateRecordWithBadRecord2() throws Exception { String schemaId = SCHEMA_ID; DataResource record = SchemaRegistryControllerTestV2.createDataResource4Document(id, schemaId); //remove related resource - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_SCHEMA_TYPE); record.getRelatedIdentifiers().remove(relatedIdentifier); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -941,7 +941,7 @@ public void testCreateTwoVersions() throws Exception { DataResource result = mapper.readValue(res.getResponse().getContentAsString(), DataResource.class); Assert.assertEquals(1L, Long.parseLong(result.getVersion())); - DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setValue(RELATED_RESOURCE_2.toString()); + DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).setValue(RELATED_RESOURCE_2.toString()); record.getAlternateIdentifiers().clear(); record.setId(null); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); @@ -1132,7 +1132,7 @@ public void testFindRecordsByResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertEquals(RELATED_RESOURCE.getIdentifier(), resourceIdentifier.getValue()); } @@ -1165,7 +1165,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); } @@ -1183,7 +1183,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); } @@ -1202,7 +1202,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); } @@ -1222,7 +1222,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); } @@ -1244,7 +1244,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); } @@ -1264,7 +1264,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); } @@ -1283,7 +1283,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertEquals(relatedResource, resourceIdentifier.getValue()); } @@ -1302,7 +1302,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertEquals(relatedResource2, resourceIdentifier.getValue()); } @@ -1322,7 +1322,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertTrue(resourceIdentifier.getValue().endsWith("ResourceId")); } @@ -1341,7 +1341,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertEquals(relatedResource, resourceIdentifier.getValue()); } @@ -1360,7 +1360,7 @@ public void testFindRecordsBySchemaIdANDResourceId() throws Exception { String schemaUrl = schemaIdentifier.getValue(); Assert.assertTrue(schemaUrl.startsWith("http://localhost:")); Assert.assertTrue(schemaUrl.contains(API_SCHEMA_PATH)); - RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier resourceIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals(IDENTIFIER_TYPE.INTERNAL, resourceIdentifier.getIdentifierType()); Assert.assertEquals(relatedResource2, resourceIdentifier.getValue()); } @@ -1913,7 +1913,7 @@ public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exce DataResource record = mapper.readValue(body, DataResource.class); DataResource record2 = mapper.readValue(body, DataResource.class); - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record2, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); expectedRelatedResource = relatedIdentifier.getValue() + "_NEW"; relatedIdentifier.setValue(expectedRelatedResource); MockMultipartFile recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); @@ -1931,14 +1931,14 @@ public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exce Assert.assertEquals(record.getVersion(), record2.getVersion());// version should be the same SchemaRegistryControllerTestV2.validateSets(record.getAcls(), record2.getAcls()); Assert.assertTrue(record.getLastUpdate().isBefore(record2.getLastUpdate())); - RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); - RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); + RelatedIdentifier relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(record2, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertNotEquals("Related resource should be updated!", relatedIdentifier1.getValue(), relatedIdentifier2.getValue()); Assert.assertEquals("Related resource should be updated!", expectedRelatedResource, relatedIdentifier2.getValue()); // Test also updating related type only... IDENTIFIER_TYPE expectedIdentifierType = IDENTIFIER_TYPE.ISBN; - DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setIdentifierType(expectedIdentifierType); + DataResourceRecordUtil.getRelatedIdentifier(record2, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).setIdentifierType(expectedIdentifierType); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record2).getBytes()); @@ -1953,8 +1953,8 @@ public void testUpdateRecordWithoutDocumentChangingRelatedResource() throws Exce Assert.assertEquals(record2.getVersion(), record3.getVersion());// version should be the same SchemaRegistryControllerTestV2.validateSets(record2.getAcls(), record3.getAcls()); Assert.assertTrue(record2.getLastUpdate().isBefore(record3.getLastUpdate())); - relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record2, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); - relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(record3, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + relatedIdentifier1 = DataResourceRecordUtil.getRelatedIdentifier(record2, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); + relatedIdentifier2 = DataResourceRecordUtil.getRelatedIdentifier(record3, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); Assert.assertEquals("Related resource should be the same!", relatedIdentifier1.getValue(), relatedIdentifier2.getValue()); Assert.assertEquals("Related resource type should be updated!", expectedIdentifierType, relatedIdentifier2.getIdentifierType()); @@ -2721,7 +2721,7 @@ private String createDCMetadataRecord() throws Exception { private String createDCMetadataRecordWithRelatedResource(String myRelatedResource, String schemaId) throws Exception { String randomId = UUID.randomUUID().toString(); DataResource record = SchemaRegistryControllerTestV2.createDataResource4JsonDocument(randomId, schemaId); - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); relatedIdentifier.setIdentifierType(IDENTIFIER_TYPE.INTERNAL); relatedIdentifier.setValue(myRelatedResource); Set<AclEntry> aclEntries = new HashSet<>(); @@ -2792,7 +2792,7 @@ private String ingestNewMetadataRecord(String id, long version) throws Exception ObjectMapper mapper = new ObjectMapper(); DataResource record = mapper.readValue(body, DataResource.class); - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); relatedIdentifier.setValue(RELATED_RESOURCE_STRING + version); relatedIdentifier.setIdentifierType(IDENTIFIER_TYPE.INTERNAL); // record.setRelatedResource(ResourceIdentifier.factoryInternalResourceIdentifier(RELATED_RESOURCE_STRING + version)); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java index 331b00cb..8f111e85 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/MetadataControllerTestWithAuthenticationEnabledV2.java @@ -495,7 +495,7 @@ public void testCreateRecordWithIdTwice() throws Exception { andDo(print()). andExpect(status().isCreated()). andReturn(); - DataResourceRecordUtil.getRelatedIdentifier(record, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR).setValue(RELATED_RESOURCE_2.getIdentifier()); + DataResourceRecordUtil.getRelatedIdentifier(record, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE).setValue(RELATED_RESOURCE_2.getIdentifier()); recordFile = new MockMultipartFile("record", "metadata-record.json", "application/json", mapper.writeValueAsString(record).getBytes()); this.mockMvc.perform(MockMvcRequestBuilders.multipart(API_METADATA_PATH). @@ -887,7 +887,7 @@ public void testCreateTwoVersions() throws Exception { relatedIdentifiers.remove(item); } } - RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE_STRING_2, null, null); + RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, RELATED_RESOURCE_STRING_2, null, null); relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); relatedIdentifiers.add(relatedResource); record.getAlternateIdentifiers().clear(); diff --git a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java index 86ae4a8d..ceca1a1c 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java +++ b/src/test/java/edu/kit/datamanager/metastore2/test/SchemaRegistryControllerTestV2.java @@ -1984,10 +1984,10 @@ public static DataResource createDataResource4Document(String id, String schemaI setTitle(record, id); record.setResourceType(ResourceType.createResourceType(metadataType, ResourceType.TYPE_GENERAL.MODEL)); - RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, RELATED_RESOURCE_STRING, null, null); + RelatedIdentifier relatedResource = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, RELATED_RESOURCE_STRING, null, null); relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); record.getRelatedIdentifiers().add(relatedResource); - relatedResource = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, schemaId, null, null); + relatedResource = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, schemaId, null, null); if ((schemaId != null) && schemaId.startsWith("http")) { relatedResource.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } else { @@ -2010,22 +2010,22 @@ public static DataResource createDataResource4Document(String id, String schemaI } public static void setRelatedResource(DataResource dataResource, String relatedResource) { - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE); if (relatedIdentifier != null) { relatedIdentifier.setValue(relatedResource); } else { - relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, relatedResource, null, null); + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, relatedResource, null, null); dataResource.getRelatedIdentifiers().add(relatedIdentifier); } relatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.URL); } public static void setRelatedSchema(DataResource dataResource, String relatedSchema) { - RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, RelatedIdentifier.RELATION_TYPES.HAS_METADATA); + RelatedIdentifier relatedIdentifier = DataResourceRecordUtil.getRelatedIdentifier(dataResource, DataResourceRecordUtil.RELATED_SCHEMA_TYPE); if (relatedIdentifier != null) { relatedIdentifier.setValue(relatedSchema); } else { - relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, relatedSchema, null, null); + relatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, relatedSchema, null, null); dataResource.getRelatedIdentifiers().add(relatedIdentifier); } if (relatedSchema.startsWith("http")) { @@ -2287,16 +2287,16 @@ public static void validateRelatedIdentifierSets(Set<RelatedIdentifier> first, S copyFirst.addAll(first); copySecond.addAll(second); for (RelatedIdentifier item : copyFirst) { - if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { + if (item.getRelationType() == DataResourceRecordUtil.RELATED_NEW_VERSION_OF) { provenanceFirstRelatedIdentifier = item; - Assert.assertEquals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, item.getRelationType()); + Assert.assertEquals(DataResourceRecordUtil.RELATED_NEW_VERSION_OF, item.getRelationType()); Assert.assertEquals(IDENTIFIER_TYPE.URL, item.getIdentifierType()); break; } } for (RelatedIdentifier item : copySecond) { - if (item.getRelationType() == RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM) { - Assert.assertEquals(RelatedIdentifier.RELATION_TYPES.IS_DERIVED_FROM, item.getRelationType()); + if (item.getRelationType() == DataResourceRecordUtil.RELATED_NEW_VERSION_OF) { + Assert.assertEquals(DataResourceRecordUtil.RELATED_NEW_VERSION_OF, item.getRelationType()); Assert.assertEquals(IDENTIFIER_TYPE.URL, item.getIdentifierType()); provenanceSecondRelatedIdentifier = item; if (provenanceFirstRelatedIdentifier != null) { diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java index a7b61725..fb199123 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/DataResourceRecordUtilTest.java @@ -203,9 +203,9 @@ public void testMergeIdenticalAcls() throws URISyntaxException { public void testFixSchemaUrl() throws URISyntaxException, IOException { System.out.println("testFixSchemaUrl"); DataResourceRecordUtil.fixSchemaUrl((RelatedIdentifier) null); - RelatedIdentifier ri1 = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "1", null, null); + RelatedIdentifier ri1 = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "1", null, null); ri1.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); - RelatedIdentifier ri2 = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "test", null, null); + RelatedIdentifier ri2 = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "test", null, null); ri2.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); DataResourceRecordUtil.fixSchemaUrl(ri2); assertTrue("Found second version", ri2.getValue().endsWith("test2")); @@ -217,7 +217,7 @@ public void testFixSchemaUrl() throws URISyntaxException, IOException { public void testFixSchemaUrlWrongFormat() throws URISyntaxException, IOException { System.out.println("testFixSchemaUrl"); DataResourceRecordUtil.fixSchemaUrl((RelatedIdentifier) null); - RelatedIdentifier ri1 = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "1" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "2", null, null); + RelatedIdentifier ri1 = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "test" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "1" + DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR + "2", null, null); ri1.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); DataResourceRecordUtil.fixSchemaUrl(ri1); // following line shouldn't be reached @@ -231,16 +231,16 @@ public void testvalidateRelatedResources4MetadataDocuments() { DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(null); assertTrue(false); } catch (BadArgumentException bae) { - assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); - assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); } try { DataResource hasNoRelatedIdentifier = DataResource.factoryNewDataResource(); DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasNoRelatedIdentifier); assertTrue(false); } catch (BadArgumentException bae) { - assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); - assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); } try { DataResource hasNeitherSchemaNorDataResource = DataResource.factoryNewDataResource(); @@ -249,61 +249,61 @@ public void testvalidateRelatedResources4MetadataDocuments() { DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasNeitherSchemaNorDataResource); assertTrue(false); } catch (BadArgumentException bae) { - assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); - assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); } try { DataResource hasTwoDataResourcesButNoSchema = DataResource.factoryNewDataResource(); - hasTwoDataResourcesButNoSchema.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first data", null, null)); - hasTwoDataResourcesButNoSchema.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second data", null, null)); + hasTwoDataResourcesButNoSchema.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "first data", null, null)); + hasTwoDataResourcesButNoSchema.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "second data", null, null)); DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasTwoDataResourcesButNoSchema); assertTrue(false); } catch (BadArgumentException bae) { - assertFalse("Multiple 'isMetadataFor' should be allowed!", bae.getMessage().contains("isMetadataFor")); - assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); + assertFalse("Multiple '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "' should be allowed!", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); } try { DataResource hasTwoSchemasAndNoDataResource = DataResource.factoryNewDataResource(); - hasTwoSchemasAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); - hasTwoSchemasAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "second schema", null, null)); + hasTwoSchemasAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, "first schema", null, null)); + hasTwoSchemasAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, "second schema", null, null)); DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasTwoSchemasAndNoDataResource); assertTrue(false); } catch (BadArgumentException bae) { - assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); - assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); } try { DataResource hasOneSchemaAndNoDataResource = DataResource.factoryNewDataResource(); - hasOneSchemaAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); + hasOneSchemaAndNoDataResource.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, "first schema", null, null)); DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasOneSchemaAndNoDataResource); assertTrue(false); } catch (BadArgumentException bae) { - assertFalse("Error should not contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); - assertTrue("Error should contain 'isMetadataFor'", bae.getMessage().contains("isMetadataFor")); + assertFalse("Error should not contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); } try { DataResource hasSchemaAndTwoDataResources = DataResource.factoryNewDataResource(); - hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); - hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first data", null, null)); - hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second data", null, null)); + hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, "first schema", null, null)); + hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "first data", null, null)); + hasSchemaAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "second data", null, null)); DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasSchemaAndTwoDataResources); assertTrue(true); } catch (BadArgumentException bae) { - assertFalse("Error should not contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); - assertFalse("Multiple 'isMetadataFor' should be allowed!", bae.getMessage().contains("isMetadataFor")); + assertFalse("Error should not contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); + assertFalse("Multiple '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "' should be allowed!", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); assertTrue(false); } try { DataResource hasTwoSchemasAndTwoDataResources = DataResource.factoryNewDataResource(); - hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "first schema", null, null)); - hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "second schema", null, null)); - hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "first data", null, null)); - hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.IS_METADATA_FOR, "second data", null, null)); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, "first schema", null, null)); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, "second schema", null, null)); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "first data", null, null)); + hasTwoSchemasAndTwoDataResources.getRelatedIdentifiers().add(RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE, "second data", null, null)); DataResourceRecordUtil.validateRelatedResources4MetadataDocuments(hasTwoSchemasAndTwoDataResources); assertTrue(false); } catch (BadArgumentException bae) { - assertTrue("Error should contain 'hasMetadata'", bae.getMessage().contains("hasMetadata")); - assertFalse("Multiple 'isMetadataFor' should be allowed!", bae.getMessage().contains("isMetadataFor")); + assertTrue("Error should contain '" + DataResourceRecordUtil.RELATED_SCHEMA_TYPE + "'", bae.getMessage().contains(DataResourceRecordUtil.RELATED_SCHEMA_TYPE.name())); + assertFalse("Multiple '" + DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE + "' should be allowed!", bae.getMessage().contains(DataResourceRecordUtil.RELATED_DATA_RESOURCE_TYPE.name())); } } diff --git a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java index 6b988f4d..e2bf9f56 100644 --- a/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java +++ b/src/test/java/edu/kit/datamanager/metastore2/util/MetadataRecordUtilTest.java @@ -729,7 +729,7 @@ public void testMigrateToMetadataRecord() { //@ToDo Make this working again // dataResource.getTitles().add(Title.factoryTitle(SCHEMA_ID)); // dataResource.setResourceType(ResourceType.createResourceType(SCHEMA_ID)); -// RelatedIdentifier relId = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, SCHEMA_ID, null, null); +// RelatedIdentifier relId = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, SCHEMA_ID, null, null); // relId.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); // dataResource.getRelatedIdentifiers().add(relId); // // dataResourceService adds ACL @@ -819,7 +819,7 @@ public void testSetToken() { } private void setSchema(DataResource dataResource) { - RelatedIdentifier factoryRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(RelatedIdentifier.RELATION_TYPES.HAS_METADATA, "anySchema", null, null); + RelatedIdentifier factoryRelatedIdentifier = RelatedIdentifier.factoryRelatedIdentifier(DataResourceRecordUtil.RELATED_SCHEMA_TYPE, "anySchema", null, null); factoryRelatedIdentifier.setIdentifierType(Identifier.IDENTIFIER_TYPE.INTERNAL); dataResource.getRelatedIdentifiers().add(factoryRelatedIdentifier); } From 06e3cfd4ff4610968eae6a35fde0d96d371b1591 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:01:24 +0000 Subject: [PATCH 162/181] Update dependency gradle to v8.11.1 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 94113f20..e2847c82 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 9c6c517f47ff842bdbd4995f12044926f357e20c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:01:29 +0000 Subject: [PATCH 163/181] Update dependency commons-io:commons-io to v2.18.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1d5fd8df..7ed6cf82 100644 --- a/build.gradle +++ b/build.gradle @@ -99,7 +99,7 @@ dependencies { implementation "com.h2database:h2:2.3.232" // apache - implementation "commons-io:commons-io:2.17.0" + implementation "commons-io:commons-io:2.18.0" implementation "org.apache.tika:tika-core:3.0.0" // JSON validator From 641f4a16470537ffdf71eb2577b6b65d693512fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:11:31 +0000 Subject: [PATCH 164/181] Update plugin org.springframework.boot to v3.4.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1d5fd8df..3e3fb4dc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.3.5' + id 'org.springframework.boot' version '3.4.0' id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.11' id 'io.freefair.maven-publish-java' version '8.11' From 2500b1edd292180f8b76422a5cdde1bb5a26b9f5 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Nov 2024 11:53:03 +0100 Subject: [PATCH 165/181] Update build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3e3fb4dc..820c8df0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.4.0' + id 'org.springframework.boot' version '3.3.6' id 'io.spring.dependency-management' version '1.1.6' id 'io.freefair.lombok' version '8.11' id 'io.freefair.maven-publish-java' version '8.11' From fbceb3cb9b2b1784fd41993d99584e5f8df307ba Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Nov 2024 13:12:17 +0100 Subject: [PATCH 166/181] Docker: Adapt versions and container names. --- .env | 4 ++-- docker-compose.yml | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.env b/.env index b2d50c2e..9328be5d 100644 --- a/.env +++ b/.env @@ -12,9 +12,9 @@ RABBIT_MQ_PASSWORD=rabbitpasswd # Only edit the following lines if you # want to update service versions. ######################################## -METASTORE_VERSION=v1.4.4 +METASTORE_VERSION=v2.0.0 FRONTEND_COLLECTION_VERSION=metastore-v1.0.1 -INDEXING_SERVICE_VERSION=v1.0.1 +INDEXING_SERVICE_VERSION=v1.0.2 ELASTICSEARCH_VERSION=8.11.1 ######################################## # Don't edit following lines diff --git a/docker-compose.yml b/docker-compose.yml index a07940f4..13d2aeec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,8 @@ services: ipv4_address: 172.0.0.10 my-apache: image: ${PREFIX4DOCKER}/frontend-collection-metastore:${FRONTEND_COLLECTION_VERSION} - container_name: my_apache + container_name: frontend.docker + hostname: frontend.docker ports: - "80:80" networks: @@ -42,7 +43,7 @@ services: image: rabbitmq:4-management depends_on: - elasticsearch - container_name: rabbitmq4indexing + container_name: rabbitmq.docker hostname: rabbitmq.docker environment: - HOSTNAMES=rabbitmq.docker @@ -55,7 +56,8 @@ services: - dps indexing-service: image: ${PREFIX4DOCKER}/indexing-service:${INDEXING_SERVICE_VERSION} - container_name: indexing4metastore + container_name: indexing.docker + hostname: indexing.docker environment: - REPO_MESSAGING_USERNAME=${RABBIT_MQ_USER} - REPO_MESSAGING_PASSWORD=${RABBIT_MQ_PASSWORD} From 6cd475b7244a1235ef03f07c64dede18388746a2 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Nov 2024 15:09:02 +0100 Subject: [PATCH 167/181] Update to Java 21 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fbef2b71..33a40fc1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,4 +23,4 @@ action.custom-6.args=--configure-on-demand -w -x check -x test -Xlint:deprecatio netbeans.com-github-philippefichet-sonarlint4netbeans.excludedRules=[{"repository":"php","rule":"S3335"},{"repository":"php","rule":"S3336"},{"repository":"php","rule":"S2002"},{"repository":"php","rule":"S2005"},{"repository":"php","rule":"S3333"},{"repository":"php","rule":"S3334"},{"repository":"Web","rule":"ImgWithoutWidthOrHeightCheck"},{"repository":"php","rule":"S1151"},{"repository":"php","rule":"S3332"},{"repository":"php","rule":"S2001"},{"repository":"Web","rule":"UnclosedTagCheck"},{"repository":"php","rule":"S2000"},{"repository":"java","rule":"S2309"},{"repository":"php","rule":"S126"},{"repository":"java","rule":"S4605"},{"repository":"java","rule":"S4604"},{"repository":"java","rule":"S2308"},{"repository":"xml","rule":"S3282"},{"repository":"php","rule":"S2918"},{"repository":"java","rule":"S2301"},{"repository":"java","rule":"S1696"},{"repository":"java","rule":"S1213"},{"repository":"java","rule":"S1698"},{"repository":"java","rule":"S1699"},{"repository":"php","rule":"S1820"},{"repository":"java","rule":"S3750"},{"repository":"java","rule":"S4288"},{"repository":"java","rule":"S1451"},{"repository":"php","rule":"S122"},{"repository":"java","rule":"S1694"},{"repository":"java","rule":"S1695"},{"repository":"php","rule":"S1821"},{"repository":"php","rule":"S5867"},{"repository":"java","rule":"S6211"},{"repository":"java","rule":"S2096"},{"repository":"java","rule":"S6212"},{"repository":"php","rule":"S2011"},{"repository":"Web","rule":"UnifiedExpressionCheck"},{"repository":"Web","rule":"PageWithoutFaviconCheck"},{"repository":"php","rule":"S134"},{"repository":"java","rule":"S1448"},{"repository":"java","rule":"S2658"},{"repository":"java","rule":"S1449"},{"repository":"java","rule":"S3749"},{"repository":"php","rule":"S139"},{"repository":"java","rule":"S5128"},{"repository":"php","rule":"S2007"},{"repository":"php","rule":"S3337"},{"repository":"php","rule":"S3338"},{"repository":"java","rule":"S1200"},{"repository":"Web","rule":"ComplexityCheck"},{"repository":"Web","rule":"RequiredAttributeCheck"},{"repository":"php","rule":"S104"},{"repository":"java","rule":"S3658"},{"repository":"java","rule":"S2208"},{"repository":"java","rule":"S2444"},{"repository":"java","rule":"S2203"},{"repository":"java","rule":"S2325"},{"repository":"java","rule":"S3414"},{"repository":"Web","rule":"S1829"},{"repository":"java","rule":"S4174"},{"repository":"Web","rule":"IllegalTabCheck"},{"repository":"java","rule":"S1106"},{"repository":"java","rule":"S1228"},{"repository":"java","rule":"S1107"},{"repository":"java","rule":"S1108"},{"repository":"java","rule":"S1109"},{"repository":"xml","rule":"S3373"},{"repository":"java","rule":"S1105"},{"repository":"php","rule":"S5856"},{"repository":"java","rule":"S2063"},{"repository":"java","rule":"S3030"},{"repository":"java","rule":"S3032"},{"repository":"Web","rule":"DoubleQuotesCheck"},{"repository":"Web","rule":"MaxLineLengthCheck"},{"repository":"java","rule":"S105"},{"repository":"Web","rule":"LongJavaScriptCheck"},{"repository":"java","rule":"S103"},{"repository":"Web","rule":"WmodeIsWindowCheck"},{"repository":"java","rule":"S104"},{"repository":"java","rule":"S109"},{"repository":"java","rule":"S113"},{"repository":"java","rule":"S4926"},{"repository":"java","rule":"S4248"},{"repository":"java","rule":"S1774"},{"repository":"php","rule":"S1105"},{"repository":"php","rule":"S1106"},{"repository":"php","rule":"S1121"},{"repository":"Web","rule":"InputWithoutLabelCheck"},{"repository":"xml","rule":"S2260"},{"repository":"java","rule":"S2059"},{"repository":"java","rule":"S1641"},{"repository":"java","rule":"S2972"},{"repository":"java","rule":"S2973"},{"repository":"java","rule":"S2974"},{"repository":"java","rule":"S2057"},{"repository":"java","rule":"S6411"},{"repository":"php","rule":"S1117"},{"repository":"java","rule":"S3052"},{"repository":"php","rule":"S1451"},{"repository":"Web","rule":"NonConsecutiveHeadingCheck"},{"repository":"java","rule":"S126"},{"repository":"java","rule":"S923"},{"repository":"java","rule":"S134"},{"repository":"java","rule":"S1315"},{"repository":"java","rule":"S1312"},{"repository":"java","rule":"S1314"},{"repository":"java","rule":"S4266"},{"repository":"java","rule":"S1310"},{"repository":"java","rule":"S2196"},{"repository":"java","rule":"S2197"},{"repository":"java","rule":"S118"},{"repository":"java","rule":"S1309"},{"repository":"java","rule":"S3725"},{"repository":"java","rule":"S121"},{"repository":"java","rule":"S122"},{"repository":"Web","rule":"DynamicJspIncludeCheck"},{"repository":"php","rule":"S1578"},{"repository":"php","rule":"S5935"},{"repository":"Web","rule":"InlineStyleCheck"},{"repository":"java","rule":"S3047"},{"repository":"java","rule":"S1541"},{"repository":"java","rule":"S2260"},{"repository":"java","rule":"S2141"},{"repository":"php","rule":"S1311"},{"repository":"java","rule":"S2384"},{"repository":"php","rule":"S4142"},{"repository":"xml","rule":"S105"},{"repository":"java","rule":"S2701"},{"repository":"xml","rule":"S103"},{"repository":"java","rule":"S2148"},{"repository":"php","rule":"S881"},{"repository":"java","rule":"S2143"},{"repository":"java","rule":"S1176"},{"repository":"java","rule":"S1160"},{"repository":"php","rule":"S1200"},{"repository":"java","rule":"S2250"},{"repository":"java","rule":"S818"},{"repository":"java","rule":"S1162"},{"repository":"java","rule":"S4551"},{"repository":"java","rule":"S2131"},{"repository":"java","rule":"S138"},{"repository":"java","rule":"S139"},{"repository":"Web","rule":"IllegalNamespaceCheck"},{"repository":"php","rule":"S5915"},{"repository":"php","rule":"S1314"},{"repository":"java","rule":"S1166"},{"repository":"php","rule":"S1799"},{"repository":"java","rule":"S5793"},{"repository":"java","rule":"S1194"},{"repository":"java","rule":"S2162"},{"repository":"java","rule":"S2164"},{"repository":"xml","rule":"S3419"},{"repository":"Web","rule":"WhiteSpaceAroundCheck"},{"repository":"java","rule":"S3937"},{"repository":"xml","rule":"S1120"},{"repository":"java","rule":"S1996"},{"repository":"Web","rule":"MultiplePageDirectivesCheck"},{"repository":"xml","rule":"S3420"},{"repository":"java","rule":"S3254"},{"repository":"xml","rule":"S3423"},{"repository":"java","rule":"S2047"},{"repository":"php","rule":"S1541"},{"repository":"Web","rule":"InternationalizationCheck"},{"repository":"java","rule":"S3242"},{"repository":"java","rule":"S6073"},{"repository":"php","rule":"S2070"},{"repository":"java","rule":"S2959"},{"repository":"Web","rule":"LinkToNothingCheck"},{"repository":"Web","rule":"S1436"},{"repository":"java","rule":"S2039"},{"repository":"xml","rule":"S2321"},{"repository":"java","rule":"S1188"},{"repository":"java","rule":"S1067"},{"repository":"java","rule":"S2156"},{"repository":"java","rule":"S3366"},{"repository":"Web","rule":"LinksIdenticalTextsDifferentTargetsCheck"},{"repository":"php","rule":"S2047"},{"repository":"php","rule":"S2046"},{"repository":"java","rule":"S5970"},{"repository":"php","rule":"S2043"},{"repository":"php","rule":"S2042"},{"repository":"php","rule":"S1990"},{"repository":"php","rule":"S2044"},{"repository":"java","rule":"S864"},{"repository":"Web","rule":"MouseEventWithoutKeyboardEquivalentCheck"},{"repository":"java","rule":"S5979"},{"repository":"java","rule":"NoSonar"},{"repository":"java","rule":"S5977"},{"repository":"java","rule":"S5612"},{"repository":"java","rule":"S1258"},{"repository":"java","rule":"S3437"},{"repository":"Web","rule":"FileLengthCheck"},{"repository":"Web","rule":"JspScriptletCheck"},{"repository":"java","rule":"S2221"},{"repository":"java","rule":"S1132"},{"repository":"java","rule":"S3553"},{"repository":"php","rule":"NoSonar"},{"repository":"Web","rule":"IllegalTagLibsCheck"},{"repository":"php","rule":"S2050"},{"repository":"java","rule":"S3306"},{"repository":"java","rule":"S2698"},{"repository":"php","rule":"S1996"},{"repository":"php","rule":"S2964"},{"repository":"java","rule":"S2693"},{"repository":"java","rule":"S1120"},{"repository":"java","rule":"S2694"},{"repository":"java","rule":"S2211"},{"repository":"php","rule":"S1997"},{"repository":"java","rule":"S2333"},{"repository":"java","rule":"S1244"},{"repository":"php","rule":"S5899"},{"repository":"java","rule":"S1151"},{"repository":"Web","rule":"IllegalElementCheck"},{"repository":"java","rule":"S5194"},{"repository":"php","rule":"S2260"},{"repository":"java","rule":"S888"},{"repository":"java","rule":"S1711"},{"repository":"java","rule":"S3578"},{"repository":"php","rule":"S2036"},{"repository":"php","rule":"S2278"},{"repository":"php","rule":"S2277"},{"repository":"php","rule":"S1067"},{"repository":"php","rule":"S2830"},{"repository":"php","rule":"S2038"},{"repository":"php","rule":"S2037"},{"repository":"php","rule":"S5783"},{"repository":"java","rule":"S1939"},{"repository":"java","rule":"S1821"},{"repository":"java","rule":"S1942"},{"repository":"java","rule":"S1943"},{"repository":"java","rule":"S881"},{"repository":"java","rule":"S5867"},{"repository":"java","rule":"S1147"},{"repository":"java","rule":"S1820"},{"repository":"java","rule":"S1941"},{"repository":"java","rule":"S1142"},{"repository":"php","rule":"S2701"},{"repository":"Web","rule":"HeaderCheck"}] netbeans.com-github-philippefichet-sonarlint4netbeans.rules_2e_parameters_2e_java_2e_S3776_2e_Threshold=15 netbeans.com-github-philippefichet-sonarlint4netbeans.extraProperties={} -netbeans.hint.jdkPlatform=JDK_19 +netbeans.hint.jdkPlatform=JDK_21 From b8f4c6ec0899c6e4705b4488eaff6cc34bbc90d8 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Nov 2024 15:10:27 +0100 Subject: [PATCH 168/181] Fix determing schemaID for messages. --- .../entities/messaging/MetadataResourceMessage.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java index 3ab6f14a..996b740b 100644 --- a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java +++ b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java @@ -104,7 +104,12 @@ public static MetadataResourceMessage createMessage(MetadataRecord metadataRecor Map<String, String> properties = new HashMap<>(); if (metadataRecord != null) { properties.put(RESOLVING_URL_PROPERTY, removeFilterFromUri(metadataRecord.getMetadataDocumentUri())); - properties.put(DOCUMENT_TYPE_PROPERTY, metadataRecord.getSchema().getIdentifier()); + // remove base path of endpoint from URL e.g.: http://x.y.z/metastore/api/v2/schemas/id_of_schema -> id_of_schema + String schemaDocumentUri = metadataRecord.getSchema().getIdentifier(); + String[] split = schemaDocumentUri.split("\\/", -1); + String schemaId = removeFilterFromUri(split[split.length - 1]); + properties.put(DOCUMENT_TYPE_PROPERTY, schemaId); + msg.setEntityId(metadataRecord.getId()); } if (action != null) { @@ -160,7 +165,8 @@ public static MetadataResourceMessage createMessage(DataResource dataResource, A if (dataResource != null) { String metadataDocumentUri = DataResourceRecordUtil.getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()).toString(); String schemaDocumentUri = DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue(); - String[] split = schemaDocumentUri.split(DataResourceRecordUtil.SCHEMA_VERSION_SEPARATOR, -1); + // remove base path of endpoint from URL e.g.: http://x.y.z/metastore/api/v2/schemas/id_of_schema -> id_of_schema + String[] split = schemaDocumentUri.split("\\/", -1); String schemaId = removeFilterFromUri(split[split.length - 1]); properties.put(RESOLVING_URL_PROPERTY, removeFilterFromUri(metadataDocumentUri)); From 80816813082d74ac7748cc7fff98eccc957c08ac Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Nov 2024 16:36:53 +0100 Subject: [PATCH 169/181] Update static documentation and README.md --- README.md | 25 +- restDocu.md | 799 ++++---- restDocuV2.md | 4803 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 5243 insertions(+), 384 deletions(-) create mode 100644 restDocuV2.md diff --git a/README.md b/README.md index 93f28af5..a1f0772d 100644 --- a/README.md +++ b/README.md @@ -186,20 +186,20 @@ To stop and start all services do the following: ``` user@localhost:/home/user/metastore2$ docker compose stop [+] Running 6/6 - ⠿ Container dockercompose-my-apache-1 Stopped 1.5s - ⠿ Container indexing4metastore Stopped 13.2s - ⠿ Container dockercompose-dps-1 Stopped 0.3s + ⠿ Container frontend.docker Stopped 1.5s ⠿ Container metastore.docker St... 13.1s - ⠿ Container rabbitmq4indexing S... 6.5s - ⠿ Container elastic4indexing St... 0.8s + ⠿ Container indexing.docker Stopped 0.3s + ⠿ Container metastore2-dps-1 Stopped 13.2s + ⠿ Container rabbitmq.docker S... 6.5s + ⠿ Container elastic.docker St... 0.8s user@localhost:/home/user/metastore2$ docker compose start [+] Running 6/6 - ⠿ Container dockercompose-dps-1 Started 0.4s - ⠿ Container dockercompose-my-apache-1 Started 0.6s - ⠿ Container elastic4indexing He... 10.9s - ⠿ Container rabbitmq4indexing S... 0.3s - ⠿ Container metastore.docker St... 0.5s - ⠿ Container indexing4metastore Started 0.5s + ⠿ Container frontend.docker Started 0.3s + ⠿ Container metastore2-dps-1 Started 10.5s + ⠿ Container elastic.docker Started 0.2s + ⠿ Container rabbitmq.docker Started 0.2s + ⠿ Container metastore.docker Started 0.2s + ⠿ Container indexing.docker Started 0.2s user@localhost:/home/user/metastore2$ ``` @@ -236,7 +236,8 @@ user@localhost:/home/user/metastore2$bash /PATH/TO/EMPTY/INSTALLATION/DIRECTORY/ ## More Information * [Information about KIT Data Manager 2](https://github.com/kit-data-manager/base-repo) -* [REST Documentation MetaStore2](restDocu.md) +* [REST Documentation MetaStore2 API V1](restDocu.md) +* [REST Documentation MetaStore2 API V2](restDocuV2.md) ## License diff --git a/restDocu.md b/restDocu.md index ed834923..1b0deba4 100644 --- a/restDocu.md +++ b/restDocu.md @@ -1,5 +1,4 @@ -Introduction -============ +# Introduction In this documentation, the basics of the KIT Data Manager RESTful API of the MetaStore Service are described. You will be guided through the @@ -33,8 +32,7 @@ will be assigned by the server, others remain empty or null as long as you don’t assign any value to them. All fields mandatory at creation time are explained in the resource creation example. -Building the URL ----------------- +## Building the URL The URL for accessing the MetaStore REST endpoints is constructed as follows: @@ -56,11 +54,9 @@ need to run the following in your browser: In former versions (< 1.3.0), no context path was provided by default. -XML (Schema) -============ +# XML (Schema) -Schema Registration and Management ----------------------------------- +## Schema Registration and Management In this first section, the handling of schema resources is explained. It all starts with creating your first xml schema resource. The model of a @@ -78,6 +74,7 @@ metadata schema record looks like this: "sid" : "...", "permission" : "..." } ], + "licenseUri" : "...", "schemaDocumentUri" : "...", "schemaHash" : "...", "locked" : false @@ -93,8 +90,9 @@ In addition, ACL may be useful to make schema readable/editable by others. This will be of interest while accessing/updating an existing schema.(if authorization is enabled) -Registering a Metadata Schema Document --------------------------------------- +License URI is optional. It’s new since 1.5.0. + +## Registering a Metadata Schema Document The following example shows the creation of the first xsd schema only providing mandatory fields mentioned above: @@ -120,7 +118,7 @@ providing mandatory fields mentioned above: </xs:element> </xs:schema> - $ curl 'http://localhost:8040/metastore/api/v1/schemas' -i -X POST \ + $ curl 'http://localhost:8040/metastore/api/v1/schemas/' -i -X POST \ -H 'Content-Type: multipart/form-data' \ -F 'schema=@schema.xsd;type=application/xml' \ -F 'record=@schema-record.json;type=application/json' @@ -129,7 +127,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaId, mimeType and type are provided by the user. HTTP-wise the call looks as follows: - POST /metastore/api/v1/schemas HTTP/1.1 + POST /metastore/api/v1/schemas/ HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Host: localhost:8040 @@ -155,18 +153,18 @@ looks as follows: Content-Disposition: form-data; name=record; filename=schema-record.json Content-Type: application/json - {"schemaId":"my_first_xsd","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"XML","createdAt":null,"lastUpdate":null,"acl":[],"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} + {"schemaId":"my_first_xsd","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"XML","createdAt":null,"lastUpdate":null,"acl":[],"licenseUri":null,"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- -As Content-Type only 'application/json' is supported and should be +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=1 - ETag: "343002905" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1 + ETag: "1162986171" Content-Type: application/json Content-Length: 467 @@ -175,14 +173,14 @@ the user and will look that way: "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.695Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.632Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "schemaHash" : "sha1:08b262fe74604d6d5d001ed03718408e52bae9aa", "doNotSync" : true } @@ -218,7 +216,7 @@ As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. HTTP/1.1 200 OK - ETag: "343002905" + ETag: "1162986171" Content-Type: application/vnd.datamanager.schema-record+json Content-Length: 467 @@ -227,14 +225,14 @@ again the corresponding ETag in the HTTP response header. "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.695Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.632Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "schemaHash" : "sha1:08b262fe74604d6d5d001ed03718408e52bae9aa", "doNotSync" : true } @@ -308,14 +306,14 @@ updated metadata schema document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "343002905"' \ + -H 'If-Match: "1162986171"' \ -F 'schema=@schema-v2.xsd;type=application/xml' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "343002905" + If-Match: "1162986171" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -343,8 +341,8 @@ As a result, you receive the updated schema record and in the HTTP response header the new location URL and the ETag. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=2 - ETag: "1480363722" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2 + ETag: "2037192494" Content-Type: application/json Content-Length: 467 @@ -353,14 +351,14 @@ response header the new location URL and the ETag. "schemaVersion" : 2, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.835Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.883Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=2", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "schemaHash" : "sha1:c227b2bf612264da33fe5a695b5450101ce9d766", "doNotSync" : true } @@ -390,14 +388,14 @@ document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1480363722"' \ + -H 'If-Match: "2037192494"' \ -F 'schema=@schema-v3.xsd;type=application/xml' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1480363722" + If-Match: "2037192494" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -426,24 +424,24 @@ As a result, you receive the updated schema record and in the HTTP response header the new location URL and the ETag. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3 - ETag: "2106056635" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 + ETag: "-1900509294" Content-Type: application/json - Content-Length: 466 + Content-Length: 467 { "schemaId" : "my_first_xsd", "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.87Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.957Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "schemaHash" : "sha1:1baea3a07d95faea70707fcf46d114315613b970", "doNotSync" : true } @@ -451,8 +449,7 @@ response header the new location URL and the ETag. The updated schema record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. -Registering another Metadata Schema Document --------------------------------------------- +## Registering another Metadata Schema Document The following example shows the creation of another xsd schema only providing mandatory fields mentioned above: @@ -477,14 +474,14 @@ providing mandatory fields mentioned above: </xs:element> </xs:schema> - $ curl 'http://localhost:8040/metastore/api/v1/schemas' -i -X POST \ + $ curl 'http://localhost:8040/metastore/api/v1/schemas/' -i -X POST \ -H 'Content-Type: multipart/form-data' \ -F 'schema=@another-schema.xsd;type=application/xml' \ -F 'record=@another-schema-record.json;type=application/json' HTTP-wise the call looks as follows: - POST /metastore/api/v1/schemas HTTP/1.1 + POST /metastore/api/v1/schemas/ HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Host: localhost:8040 @@ -510,18 +507,18 @@ HTTP-wise the call looks as follows: Content-Disposition: form-data; name=record; filename=another-schema-record.json Content-Type: application/json - {"schemaId":"another_xsd","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"XML","createdAt":null,"lastUpdate":null,"acl":[],"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} + {"schemaId":"another_xsd","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"XML","createdAt":null,"lastUpdate":null,"acl":[],"licenseUri":null,"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- -As Content-Type only 'application/json' is supported and should be +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v1/schemas/another_xsd?version=1 - ETag: "-786384830" + Location: http://localhost:8040/metastore/api/v2/schemas/another_xsd?version=1 + ETag: "2129018532" Content-Type: application/json Content-Length: 465 @@ -530,14 +527,14 @@ the user and will look that way: "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.893Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.983Z", "acl" : [ { "id" : 2, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/another_xsd?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/another_xsd?version=1", "schemaHash" : "sha1:c834a580e7b74c66650a8b00640e4f16cfab7bac", "doNotSync" : true } @@ -548,33 +545,33 @@ Now there are two schemaIds registered in the metadata schema registry. Obtaining all accessible metadata schema records. - $ curl 'http://localhost:8040/metastore/api/v1/schemas' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/schemas/' -i -X GET Same for HTTP request: - GET /metastore/api/v1/schemas HTTP/1.1 + GET /metastore/api/v1/schemas/ HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata schema records. HTTP/1.1 200 OK - Content-Range: 0-9/2 + Content-Range: 0-1/2 Content-Type: application/json - Content-Length: 937 + Content-Length: 938 [ { "schemaId" : "another_xsd", "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.893Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.983Z", "acl" : [ { "id" : 2, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/another_xsd?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/another_xsd?version=1", "schemaHash" : "sha1:c834a580e7b74c66650a8b00640e4f16cfab7bac", "doNotSync" : true }, { @@ -582,14 +579,14 @@ As a result, you receive a list of metadata schema records. "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.87Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.957Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "schemaHash" : "sha1:1baea3a07d95faea70707fcf46d114315613b970", "doNotSync" : true } ] @@ -607,7 +604,7 @@ additional query parameters. The modified HTTP request with pagination looks like follows: - GET /metastore/api/v1/schemas?page=0&size=20 HTTP/1.1 + GET /metastore/api/v1/schemas/?page=0&size=20 HTTP/1.1 Host: localhost:8040 ### Getting a List of all Schema Records for a Specific SchemaId @@ -615,34 +612,34 @@ The modified HTTP request with pagination looks like follows: If you want to obtain all versions of a specific schema you may add the schemaId as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v1/schemas?schemaId=my_first_xsd' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/schemas/?schemaId=my_first_xsd' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/schemas?schemaId=my_first_xsd HTTP/1.1 + GET /metastore/api/v1/schemas/?schemaId=my_first_xsd HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata schema records in descending order. (current version first) HTTP/1.1 200 OK - Content-Range: 0-9/3 + Content-Range: 0-2/3 Content-Type: application/json - Content-Length: 1408 + Content-Length: 1409 [ { "schemaId" : "my_first_xsd", "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.87Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.957Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "schemaHash" : "sha1:1baea3a07d95faea70707fcf46d114315613b970", "doNotSync" : true }, { @@ -650,14 +647,14 @@ order. (current version first) "schemaVersion" : 2, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.835Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.883Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=2", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "schemaHash" : "sha1:c227b2bf612264da33fe5a695b5450101ce9d766", "doNotSync" : true }, { @@ -665,14 +662,14 @@ order. (current version first) "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:24.695Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:29.632Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "schemaHash" : "sha1:08b262fe74604d6d5d001ed03718408e52bae9aa", "doNotSync" : true } ] @@ -872,29 +869,29 @@ example we introduce a user called 'admin' and give him all rights. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "2106056635"' \ + -H 'If-Match: "-1900509294"' \ -F 'record=@schema-record-v4.json;type=application/json' Same for the HTTP request. PUT /metastore/api/v1/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "2106056635" + If-Match: "-1900509294" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=schema-record-v4.json Content-Type: application/json - {"schemaId":"my_first_xsd","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/xml","type":"XML","createdAt":"2023-07-12T12:45:24Z","lastUpdate":"2023-07-12T12:45:24.87Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"schemaDocumentUri":"http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3","schemaHash":"sha1:1baea3a07d95faea70707fcf46d114315613b970","doNotSync":true} + {"schemaId":"my_first_xsd","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/xml","type":"XML","createdAt":"2024-11-22T14:26:29Z","lastUpdate":"2024-11-22T14:26:29.957Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"licenseUri":null,"schemaDocumentUri":"http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3","schemaHash":"sha1:1baea3a07d95faea70707fcf46d114315613b970","doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- As a result, you receive 200 as HTTP status, the updated metadata schema record and the updated ETag and location in the HTTP response header. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3 - ETag: "-229236150" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 + ETag: "-152071665" Content-Type: application/json Content-Length: 543 @@ -903,8 +900,8 @@ record and the updated ETag and location in the HTTP response header. "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2023-07-12T12:45:24Z", - "lastUpdate" : "2023-07-12T12:45:25.147Z", + "createdAt" : "2024-11-22T14:26:29Z", + "lastUpdate" : "2024-11-22T14:26:30.255Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -914,7 +911,7 @@ record and the updated ETag and location in the HTTP response header. "sid" : "admin", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "schemaHash" : "sha1:1baea3a07d95faea70707fcf46d114315613b970", "doNotSync" : true } @@ -927,8 +924,7 @@ After the update the following fields has changed: - acl additional ACL entry (set during update) -Metadata Management -------------------- +## Metadata Management After registration of a schema metadata may be added to MetaStore. In this section, the handling of metadata resources is explained. It all @@ -954,6 +950,7 @@ metadata record looks like this: "sid": "...", "permission": "..." }], + "licenseUri": "...", "metadataDocumentUri": "...", "documentHash": "..." } @@ -971,6 +968,8 @@ In addition, ACL may be useful to make metadata editable by others. If linked schema is identified by its schemaId the INTERNAL type has to be used. +License URI is optional. It’s new since 1.5.0. + ### Register/Ingest a Metadata Record with Metadata Document The following example shows the creation of the first metadata record @@ -998,7 +997,7 @@ and its metadata only providing mandatory fields mentioned above: The schemaId used while registering metadata schema has to be used to link the metadata with the approbriate metadata schema. - $ curl 'http://localhost:8040/metastore/api/v1/metadata' -i -X POST \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/' -i -X POST \ -H 'Content-Type: multipart/form-data' \ -F 'record=@metadata-record.json;type=application/json' \ -F 'document=@metadata.xml;type=application/xml' @@ -1007,7 +1006,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: - POST /metastore/api/v1/metadata HTTP/1.1 + POST /metastore/api/v1/metadata/ HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Host: localhost:8040 @@ -1015,7 +1014,7 @@ call looks as follows: Content-Disposition: form-data; name=record; filename=metadata-record.json Content-Type: application/json - {"id":null,"pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":null,"lastUpdate":null,"schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":1,"recordVersion":null,"acl":[],"metadataDocumentUri":null,"documentHash":null} + {"id":null,"pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":null,"lastUpdate":null,"schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":1,"recordVersion":null,"acl":[],"licenseUri":null,"metadataDocumentUri":null,"documentHash":null} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata.xml Content-Type: application/xml @@ -1026,28 +1025,28 @@ call looks as follows: </example:metadata> --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- -As Content-Type only 'application/json' is supported and should be +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1 - ETag: "-769713607" + Location: http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1 + ETag: "-617455241" Content-Type: application/json - Content-Length: 709 + Content-Length: 711 { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.2Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.394Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=1", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "identifierType" : "URL" }, "schemaVersion" : 1, @@ -1057,7 +1056,7 @@ the user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1", "documentHash" : "sha1:ac92891f6377919446143e0a8639f12715397228" } @@ -1074,12 +1073,12 @@ avoid conflicts. For accessing the metadata the location URL provided before may be used. The URL is compiled by the id of the metadata and its version. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1' -i -X GET \ -H 'Accept: application/xml' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1 HTTP/1.1 Accept: application/xml Host: localhost:8040 @@ -1107,12 +1106,12 @@ The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". Then the command line looks like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 @@ -1120,20 +1119,20 @@ The linked metadata will be returned. The result is sent back to the user and will look that way: HTTP/1.1 200 OK - ETag: "-769713607" + ETag: "-617455241" Content-Type: application/vnd.datamanager.metadata-record+json - Content-Length: 709 + Content-Length: 711 { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.2Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.394Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=1", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "identifierType" : "URL" }, "schemaVersion" : 1, @@ -1143,7 +1142,7 @@ user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1", "documentHash" : "sha1:ac92891f6377919446143e0a8639f12715397228" } @@ -1180,25 +1179,25 @@ the ETag is needed: <example:date>2018-07-02</example:date> </example:metadata> - $ curl 'http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-769713607"' \ + -H 'If-Match: "-617455241"' \ -F 'record=@metadata-record-v2.json;type=application/json' \ -F 'document=@metadata-v2.xml;type=application/xml' You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64 HTTP/1.1 + PUT /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-769713607" + If-Match: "-617455241" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record-v2.json Content-Type: application/json - {"id":"7a07631d-d999-42bb-bd64-32d45838fe64","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2023-07-12T12:45:25Z","lastUpdate":"2023-07-12T12:45:25.2Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"metadataDocumentUri":"http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} + {"id":"d2950149-073d-4e27-bbe8-47d995c00934","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:30Z","lastUpdate":"2024-11-22T14:26:30.394Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v2.xml Content-Type: application/xml @@ -1215,31 +1214,35 @@ Version number of record was incremented by one and 'lastUpdate' was also modified by the server. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=2 - ETag: "-561646093" + Location: http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2 + ETag: "-1243255844" Content-Type: application/json - Content-Length: 711 + Content-Length: 779 { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.348Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.618Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=2", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "identifierType" : "URL" }, "schemaVersion" : 2, "recordVersion" : 2, "acl" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2", "documentHash" : "sha1:e13a87884df391a611fb6257ea53883811d9451a" } @@ -1252,42 +1255,46 @@ Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=2' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=2 HTTP/1.1 + GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 You will get the new metadata record with the new ETag. HTTP/1.1 200 OK - ETag: "-561646093" + ETag: "-1243255844" Content-Type: application/vnd.datamanager.metadata-record+json - Content-Length: 711 + Content-Length: 779 { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.348Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.618Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=2", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "identifierType" : "URL" }, "schemaVersion" : 2, "recordVersion" : 2, "acl" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2", "documentHash" : "sha1:e13a87884df391a611fb6257ea53883811d9451a" } @@ -1315,24 +1322,24 @@ Etag. <example:note>since version 3 notes are allowed</example:note> </example:metadata> - $ curl 'http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-561646093"' \ + -H 'If-Match: "-1243255844"' \ -F 'record=@metadata-record-v3.json;type=application/json' \ -F 'document=@metadata-v3.xml;type=application/xml' HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64 HTTP/1.1 + PUT /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-561646093" + If-Match: "-1243255844" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record-v3.json Content-Type: application/json - {"id":"7a07631d-d999-42bb-bd64-32d45838fe64","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2023-07-12T12:45:25Z","lastUpdate":"2023-07-12T12:45:25.2Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"metadataDocumentUri":"http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} + {"id":"d2950149-073d-4e27-bbe8-47d995c00934","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:30Z","lastUpdate":"2024-11-22T14:26:30.394Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v3.xml Content-Type: application/xml @@ -1348,42 +1355,46 @@ HTTP-wise the call looks as follows: You will get the new metadata record. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=3 - ETag: "1386118128" + Location: http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3 + ETag: "-49539178" Content-Type: application/json - Content-Length: 711 + Content-Length: 779 { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.428Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.721Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } Now you can access the updated metadata via the URI in the HTTP response header. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=3' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=3 HTTP/1.1 + GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3 HTTP/1.1 Host: localhost:8040 You will get the updated metadata. @@ -1432,73 +1443,81 @@ size as additional query parameters. If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?id=7a07631d-d999-42bb-bd64-32d45838fe64' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?id=d2950149-073d-4e27-bbe8-47d995c00934' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?id=7a07631d-d999-42bb-bd64-32d45838fe64 HTTP/1.1 + GET /metastore/api/v1/metadata/?id=d2950149-073d-4e27-bbe8-47d995c00934 HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata records in descending order. (current version first) HTTP/1.1 200 OK - Content-Range: 0-9/3 + Content-Range: 0-2/3 Content-Type: application/json - Content-Length: 2139 + Content-Length: 2277 [ { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.428Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.721Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" }, { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.348Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.618Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=2", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "identifierType" : "URL" }, "schemaVersion" : 2, "recordVersion" : 2, "acl" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=2", - "documentHash" : "sha1:e13a87884df391a611fb6257ea53883811d9451a" + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2", + "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" }, { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.2Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.394Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=1", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "identifierType" : "URL" }, "schemaVersion" : 1, @@ -1508,8 +1527,8 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=1", - "documentHash" : "sha1:ac92891f6377919446143e0a8639f12715397228" + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1", + "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } ] #### Find by resourceId @@ -1520,40 +1539,44 @@ MetaStore may hold multiple metadata documents per resource. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?resoureId=https%3A%2F%2Frepo%2FanyResourceId' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?resoureId=https%3A%2F%2Frepo%2FanyResourceId HTTP/1.1 + GET /metastore/api/v1/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId HTTP/1.1 Host: localhost:8040 You will get the current version of the metadata record(s). HTTP/1.1 200 OK - Content-Range: 0-9/1 + Content-Range: 0-0/1 Content-Type: application/json - Content-Length: 715 + Content-Length: 783 [ { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.428Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.721Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } ] @@ -1563,41 +1586,45 @@ If you want to find all metadata records updated after a specific date. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A25.517721088Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A25.517721088Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z HTTP/1.1 Host: localhost:8040 You will get the current version metadata records updated ln the last 2 hours. HTTP/1.1 200 OK - Content-Range: 0-9/1 + Content-Range: 0-0/1 Content-Type: application/json - Content-Length: 715 + Content-Length: 783 [ { - "id" : "7a07631d-d999-42bb-bd64-32d45838fe64", + "id" : "d2950149-073d-4e27-bbe8-47d995c00934", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:25Z", - "lastUpdate" : "2023-07-12T12:45:25.428Z", + "createdAt" : "2024-11-22T14:26:30Z", + "lastUpdate" : "2024-11-22T14:26:30.721Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_xsd?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/7a07631d-d999-42bb-bd64-32d45838fe64?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } ] @@ -1608,28 +1635,26 @@ range. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A25.517721088Z&until=2023-07-12T11%3A45%3A25.517718361Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z&until=2024-11-22T13%3A26%3A30.799639633Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A25.517721088Z&until=2023-07-12T11%3A45%3A25.517718361Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z&until=2024-11-22T13%3A26%3A30.799639633Z HTTP/1.1 Host: localhost:8040 You will get an empty array as no metadata record exists in the given range: HTTP/1.1 200 OK - Content-Range: 0-9/0 + Content-Range: */0 Content-Type: application/json Content-Length: 3 [ ] -JSON (Schema) -============= +# JSON (Schema) -Schema Registration and Management ----------------------------------- +## Schema Registration and Management In this section, the handling of json schema resources is explained. It all starts with creating your first json schema resource. The model of a @@ -1647,6 +1672,7 @@ metadata schema record looks like this: "sid" : "...", "permission" : "..." } ], + "licenseUri" : "...", "schemaDocumentUri" : "...", "schemaHash" : "...", "locked" : false @@ -1664,8 +1690,9 @@ At least the following elements are expected to be provided by the user: In addition, ACL may be useful to make schema editable by others. (This will be of interest while updating an existing schema) -Registering a Metadata Schema Document --------------------------------------- +License URI is optional. It’s new since 1.5.0. + +## Registering a Metadata Schema Document The following example shows the creation of the first json schema only providing mandatory fields mentioned above: @@ -1696,7 +1723,7 @@ providing mandatory fields mentioned above: "additionalProperties": false } - $ curl 'http://localhost:8040/metastore/api/v1/schemas' -i -X POST \ + $ curl 'http://localhost:8040/metastore/api/v1/schemas/' -i -X POST \ -H 'Content-Type: multipart/form-data' \ -F 'schema=@schema.json;type=application/json' \ -F 'record=@schema-record4json.json;type=application/json' @@ -1705,7 +1732,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaId, mimeType and type are provided by the user. HTTP-wise the call looks as follows: - POST /metastore/api/v1/schemas HTTP/1.1 + POST /metastore/api/v1/schemas/ HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Host: localhost:8040 @@ -1735,34 +1762,34 @@ looks as follows: Content-Disposition: form-data; name=record; filename=schema-record4json.json Content-Type: application/json - {"schemaId":"my_first_json","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"JSON","createdAt":null,"lastUpdate":null,"acl":[],"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} + {"schemaId":"my_first_json","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"JSON","createdAt":null,"lastUpdate":null,"acl":[],"licenseUri":null,"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- -As Content-Type only 'application/json' is supported and should be +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=1 - ETag: "1946363153" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1 + ETag: "20745429" Content-Type: application/json - Content-Length: 471 + Content-Length: 470 { "schemaId" : "my_first_json", "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:20.442Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:22.38Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "schemaHash" : "sha1:a312efb2ad92b1c8ce8f6556c40e2b185c6682c4", "doNotSync" : true } @@ -1798,23 +1825,23 @@ As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. HTTP/1.1 200 OK - ETag: "1946363153" + ETag: "20745429" Content-Type: application/vnd.datamanager.schema-record+json - Content-Length: 471 + Content-Length: 470 { "schemaId" : "my_first_json", "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:20.442Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:22.38Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "schemaHash" : "sha1:a312efb2ad92b1c8ce8f6556c40e2b185c6682c4", "doNotSync" : true } @@ -1897,14 +1924,14 @@ updated metadata schema document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1946363153"' \ + -H 'If-Match: "20745429"' \ -F 'schema=@schema-v2.json;type=application/json' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1946363153" + If-Match: "20745429" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -1942,8 +1969,8 @@ As a result, you receive the updated schema record and in the HTTP response header the new location URL and the ETag. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=2 - ETag: "-1107116181" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2 + ETag: "1904643808" Content-Type: application/json Content-Length: 471 @@ -1952,14 +1979,14 @@ response header the new location URL and the ETag. "schemaVersion" : 2, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:21.276Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:23.028Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=2", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "schemaHash" : "sha1:6d65072f514d887d9fa69997d3aa067524c1c085", "doNotSync" : true } @@ -2004,14 +2031,14 @@ document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-1107116181"' \ + -H 'If-Match: "1904643808"' \ -F 'schema=@schema-v3.json;type=application/json' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-1107116181" + If-Match: "1904643808" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -2054,8 +2081,8 @@ As a result, you receive the updated schema record and in the HTTP response header the new location URL and the ETag. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3 - ETag: "-1097218276" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 + ETag: "850923140" Content-Type: application/json Content-Length: 471 @@ -2064,14 +2091,14 @@ response header the new location URL and the ETag. "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:21.512Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:23.165Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "schemaHash" : "sha1:16221eb6fd0177135b873acd78da1a221a8b621d", "doNotSync" : true } @@ -2079,8 +2106,7 @@ response header the new location URL and the ETag. The updated schema record contains three modified fields: 'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. -Registering another Metadata Schema Document --------------------------------------------- +## Registering another Metadata Schema Document The following example shows the creation of another json schema only providing mandatory fields mentioned above: @@ -2111,14 +2137,14 @@ providing mandatory fields mentioned above: "additionalProperties": false } - $ curl 'http://localhost:8040/metastore/api/v1/schemas' -i -X POST \ + $ curl 'http://localhost:8040/metastore/api/v1/schemas/' -i -X POST \ -H 'Content-Type: multipart/form-data' \ -F 'schema=@another-schema.json;type=application/xml' \ -F 'record=@another-schema-record.json;type=application/json' HTTP-wise the call looks as follows: - POST /metastore/api/v1/schemas HTTP/1.1 + POST /metastore/api/v1/schemas/ HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Host: localhost:8040 @@ -2148,34 +2174,34 @@ HTTP-wise the call looks as follows: Content-Disposition: form-data; name=record; filename=another-schema-record.json Content-Type: application/json - {"schemaId":"another_json","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"JSON","createdAt":null,"lastUpdate":null,"acl":[],"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} + {"schemaId":"another_json","pid":null,"schemaVersion":null,"label":null,"definition":null,"comment":null,"mimeType":null,"type":"JSON","createdAt":null,"lastUpdate":null,"acl":[],"licenseUri":null,"schemaDocumentUri":null,"schemaHash":null,"doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- -As Content-Type only 'application/json' is supported and should be +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v1/schemas/another_json?version=1 - ETag: "1336866933" + Location: http://localhost:8040/metastore/api/v2/schemas/another_json?version=1 + ETag: "-1491761616" Content-Type: application/json - Content-Length: 468 + Content-Length: 469 { "schemaId" : "another_json", "schemaVersion" : 1, - "mimeType" : "application/xml", + "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:21Z", - "lastUpdate" : "2023-07-12T12:45:21.727Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:23.249Z", "acl" : [ { "id" : 2, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/another_json?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/another_json?version=1", "schemaHash" : "sha1:a4bd131a0cbd65000fa5db6ca71d45379348adb4", "doNotSync" : true } @@ -2186,33 +2212,33 @@ Now there are two schemaIds registered in the metadata schema registry. Obtaining all accessible metadata schema records. - $ curl 'http://localhost:8040/metastore/api/v1/schemas' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/schemas/' -i -X GET Same for HTTP request: - GET /metastore/api/v1/schemas HTTP/1.1 + GET /metastore/api/v1/schemas/ HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata schema records. HTTP/1.1 200 OK - Content-Range: 0-9/2 + Content-Range: 0-1/2 Content-Type: application/json - Content-Length: 945 + Content-Length: 946 [ { "schemaId" : "another_json", "schemaVersion" : 1, - "mimeType" : "application/xml", + "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:21Z", - "lastUpdate" : "2023-07-12T12:45:21.727Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:23.249Z", "acl" : [ { "id" : 2, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/another_json?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/another_json?version=1", "schemaHash" : "sha1:a4bd131a0cbd65000fa5db6ca71d45379348adb4", "doNotSync" : true }, { @@ -2220,14 +2246,14 @@ As a result, you receive a list of metadata schema records. "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:21.512Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:23.165Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "schemaHash" : "sha1:16221eb6fd0177135b873acd78da1a221a8b621d", "doNotSync" : true } ] @@ -2245,7 +2271,7 @@ additional query parameters. The modified HTTP request with pagination looks like follows: - GET /metastore/api/v1/schemas?page=0&size=20 HTTP/1.1 + GET /metastore/api/v1/schemas/?page=0&size=20 HTTP/1.1 Host: localhost:8040 ### Getting a List of all Schema Records for a Specific SchemaId @@ -2253,34 +2279,34 @@ The modified HTTP request with pagination looks like follows: If you want to obtain all versions of a specific schema you may add the schemaId as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v1/schemas?schemaId=my_first_json' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/schemas/?schemaId=my_first_json' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/schemas?schemaId=my_first_json HTTP/1.1 + GET /metastore/api/v1/schemas/?schemaId=my_first_json HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata schema records in descending order. (current version first) HTTP/1.1 200 OK - Content-Range: 0-9/3 + Content-Range: 0-2/3 Content-Type: application/json - Content-Length: 1421 + Content-Length: 1420 [ { "schemaId" : "my_first_json", "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:21.512Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:23.165Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "schemaHash" : "sha1:16221eb6fd0177135b873acd78da1a221a8b621d", "doNotSync" : true }, { @@ -2288,14 +2314,14 @@ order. (current version first) "schemaVersion" : 2, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:21.276Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:23.028Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=2", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "schemaHash" : "sha1:6d65072f514d887d9fa69997d3aa067524c1c085", "doNotSync" : true }, { @@ -2303,14 +2329,14 @@ order. (current version first) "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:20.442Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:22.38Z", "acl" : [ { "id" : 1, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=1", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "schemaHash" : "sha1:a312efb2ad92b1c8ce8f6556c40e2b185c6682c4", "doNotSync" : true } ] @@ -2442,13 +2468,13 @@ to technical reasons.) HTTP/1.1 422 Unprocessable Entity Content-Type: application/problem+json - Content-Length: 376 + Content-Length: 432 { "type" : "about:blank", "title" : "Unprocessable Entity", "status" : 422, - "detail" : "400 BAD_REQUEST \"Error validating json!\n$.date ist nicht im Schema definiert und das Schema verbietet additionalProperties\n$.note ist nicht im Schema definiert und das Schema verbietet additionalProperties\"", + "detail" : "400 BAD_REQUEST \"Error validating json!\n$: Eigenschaft 'date' ist im Schema nicht definiert und das Schema lässt keine zusätzlichen Eigenschaften zu\n$: Eigenschaft 'note' ist im Schema nicht definiert und das Schema lässt keine zusätzlichen Eigenschaften zu\"", "instance" : "/metastore/api/v1/schemas/my_first_json/validate" } @@ -2507,29 +2533,29 @@ example we introduce a user called 'admin' and give him all rights. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-1097218276"' \ + -H 'If-Match: "850923140"' \ -F 'record=@schema-record4json-v4.json;type=application/json' Same for the HTTP request. PUT /metastore/api/v1/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-1097218276" + If-Match: "850923140" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=schema-record4json-v4.json Content-Type: application/json - {"schemaId":"my_first_json","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/json","type":"JSON","createdAt":"2023-07-12T12:45:20Z","lastUpdate":"2023-07-12T12:45:21.512Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"schemaDocumentUri":"http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3","schemaHash":"sha1:16221eb6fd0177135b873acd78da1a221a8b621d","doNotSync":true} + {"schemaId":"my_first_json","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/json","type":"JSON","createdAt":"2024-11-22T14:26:22Z","lastUpdate":"2024-11-22T14:26:23.165Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"licenseUri":null,"schemaDocumentUri":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3","schemaHash":"sha1:16221eb6fd0177135b873acd78da1a221a8b621d","doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- As a result, you receive 200 as HTTP status, the updated metadata schema record and the updated ETag and location in the HTTP response header. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3 - ETag: "-2006054549" + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 + ETag: "601310168" Content-Type: application/json Content-Length: 547 @@ -2538,8 +2564,8 @@ record and the updated ETag and location in the HTTP response header. "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2023-07-12T12:45:20Z", - "lastUpdate" : "2023-07-12T12:45:22.258Z", + "createdAt" : "2024-11-22T14:26:22Z", + "lastUpdate" : "2024-11-22T14:26:23.715Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2549,7 +2575,7 @@ record and the updated ETag and location in the HTTP response header. "sid" : "admin", "permission" : "ADMINISTRATE" } ], - "schemaDocumentUri" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "schemaDocumentUri" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "schemaHash" : "sha1:16221eb6fd0177135b873acd78da1a221a8b621d", "doNotSync" : true } @@ -2562,8 +2588,7 @@ After the update the following fields has changed: - acl additional ACL entry (set during update) -Metadata Management -------------------- +## Metadata Management After registration of a schema metadata may be added to MetaStore. In this section, the handling of metadata resources is explained. It all @@ -2589,6 +2614,7 @@ metadata record looks like this: "sid": "...", "permission": "..." }], + "licenseUri": "...", "metadataDocumentUri": "...", "documentHash": "..." } @@ -2603,6 +2629,8 @@ At least the following elements are expected to be provided by the user: In addition, ACL may be useful to make metadata editable by others. (This will be of interest while updating an existing metadata) +License URI is optional. It’s new since 1.5.0. + ### Register/Ingest a Metadata Record with Metadata Document The following example shows the creation of the first metadata record @@ -2629,7 +2657,7 @@ and its metadata only providing mandatory fields mentioned above: The schemaId used while registering metadata schema has to be used to link the metadata with the approbriate metadata schema. - $ curl 'http://localhost:8040/metastore/api/v1/metadata' -i -X POST \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/' -i -X POST \ -H 'Content-Type: multipart/form-data' \ -F 'record=@metadata-record4json.json;type=application/json' \ -F 'document=@metadata.json;type=application/json' @@ -2638,7 +2666,7 @@ You can see, that most of the sent metadata schema record is empty. Only schemaId and relatedResource are provided by the user. HTTP-wise the call looks as follows: - POST /metastore/api/v1/metadata HTTP/1.1 + POST /metastore/api/v1/metadata/ HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Host: localhost:8040 @@ -2646,7 +2674,7 @@ call looks as follows: Content-Disposition: form-data; name=record; filename=metadata-record4json.json Content-Type: application/json - {"id":null,"pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":null,"lastUpdate":null,"schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":1,"recordVersion":null,"acl":[],"metadataDocumentUri":null,"documentHash":null} + {"id":null,"pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":null,"lastUpdate":null,"schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":1,"recordVersion":null,"acl":[],"licenseUri":null,"metadataDocumentUri":null,"documentHash":null} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata.json Content-Type: application/json @@ -2656,28 +2684,28 @@ call looks as follows: } --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- -As Content-Type only 'application/json' is supported and should be +As Content-Type only 'multpart/form-data' is supported and should be provided. The other headers are typically set by the HTTP client. After validating the provided document, adding missing information where possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1 - ETag: "1695569906" + Location: http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 + ETag: "-1572304547" Content-Type: application/json Content-Length: 712 { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.364Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:23.866Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=1", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "identifierType" : "URL" }, "schemaVersion" : 1, @@ -2687,7 +2715,7 @@ the user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1", "documentHash" : "sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a" } @@ -2704,12 +2732,12 @@ avoid conflicts. For accessing the metadata the location URL provided before may be used. The URL is compiled by the id of the metadata and its version. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1' -i -X GET \ -H 'Accept: application/json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 HTTP/1.1 Accept: application/json Host: localhost:8040 @@ -2734,12 +2762,12 @@ The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". Then the command line looks like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 @@ -2747,20 +2775,20 @@ The linked metadata will be returned. The result is sent back to the user and will look that way: HTTP/1.1 200 OK - ETag: "1695569906" + ETag: "-1572304547" Content-Type: application/vnd.datamanager.metadata-record+json Content-Length: 712 { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.364Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:23.866Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=1", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "identifierType" : "URL" }, "schemaVersion" : 1, @@ -2770,7 +2798,7 @@ user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1", "documentHash" : "sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a" } @@ -2805,25 +2833,25 @@ mentioned before the ETag is needed: "date": "2018-07-02" } - $ curl 'http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1695569906"' \ + -H 'If-Match: "-1572304547"' \ -F 'record=@metadata-record4json-v2.json;type=application/json' \ -F 'document=@metadata-v2.json;type=application/xml' You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1 HTTP/1.1 + PUT /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1695569906" + If-Match: "-1572304547" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record4json-v2.json Content-Type: application/json - {"id":"290d4094-8b58-4f16-ba85-d0cc88a1f81e","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2023-07-12T12:45:22Z","lastUpdate":"2023-07-12T12:45:22.364Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"metadataDocumentUri":"http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} + {"id":"1607d566-7869-41a5-9c17-0f12badcebb7","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:23Z","lastUpdate":"2024-11-22T14:26:23.866Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v2.json Content-Type: application/xml @@ -2839,31 +2867,35 @@ Version number of record was incremented by one and 'lastUpdate' was also modified by the server. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=2 - ETag: "834161808" + Location: http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2 + ETag: "1226014924" Content-Type: application/json - Content-Length: 712 + Content-Length: 780 { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.565Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:24.168Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=2", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "identifierType" : "URL" }, "schemaVersion" : 2, "recordVersion" : 2, "acl" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2", "documentHash" : "sha1:1844c8057b673ae260fcc6b6ba146529b2b52771" } @@ -2876,42 +2908,46 @@ Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=2' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=2 HTTP/1.1 + GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 You will get the new metadata record with the new ETag. HTTP/1.1 200 OK - ETag: "834161808" + ETag: "1226014924" Content-Type: application/vnd.datamanager.metadata-record+json - Content-Length: 712 + Content-Length: 780 { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.565Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:24.168Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=2", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "identifierType" : "URL" }, "schemaVersion" : 2, "recordVersion" : 2, "acl" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2", "documentHash" : "sha1:1844c8057b673ae260fcc6b6ba146529b2b52771" } @@ -2938,24 +2974,24 @@ Etag. "note": "since version 3 notes are allowed" } - $ curl 'http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "834161808"' \ + -H 'If-Match: "1226014924"' \ -F 'record=@metadata-record4json-v3.json;type=application/json' \ -F 'document=@metadata-v3.json;type=application/xml' HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e HTTP/1.1 + PUT /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "834161808" + If-Match: "1226014924" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record4json-v3.json Content-Type: application/json - {"id":"290d4094-8b58-4f16-ba85-d0cc88a1f81e","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2023-07-12T12:45:22Z","lastUpdate":"2023-07-12T12:45:22.364Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"metadataDocumentUri":"http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} + {"id":"1607d566-7869-41a5-9c17-0f12badcebb7","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:23Z","lastUpdate":"2024-11-22T14:26:23.866Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v3.json Content-Type: application/xml @@ -2970,42 +3006,46 @@ HTTP-wise the call looks as follows: You will get the new metadata record. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=3 - ETag: "2104122253" + Location: http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3 + ETag: "838189446" Content-Type: application/json - Content-Length: 712 + Content-Length: 780 { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.657Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:24.299Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } Now you can access the updated metadata via the URI in the HTTP response header. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=3' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=3 HTTP/1.1 + GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3 HTTP/1.1 Host: localhost:8040 You will get the updated metadata. @@ -3049,73 +3089,81 @@ size as additional query parameters. If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?id=290d4094-8b58-4f16-ba85-d0cc88a1f81e' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?id=1607d566-7869-41a5-9c17-0f12badcebb7' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?id=290d4094-8b58-4f16-ba85-d0cc88a1f81e HTTP/1.1 + GET /metastore/api/v1/metadata/?id=1607d566-7869-41a5-9c17-0f12badcebb7 HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata records in descending order. (current version first) HTTP/1.1 200 OK - Content-Range: 0-9/3 + Content-Range: 0-2/3 Content-Type: application/json - Content-Length: 2144 + Content-Length: 2280 [ { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.657Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:24.299Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" }, { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.565Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:24.168Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=2", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "identifierType" : "URL" }, "schemaVersion" : 2, "recordVersion" : 2, "acl" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=2", - "documentHash" : "sha1:1844c8057b673ae260fcc6b6ba146529b2b52771" + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2", + "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" }, { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.364Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:23.866Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=1", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "identifierType" : "URL" }, "schemaVersion" : 1, @@ -3125,8 +3173,8 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=1", - "documentHash" : "sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a" + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1", + "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } ] #### Find by resourceId @@ -3137,40 +3185,44 @@ MetaStore may hold multiple metadata documents per resource. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?resoureId=https%3A%2F%2Frepo%2FanyResourceId' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?resoureId=https%3A%2F%2Frepo%2FanyResourceId HTTP/1.1 + GET /metastore/api/v1/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId HTTP/1.1 Host: localhost:8040 You will get the current version metadata record. HTTP/1.1 200 OK - Content-Range: 0-9/1 + Content-Range: 0-0/1 Content-Type: application/json - Content-Length: 716 + Content-Length: 784 [ { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.657Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:24.299Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } ] @@ -3180,41 +3232,45 @@ If you want to find all metadata records updated after a specific date. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A22.771448462Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A22.771448462Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z HTTP/1.1 Host: localhost:8040 You will get the current version metadata records updated ln the last 2 hours. HTTP/1.1 200 OK - Content-Range: 0-9/1 + Content-Range: 0-0/1 Content-Type: application/json - Content-Length: 716 + Content-Length: 784 [ { - "id" : "290d4094-8b58-4f16-ba85-d0cc88a1f81e", + "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2023-07-12T12:45:22Z", - "lastUpdate" : "2023-07-12T12:45:22.657Z", + "createdAt" : "2024-11-22T14:26:23Z", + "lastUpdate" : "2024-11-22T14:26:24.299Z", "schema" : { - "identifier" : "http://localhost:8040/metastore/api/v1/schemas/my_first_json?version=3", + "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" }, "schemaVersion" : 3, "recordVersion" : 3, "acl" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { "id" : 4, "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v1/metadata/290d4094-8b58-4f16-ba85-d0cc88a1f81e?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } ] @@ -3225,25 +3281,24 @@ range. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A22.771448462Z&until=2023-07-12T11%3A45%3A22.771444065Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z&until=2024-11-22T13%3A26%3A24.409014540Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata?from=2023-07-12T10%3A45%3A22.771448462Z&until=2023-07-12T11%3A45%3A22.771444065Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z&until=2024-11-22T13%3A26%3A24.409014540Z HTTP/1.1 Host: localhost:8040 You will get an empty array as no metadata record exists in the given range: HTTP/1.1 200 OK - Content-Range: 0-9/0 + Content-Range: */0 Content-Type: application/json Content-Length: 3 [ ] -Remarks on Working with Versions -================================ +# Remarks on Working with Versions While working with versions you should keep some particularities in mind. Access to version is only possible for single resources. There is diff --git a/restDocuV2.md b/restDocuV2.md new file mode 100644 index 00000000..7f07bd15 --- /dev/null +++ b/restDocuV2.md @@ -0,0 +1,4803 @@ +# Introduction + +In this documentation, the basics of the KIT Data Manager RESTful API of +the MetaStore Service are described. You will be guided through the +first steps of register an XML schema, update it. After that add an +appropriate metadata document to MetaStore which may be linked to a data +resource. + +This documentation assumes, that you have an instance of the KIT Data +Manager MetaStore repository service installed locally. If the +repository is running on another host or port you should change hostname +and/or port accordingly. Furthermore, the examples assume that you are +using the repository without authentication and authorization, which is +provided by another service. If you plan to use this optional service, +please refer to its documentation first to see how the examples in this +documentation have to be modified in order to work with authentication. +Typically, this should be achieved by simple adding an additional header +entry. + +The example structure is identical for all examples below. Each example +starts with a CURL command that can be run by copy&paste to your +console/terminal window. The second part shows the HTTP request sent to +the server including arguments and required headers. Finally, the third +block shows the response comming from the server. In between, special +characteristics of the calls are explained together with additional, +optional arguments or alternative responses. + +For technical reasons, all metadata resources shown in the examples +contain all fields, e.g. also empty lists or fields with value 'null'. +You may ignore most of them as long as they are not needed. Some of them +will be assigned by the server, others remain empty or null as long as +you don’t assign any value to them. All fields mandatory at creation +time are explained in the resource creation example. + +## Building the URL + +The URL for accessing the MetaStore REST endpoints is constructed as +follows: + +1. Protocol (e.g.: http, https) + +2. Host name (e.g.: localhost, www.example.org) + +3. Port (e.g.: 8040) + +4. Context path (e.g.: /metastore) + +5. Endpoint (e.g.: /api/v2/metadata) + +For example, to list all the schema records in your local MetaStore, you +need to run the following in your browser: +<http://localhost:8040/metastore/api/v2/schemas/> + +In former versions (< 1.3.0), no context path was provided by +default. + +# XML (Schema) + +## Schema Registration and Management + +In this first section, the handling of schema resources is explained. It +all starts with creating your first xml schema resource. The model of a +datacite record looks like this: + + { + "id" : "...", + "identifier" : { + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "givenName" : "..." + } ], + "titles" : [ { + "value" : "..." + } ], + "publisher" : "...", + "publicationYear" : "...", + "resourceType" : { + "value" : "...", + "typeGeneral" : "..." + }, + "dates" : [ { + "value" : "...", + "type" : "..." + } ], + "relatedIdentifiers" : [ { + "value" : "...", + "identifierType" : "...", + "relationType" : "..." + }} ], + "alternateIdentifiers" : [ { + "value" : "...", + "identifierType" : "..." + } ], + "version" : "...", + "rights": [ + { + "schemeId": "", + "schemeUri": "" + } + ], + "lastUpdate" : "...", + "state" : "...", + "acls" : [ { + "sid" : "...", + "permission" : "..." + } ] + } + +At least the following elements are expected to be provided by the user: + +- id: A unique label for the schema. + +- title: Any title for the schema. + +In addition, ACL may be useful to make schema readable/editable by +others. This will be of interest while accessing/updating an existing +schema.(if authorization is enabled) + +License URI is optional. It’s new since 1.4.2. + +## Registering a Metadata Schema Document + +The following example shows the creation of the first XSD schema only +providing mandatory fields mentioned above: + + schema-record-v2.json: + { + "id": "my_first_xsd", + "titles": [ + { + "value": "Title for my_first_xsd", + } + ] + } + + schema.xsd: + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:schema> + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'schema=@schema.xsd;type=application/xml' \ + -F 'record=@schema-record.json;type=application/json' + +You can see, that most of the sent datacite record is empty. Only +(schema)Id and title are provided by the user. HTTP-wise the call looks +as follows: + + POST /metastore/api/v2/schemas/ HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=schema.xsd + Content-Type: application/xml + + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + </xs:schema> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=schema-record.json + Content-Type: application/json + + {"id":"my_first_xsd","identifier":null,"creators":[],"titles":[{"id":null,"value":"Title for my_first_xsd","titleType":null,"lang":null}],"publisher":null,"publicationYear":null,"resourceType":null,"subjects":[],"contributors":[],"dates":[],"relatedIdentifiers":[],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[],"sizes":[],"formats":[],"version":null,"rights":[],"fundingReferences":[],"lastUpdate":null,"state":null,"embargoDate":null,"acls":[]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As Content-Type only 'multpart/form-data' is supported and should be +provided. The other headers are typically set by the HTTP client. After +validating the provided document, adding missing information where +possible and persisting the created resource, the result is sent back to +the user and will look that way: + + HTTP/1.1 201 Created + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1 + ETag: "1432044929" + Content-Type: application/json + Content-Length: 801 + + { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:32.58Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +What you see is, that the datacite record looks different from the +original document. Some of the elements received a value by the server. +Furthermore, you’ll find an ETag header with the current ETag of the +resource. This value is returned by POST, GET and PUT calls and must be +provided for all calls modifying the resource, e.g. POST, PUT and +DELETE, in order to avoid conflicts. + +There are two possible values for DataResource: *XML\_Schema* and +*JSON\_Schema* which depends on the format of the given schema document. +(XML in our case.) As the schema document has a defined structure +"MODEL" is used as 'typeGeneral'. + +### Getting a Datacite Schema Record + +For obtaining one datacite record you have to provide the value of the +field 'Id'. + +As 'Accept' field you have to provide +'application/vnd.datacite.org+json' otherwise you will get the landing +page of the digital object schema instead. + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X GET \ + -H 'Accept: application/vnd.datacite.org+json' + +In the actual HTTP request just access the path of the resource using +the base path and the 'schemaId'. Be aware that you also have to provide +the 'Accept' field. + + GET /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 + Accept: application/vnd.datacite.org+json + Host: localhost:8040 + +As a result, you receive the datacite record send before and again the +corresponding ETag in the HTTP response header. + + HTTP/1.1 200 OK + ETag: "1432044929" + Content-Type: application/vnd.datacite.org+json + Content-Length: 801 + + { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:32.58Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +### Getting a Metadata Schema Document + +For obtaining accessible metadata schemas you also have to provide the +'schemaId'. For accessing schema document you have to provide +'application/xml' as 'Accept' header. + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X GET \ + -H 'Accept: application/xml' + +In the actual HTTP request there is nothing special. You just access the +path of the resource using the base path and the 'schemaId'. + + GET /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 + Accept: application/xml + Host: localhost:8040 + +As a result, you receive the XSD schema send before. + + HTTP/1.1 200 OK + Content-Type: application/xml + Content-Length: 591 + Accept-Ranges: bytes + + <?xml version="1.0" encoding="UTF-8"?> + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" xmlns="http://www.example.org/schema/xsd/" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + + <xs:complexType> + + <xs:sequence> + + <xs:element name="title" type="xs:string"/> + + </xs:sequence> + + </xs:complexType> + + </xs:element> + + </xs:schema> + +### Updating a Metadata Schema Document (add mandatory 'date' field) + +Updating a metadata schema document will not break old metadata +documents. As every update results in a new version 'old' metadata +schema documents are still available. + +For updating an existing metadata schema and/or datacite record a valid +ETag is needed. The actual ETag is available via the HTTP GET call of +the datacite record (see above). Just send an HTTP POST with the updated +metadata schema document and/or datacite record. + + schema-v2.xsd: + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + <xs:element name="date" type="xs:date"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:schema> + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "1432044929"' \ + -F 'schema=@schema-v2.xsd;type=application/xml' + +HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "1432044929" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=schema-v2.xsd + Content-Type: application/xml + + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + <xs:element name="date" type="xs:date"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + </xs:schema> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive the updated datacite record of the schema +document and in the HTTP response header the new location URL and the +ETag. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2 + ETag: "1398092788" + Content-Type: application/json + Content-Length: 1011 + + { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=1", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:32.695Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +### Updating a Metadata Schema Document (add optional 'note' field) + +For updating existing metadata schema document we have to provide the +new ETag. Just send an HTTP POST with the updated metadata schema +document and/or datacite record. + + schema-v3.xsd: + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + <xs:element name="date" type="xs:date"/> + <xs:element name="note" type="xs:string" minOccurs="0"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:schema> + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "1398092788"' \ + -F 'schema=@schema-v3.xsd;type=application/xml' + +HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "1398092788" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=schema-v3.xsd + Content-Type: application/xml + + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" + xmlns="http://www.example.org/schema/xsd/" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + <xs:element name="date" type="xs:date"/> + <xs:element name="note" type="xs:string" minOccurs="0"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + </xs:schema> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive the updated datacite record of the schema +document and in the HTTP response header the new location URL and the +ETag. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 + ETag: "-393230376" + Content-Type: application/json + Content-Length: 1011 + + { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:32.731Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +The updated datacite record contains three modified fields: +'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. + +## Registering another Metadata Schema Document + +The following example shows the creation of another XSD schema only +providing mandatory fields mentioned above: + + another-schema-record.json: + { + "id": "another_xsd", + "titles": [ + { + "value": "Title for another_xsd", + } + ] + } + + another-schema.xsd: + <xs:schema targetNamespace="http://www.example.org/schema/xsd/example" + xmlns="http://www.example.org/schema/xsd/example" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="description" type="xs:string"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:schema> + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'schema=@another-schema.xsd;type=application/xml' \ + -F 'record=@another-schema-record.json;type=application/json' + +HTTP-wise the call looks as follows: + + POST /metastore/api/v2/schemas/ HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=another-schema.xsd + Content-Type: application/xml + + <xs:schema targetNamespace="http://www.example.org/schema/xsd/example" + xmlns="http://www.example.org/schema/xsd/example" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + <xs:complexType> + <xs:sequence> + <xs:element name="description" type="xs:string"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + </xs:schema> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=another-schema-record.json + Content-Type: application/json + + {"id":"another_xsd","identifier":null,"creators":[],"titles":[{"id":null,"value":"Title for another_xsd","titleType":null,"lang":null}],"publisher":null,"publicationYear":null,"resourceType":null,"subjects":[],"contributors":[],"dates":[],"relatedIdentifiers":[],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[],"sizes":[],"formats":[],"version":null,"rights":[],"fundingReferences":[],"lastUpdate":null,"state":null,"embargoDate":null,"acls":[]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As Content-Type only 'multpart/form-data' is supported and should be +provided. The other headers are typically set by the HTTP client. After +validating the provided document, adding missing information where +possible and persisting the created resource, the result is sent back to +the user and will look that way: + + HTTP/1.1 201 Created + Location: http://localhost:8040/metastore/api/v2/schemas/another_xsd?version=1 + ETag: "1370518322" + Content-Type: application/json + Content-Length: 799 + + { + "id" : "another_xsd", + "identifier" : { + "id" : 2, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 2, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 2, + "value" : "Title for another_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 2, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 2, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 2, + "value" : "another_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:32.747Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 2, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +Now there are two schemaIds registered in the metadata schema registry. + +### Getting a List of Metadata Schema Records + +For getting all accessible datacite records of schema documents type: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/' -i -X GET + +Same for HTTP request: + + GET /metastore/api/v2/schemas/ HTTP/1.1 + Host: localhost:8040 + +As a result, you receive a list of datacite records. + + HTTP/1.1 200 OK + Content-Range: 0-1/2 + Content-Type: application/json + Content-Length: 1816 + + [ { + "id" : "another_xsd", + "identifier" : { + "id" : 2, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 2, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 2, + "value" : "Title for another_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 2, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 2, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 2, + "value" : "another_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:32.747Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 2, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:32.731Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +Only the current version of each schemaId is listed. + +The header contains the field 'Content-Range" which displays delivered +indices and the maximum number of available schema records. If there are +more than 20 schemas registered you have to provide page and/or size as +additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** + +- size: Number of entries per page. + +The modified HTTP request with pagination looks like follows: + + GET /metastore/api/v2/schemas/?page=0&size=20 HTTP/1.1 + Host: localhost:8040 + +### Getting a List of all Schema Records for a Specific SchemaId + +If you want to obtain all versions of a specific schema you may add the +schemaId as a filter parameter. This may look like this: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/?schemaId=my_first_xsd' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/schemas/?schemaId=my_first_xsd HTTP/1.1 + Host: localhost:8040 + +As a result, you receive a list of datacite records in descending order. +(current version first) + + HTTP/1.1 200 OK + Content-Range: 0-2/3 + Content-Type: application/json + Content-Length: 2831 + + [ { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:32.731Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=1", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:32.695Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:32.58Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +### Getting current Version of Metadata Schema Document + +To get the current version of the metadata schema document just send an +HTTP GET with the linked 'schemaId': + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X GET \ + -H 'Accept: application/xml' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 + Accept: application/xml + Host: localhost:8040 + +As a result, you receive the XSD schema document sent before: + + HTTP/1.1 200 OK + Content-Type: application/xml + Content-Length: 767 + Accept-Ranges: bytes + + <?xml version="1.0" encoding="UTF-8"?> + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" xmlns="http://www.example.org/schema/xsd/" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + + <xs:complexType> + + <xs:sequence> + + <xs:element name="title" type="xs:string"/> + + <xs:element name="date" type="xs:date"/> + + <xs:element name="note" type="xs:string" minOccurs="0"/> + + </xs:sequence> + + </xs:complexType> + + </xs:element> + + </xs:schema> + +For accessing schema document you have to provide 'application/xml' as +'Accept' header. + +### Getting a specific Version of Metadata Schema Document + +To get a specific version of the metadata schema document just send an +HTTP GET with the linked 'schemaId' and the version number you are +looking for as query parameter: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1' -i -X GET \ + -H 'Accept: application/xml' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/schemas/my_first_xsd?version=1 HTTP/1.1 + Accept: application/xml + Host: localhost:8040 + +As a result, you receive the initial XSD schema document (version 1). + + HTTP/1.1 200 OK + Content-Type: application/xml + Content-Length: 591 + Accept-Ranges: bytes + + <?xml version="1.0" encoding="UTF-8"?> + <xs:schema targetNamespace="http://www.example.org/schema/xsd/" xmlns="http://www.example.org/schema/xsd/" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xs:element name="metadata"> + + <xs:complexType> + + <xs:sequence> + + <xs:element name="title" type="xs:string"/> + + </xs:sequence> + + </xs:complexType> + + </xs:element> + + </xs:schema> + +As before you have to provide 'application/XML' as 'Accept' header. + +### Validating Metadata Document + +Before an ingest of metadata is made the metadata should be successfully +validated. Otherwise the ingest may be rejected. Select the schema and +the schemaVersion to validate given document. + + metadata-v3.xml: + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My third XML document</example:title> + <example:date>2018-07-02</example:date> + <example:note>since version 3 notes are allowed</example:note> + </example:metadata> + +On a first step validation with the old schema will be done: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd/validate?version=1' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'document=@metadata-v3.xml;type=application/xml' + +Same for the HTTP request. The schemaVersion number is set by a query +parameter. + + POST /metastore/api/v2/schemas/my_first_xsd/validate?version=1 HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v3.xml + Content-Type: application/xml + + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My third XML document</example:title> + <example:date>2018-07-02</example:date> + <example:note>since version 3 notes are allowed</example:note> + </example:metadata> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive 422 as HTTP status and an error message holding +some information about the error. + + HTTP/1.1 422 Unprocessable Entity + Content-Type: application/problem+json + Content-Length: 314 + + { + "type" : "about:blank", + "title" : "Unprocessable Entity", + "status" : 422, + "detail" : "Validation error: cvc-complex-type.2.4.d: Invalid content was found starting with element 'example:date'. No child element is expected at this point.", + "instance" : "/metastore/api/v2/schemas/my_first_xsd/validate" + } + +The document holds a mandatory and an optional field introduced in the +second and third version of schema. Let’s try to validate with third +version of schema. Only version number will be different. (if no query +parameter is available the current version will be selected) + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd/validate' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'document=@metadata-v3.xml;type=application/xml' + +Same for the HTTP request. + + POST /metastore/api/v2/schemas/my_first_xsd/validate HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v3.xml + Content-Type: application/xml + + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My third XML document</example:title> + <example:date>2018-07-02</example:date> + <example:note>since version 3 notes are allowed</example:note> + </example:metadata> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +Everything should be fine now. As a result, you receive 204 as HTTP +status and no further content. + + HTTP/1.1 204 No Content + +### Update Metadata Schema Record + +In case of authorization it may be neccessary to update datacite record +to be accessible by others. To do so an update has to be made. In this +example we introduce a user called 'admin' and give him all rights. + + schema-record-v4.json + { + "id": "my_first_xsd", + [...] + "acls": [ + { + "id": 1, + "sid": "SELF", + "permission": "ADMINISTRATE" + }, + { + "sid": "admin", + "permission": "ADMINISTRATE" + } + ] + } + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "-393230376"' \ + -F 'record=@schema-record-v4.json;type=application/json' + +Same for the HTTP request. + + PUT /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "-393230376" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=schema-record-v4.json + Content-Type: application/json + + {"id":"my_first_xsd","identifier":{"id":1,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":1,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":1,"value":"Title for my_first_xsd","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":1,"value":"XML_Schema","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":1,"value":"2024-11-22T14:26:32Z","type":"CREATED"}],"relatedIdentifiers":[{"id":1,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=2","relationType":"IS_NEW_VERSION_OF","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":1,"value":"my_first_xsd","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"3","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:32.731Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive 200 as HTTP status, the updated datacite record +and the updated ETag and location in the HTTP response header. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 + ETag: "137544821" + Content-Type: application/json + Content-Length: 1087 + + { + "id" : "my_first_xsd", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_xsd" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "XML_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:32Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_xsd", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:33.003Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + }, { + "id" : 3, + "sid" : "admin", + "permission" : "ADMINISTRATE" + } ] + } + +After the update the following fields has changed: + +- version number increased by one. + +- lastUpdate to the date of the last update (set by server) + +- acls additional ACL entry (set during update) + +## Metadata Management + +After registration of a schema metadata may be added to MetaStore. In +this section, the handling of metadata resources is explained. It all +starts with creating your first metadata resource. The model of a +datacite record looks like this: + + { + "id" : "...", + "identifier" : { + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "givenName" : "..." + } ], + "titles" : [ { + "value" : "..." + } ], + "publisher" : "...", + "publicationYear" : "...", + "resourceType" : { + "value" : "...", + "typeGeneral" : "..." + }, + "dates" : [ { + "value" : "...", + "type" : "..." + } ], + "relatedIdentifiers" : [ { + "value" : "...", + "identifierType" : "...", + "relationType" : "..." + }} ], + "alternateIdentifiers" : [ { + "value" : "...", + "identifierType" : "..." + } ], + "version" : "...", + "rights": [ + { + "schemeId": "", + "schemeUri": "" + } + ], + "lastUpdate" : "...", + "state" : "...", + "acls" : [ { + "sid" : "...", + "permission" : "..." + } ] + } + +At least the following elements have to be provided by the user: + +- title: Any title for the metadata document + +- resourceType: *XML\_Metadata' or 'JSON\_Metadata' and type 'MODEL*. + +- relatedIdentifier/schema: Link to the related schema. + (identifierType: INTERNAL and URL are supported, relationType: + IS\_DERIVED\_FROM) + +- relatedIdentifier/data: Link to the (data) resource. + (identifierType: any, relationType: IS\_METADATA\_FOR) + +In addition, ACL may be useful to make metadata editable by others. +(This will be of interest while updating an existing metadata) + +If linked schema is identified by its schemaId the INTERNAL type has to +be used. It’s then linked to the current schema version at creation +time. + +License URI is optional. It’s new since 1.4.2. + +### Register/Ingest a Datacite Record with Metadata Document + +The following example shows the creation of the first metadata document +and its datacite record only providing mandatory fields mentioned above: + + metadata-record.json: + { + "titles": [ + { + "value": "Title of first XML metadata document", + } + ], + "resourceType": { + "value": "XML_Metadata", + "typeGeneral": "MODEL" + }, + "relatedIdentifiers": [ + { + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", + "relationType": "IS_DERIVED_FROM" + }, + { + "identifierType": "URL", + "value": "https://repo/anyResourceId", + "relationType": "IS_METADATA_FOR" + } + ] + } + + metadata.xml: + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My first XML document</example:title> + </example:metadata> + +The schemaId used while registering metadata schema has to be used to +link the metadata with the approbriate metadata schema. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'record=@metadata-record.json;type=application/json' \ + -F 'document=@metadata.xml;type=application/xml' + +You can see, that most of the sent datacite record is empty. Only +schemaId and relatedResource are provided by the user. HTTP-wise the +call looks as follows: + + POST /metastore/api/v2/metadata/ HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=metadata-record.json + Content-Type: application/json + + {"id":null,"identifier":null,"creators":[],"titles":[{"id":null,"value":"Title of first XML metadata document","titleType":null,"lang":null}],"publisher":null,"publicationYear":null,"resourceType":{"id":null,"value":"XML_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[],"relatedIdentifiers":[{"id":null,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":null,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[],"sizes":[],"formats":[],"version":null,"rights":[],"fundingReferences":[],"lastUpdate":null,"state":null,"embargoDate":null,"acls":[]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata.xml + Content-Type: application/xml + + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My first XML document</example:title> + </example:metadata> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As Content-Type only 'multpart/form-data' is supported and should be +provided. The other headers are typically set by the HTTP client. After +validating the provided document, adding missing information where +possible and persisting the created resource, the result is sent back to +the user and will look that way: + + HTTP/1.1 201 Created + Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 + ETag: "-1820103729" + Content-Type: application/json + Content-Length: 1203 + + { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:33.023Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +What you see is, that the datacite record looks different from the +original document. Some of the elements received a value by the server. +In the header you’ll find a location URL to access the ingested metadata +and an ETag with the current ETag of the resource. This value is +returned by POST, GET and PUT calls and must be provided for all calls +modifying the resource, e.g. POST, PUT and DELETE, in order to avoid +conflicts. + +### Accessing Metadata Document + +For accessing the metadata the location URL provided before may be used. +The URL is compiled by the id of the metadata and its version. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1' -i -X GET \ + -H 'Accept: application/xml' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 HTTP/1.1 + Accept: application/xml + Host: localhost:8040 + +The linked metadata will be returned. The result is sent back to the +user and will look that way: + + HTTP/1.1 200 OK + Content-Length: 198 + Accept-Ranges: bytes + Content-Type: application/xml + + <?xml version="1.0" encoding="UTF-8"?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/"> + + <example:title>My first XML document</example:title> + + </example:metadata> + +What you see is, that the metadata is untouched. + +For accessing metadata document you have to provide 'application/xml' as +'Accept' header. + +### Accessing Datacite Record of Metadata Document + +For accessing the datacite record the same URL as before has to be used. +The only difference is the content type. It has to be set to +"application/vnd.datacite.org+json". Then the command line looks like +this: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1' -i -X GET \ + -H 'Accept: application/vnd.datacite.org+json' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 HTTP/1.1 + Accept: application/vnd.datacite.org+json + Host: localhost:8040 + +The linked metadata will be returned. The result is sent back to the +user and will look that way: + + HTTP/1.1 200 OK + ETag: "-1820103729" + Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 + Content-Type: application/vnd.datacite.org+json + Content-Length: 1203 + + { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:33.023Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +You also get the datacite record seen before. + +### Updating a Datacite Record (edit ACL entries) & Metadata Document + +The following example shows the update of the datacite record and +metadata document to a newer version of the schema. As mentioned before +the ETag is needed: + + metadata-record-v2.json: + { + "id": "3efdd6fe-429d-40a6-acff-c7c40631d508", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] + "acls": [ + [...] + { + "sid": "guest", + "permission": "READ" + } + ] + } + + metadata-v2.xml: + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My second XML document</example:title> + <example:date>2018-07-02</example:date> + </example:metadata> + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "-1820103729"' \ + -F 'record=@metadata-record-v2.json;type=application/json' \ + -F 'document=@metadata-v2.xml;type=application/xml' + +You can see, that the schema was set to version 2 (allowing additional +field for date) and the ACL entry for "guest" was added. All other +properties are still the same. HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9 HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "-1820103729" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=metadata-record-v2.json + Content-Type: application/json + + {"id":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first XML metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"XML_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:33Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:33.023Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v2.xml + Content-Type: application/xml + + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My second XML document</example:title> + <example:date>2018-07-02</example:date> + </example:metadata> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +The response provides the updated datacite record: + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2 + ETag: "1547105945" + Content-Type: application/json + Content-Length: 1475 + + { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", + "relationType" : "HAS_METADATA" + }, { + "id" : 4, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:33.105Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +You will get the updated datacite record with the following changes: - +new schema (version) - an additional ACL entry - 'version' of record was +incremented by one - 'lastUpdate' was also modified by the server. + +### Updating Datacite Record of Metadata Document & Document + +Repeat the last step and update to the current version. As mentioned +before the ETag is needed. As the ETag has changed in the meanwhile you +first have to get the new ETag. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2' -i -X GET \ + -H 'Accept: application/vnd.datacite.org+json' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2 HTTP/1.1 + Accept: application/vnd.datacite.org+json + Host: localhost:8040 + +You will get the new datacite record with the new ETag. + + HTTP/1.1 200 OK + ETag: "1547105945" + Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2 + Content-Type: application/vnd.datacite.org+json + Content-Length: 1475 + + { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", + "relationType" : "HAS_METADATA" + }, { + "id" : 4, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:33.105Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +Now you can update metadata due to new version of schema using the new +Etag. + + metadata-record-v3.json: + { + "id": "3efdd6fe-429d-40a6-acff-c7c40631d508", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "INTERNAL", + "value": "my_first_xsd", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] + } + +In contrast to the previous update, the INTERNAL identifier is used. +This always refers to the latest version of the schema (in our case +version 3). + + metadata-v3.xml: + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My third XML document</example:title> + <example:date>2018-07-02</example:date> + <example:note>since version 3 notes are allowed</example:note> + </example:metadata> + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "1547105945"' \ + -F 'record=@metadata-record-v3.json;type=application/json' \ + -F 'document=@metadata-v3.xml;type=application/xml' + +HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9 HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "1547105945" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=metadata-record-v3.json + Content-Type: application/json + + {"id":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first XML metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"XML_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:33Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"INTERNAL","value":"my_first_xsd","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:33.023Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v3.xml + Content-Type: application/xml + + <?xml version='1.0' encoding='utf-8'?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/" > + <example:title>My third XML document</example:title> + <example:date>2018-07-02</example:date> + <example:note>since version 3 notes are allowed</example:note> + </example:metadata> + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +You will get the new datacite record. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=3 + ETag: "1996473594" + Content-Type: application/json + Content-Length: 1475 + + { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:33.149Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +Now you can access the updated metadata via the URI in the HTTP response +header. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=3' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=3 HTTP/1.1 + Host: localhost:8040 + +You will get the updated metadata. + + HTTP/1.1 200 OK + Content-Length: 323 + Accept-Ranges: bytes + Content-Type: application/json + + <?xml version="1.0" encoding="UTF-8"?> + <example:metadata xmlns:example="http://www.example.org/schema/xsd/"> + + <example:title>My third XML document</example:title> + + <example:date>2018-07-02</example:date> + + <example:note>since version 3 notes are allowed</example:note> + + </example:metadata> + +### Find a Datacite Record of Metadata Document + +Search will find all current datacite records. There are some filters +available which may be combined. All filters for the datacite records +are set via query parameters. The following filters are allowed: + +- id + +- resourceId + +- from + +- until + +The header contains the field 'Content-Range" which displays delivered +indices and the maximum number of available schema records. If there are +more than 20 datacite records registered you have to provide page and/or +size as additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** + +- size: Number of entries per page. + +### Getting a List of all Datacite Records for a Specific Metadata Document + +If you want to obtain all versions of a specific resource you may add +'id' as a filter parameter. This may look like this: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?id=db2379c7-1952-406d-ae69-5a9a9d39c9c9' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?id=db2379c7-1952-406d-ae69-5a9a9d39c9c9 HTTP/1.1 + Host: localhost:8040 + +As a result, you receive a list of datacite records in descending order. +(current version first) + + HTTP/1.1 200 OK + Content-Range: 0-2/3 + Content-Type: application/json + Content-Length: 4161 + + [ { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:33.149Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", + "relationType" : "HAS_METADATA" + }, { + "id" : 4, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:33.105Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:33.023Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +#### Find by resourceId + +If you want to find all records belonging to an external resource. +MetaStore may hold multiple metadata documents per resource. +(Nevertheless only one per registered schema) + +Command line: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId HTTP/1.1 + Host: localhost:8040 + +You will get the current version of the datacite record(s). + + HTTP/1.1 200 OK + Content-Range: 0-0/1 + Content-Type: application/json + Content-Length: 1479 + + [ { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:33.149Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +#### Find after a specific date + +If you want to find all datacite records updated after a specific date. + +Command line: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z HTTP/1.1 + Host: localhost:8040 + +You will get the current version datacite records updated ln the last 2 +hours. + + HTTP/1.1 200 OK + Content-Range: 0-0/1 + Content-Type: application/json + Content-Length: 1479 + + [ { + "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first XML metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "XML_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:33Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:33.149Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +#### Find in a specific date range + +If you want to find all datacite records updated in a specific date +range. + +Command line: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z&until=2024-11-22T13%3A26%3A33.209219312Z' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z&until=2024-11-22T13%3A26%3A33.209219312Z HTTP/1.1 + Host: localhost:8040 + +You will get an empty array as no datacite record exists in the given +range: + + HTTP/1.1 200 OK + Content-Range: */0 + Content-Type: application/json + Content-Length: 3 + + [ ] + +# JSON (Schema) + +## Schema Registration and Management + +In this section, the handling of json schema resources is explained. It +all starts with creating your first json schema resource. The model of a +datacite record looks like this: + + { + "schemaId" : "...", + "schemaVersion" : 1, + "mimeType" : "...", + "type" : "...", + "createdAt" : "...", + "lastUpdate" : "...", + "acl" : [ { + "id" : 1, + "sid" : "...", + "permission" : "..." + } ], + "licenseUri" : "...", + "schemaDocumentUri" : "...", + "schemaHash" : "...", + "locked" : false + } + +At least the following elements are expected to be provided by the user: + +- schemaId: A unique label for the schema. + +- mimeType: The resource type must be assigned by the user. For JSON + schemas this should be *application/json* + +- type: XML or JSON. For JSON schemas this should be *JSON* + +In addition, ACL may be useful to make schema editable by others. (This +will be of interest while updating an existing schema) + +License URI is optional. It’s new since 1.5.0. + +## Registering a Metadata Schema Document + +The following example shows the creation of the first json schema only +providing mandatory fields mentioned above: + + schema-record4json.json: + { + "schemaId" : "my_first_json", + "type" : "JSON" + } + + schema.json: + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + } + }, + "additionalProperties": false + } + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'schema=@schema.json;type=application/json' \ + -F 'record=@schema-record4json.json;type=application/json' + +You can see, that most of the sent datacite record is empty. Only +schemaId, mimeType and type are provided by the user. HTTP-wise the call +looks as follows: + + POST /metastore/api/v2/schemas/ HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=schema.json + Content-Type: application/json + + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + } + }, + "additionalProperties": false + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=schema-record4json.json + Content-Type: application/json + + {"id":"my_first_json","identifier":null,"creators":[],"titles":[{"id":null,"value":"Title for my_first_json","titleType":null,"lang":null}],"publisher":null,"publicationYear":null,"resourceType":null,"subjects":[],"contributors":[],"dates":[],"relatedIdentifiers":[],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[],"sizes":[],"formats":[],"version":null,"rights":[],"fundingReferences":[],"lastUpdate":null,"state":null,"embargoDate":null,"acls":[]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As Content-Type only 'multpart/form-data' is supported and should be +provided. The other headers are typically set by the HTTP client. After +validating the provided document, adding missing information where +possible and persisting the created resource, the result is sent back to +the user and will look that way: + + HTTP/1.1 201 Created + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1 + ETag: "-759012511" + Content-Type: application/json + Content-Length: 806 + + { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:26.664Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +What you see is, that the datacite record looks different from the +original document. Some of the elements received a value by the server. +Furthermore, you’ll find an ETag header with the current ETag of the +resource. This value is returned by POST, GET and PUT calls and must be +provided for all calls modifying the resource, e.g. POST, PUT and +DELETE, in order to avoid conflicts. + +There are two possible values for DataResource: *XML\_Schema* and +*JSON\_Schema* which depends on the format of the given schema document. +(JSON in our case.) As the schema document has a defined structure +"MODEL" is used as 'typeGeneral'. + +### Getting a Datacite Schema Record + +For obtaining one datacite record you have to provide the value of the +field 'schemaId'. + +As 'Accept' field you have to provide +'application/vnd.datacite.org+json' otherwise you will get the landing +page of the digital object instead. + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X GET \ + -H 'Accept: application/vnd.datacite.org+json' + +In the actual HTTP request just access the path of the resource using +the base path and the 'schemaId'. Be aware that you also have to provide +the 'Accept' field. + + GET /metastore/api/v2/schemas/my_first_json HTTP/1.1 + Accept: application/vnd.datacite.org+json + Host: localhost:8040 + +As a result, you receive the datacite record send before and again the +corresponding ETag in the HTTP response header. + + HTTP/1.1 200 OK + ETag: "-759012511" + Content-Type: application/vnd.datacite.org+json + Content-Length: 806 + + { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:26.664Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +### Getting a Metadata Schema Document + +For obtaining accessible metadata schemas you also have to provide the +'schemaId'. For accessing schema document you have to provide +'application/xml' as 'Accept' header. + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X GET \ + -H 'Accept: application/json' + +In the actual HTTP request there is nothing special. You just access the +path of the resource using the base path and the 'schemaId'. + + GET /metastore/api/v2/schemas/my_first_json HTTP/1.1 + Accept: application/json + Host: localhost:8040 + +As a result, you receive the XSD schema send before. + + HTTP/1.1 200 OK + Content-Type: text/plain + Content-Length: 388 + Accept-Ranges: bytes + + { + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "$id" : "http://www.example.org/schema/json", + "type" : "object", + "title" : "Json schema for tests", + "default" : { }, + "required" : [ "title" ], + "properties" : { + "title" : { + "type" : "string", + "title" : "Title", + "description" : "Title of object." + } + }, + "additionalProperties" : false + } + +### Updating a Metadata Schema Document (add mandatory 'date' field) + +Updating a metadata schema document will not break old metadata +documents. As every update results in a new version 'old' metadata +schema documents are still available. + +For updating an existing metadata schema (record) a valid ETag is +needed. The actual ETag is available via the HTTP GET call of the +datacite record (see above). Just send an HTTP POST with the updated +metadata schema document and/or datacite record. + + schema-v2.json: + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title", + "date" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + }, + "date": { + "type": "string", + "format": "date", + "title": "Date", + "description": "Date of object" + } + }, + "additionalProperties": false + } + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "-759012511"' \ + -F 'schema=@schema-v2.json;type=application/json' + +HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/schemas/my_first_json HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "-759012511" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=schema-v2.json + Content-Type: application/json + + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title", + "date" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + }, + "date": { + "type": "string", + "format": "date", + "title": "Date", + "description": "Date of object" + } + }, + "additionalProperties": false + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive the updated datacite record of the schema +document and in the HTTP response header the new location URL and the +ETag. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2 + ETag: "575519683" + Content-Type: application/json + Content-Length: 1016 + + { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=1", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:26.854Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +### Updating a Metadata Schema Document (add optional 'note' field) + +For updating existing metadata schema document we have to provide the +new ETag. Just send an HTTP POST with the updated metadata schema +document and/or datacite record. + + schema-v3.json: + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title", + "date" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + }, + "date": { + "type": "string", + "format": "date", + "title": "Date", + "description": "Date of object" + }, + "note": { + "type": "string", + "title": "Note", + "description": "Additonal information about object" + } + }, + "additionalProperties": false + } + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "575519683"' \ + -F 'schema=@schema-v3.json;type=application/json' + +HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/schemas/my_first_json HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "575519683" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=schema-v3.json + Content-Type: application/json + + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json", + "type": "object", + "title": "Json schema for tests", + "default": {}, + "required": [ + "title", + "date" + ], + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Title of object." + }, + "date": { + "type": "string", + "format": "date", + "title": "Date", + "description": "Date of object" + }, + "note": { + "type": "string", + "title": "Note", + "description": "Additonal information about object" + } + }, + "additionalProperties": false + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive the updated datacite record of the schema +document and in the HTTP response header the new location URL and the +ETag. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 + ETag: "254981415" + Content-Type: application/json + Content-Length: 1015 + + { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:26.94Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +The updated datacite record contains three modified fields: +'schemaVersion', 'lastUpdate' and 'schemaDocumentUri'. + +## Registering another Metadata Schema Document + +The following example shows the creation of another json schema only +providing mandatory fields mentioned above: + + another-schema-record4json.json: + { + "schemaId" : "another_json", + "type" : "JSON" + } + + another-schema.json: + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json/example", + "type": "object", + "title": "Another Json schema for tests", + "default": {}, + "required": [ + "description" + ], + "properties": { + "description": { + "type": "string", + "title": "Description", + "description": "Any description." + } + }, + "additionalProperties": false + } + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'schema=@another-schema.json;type=application/xml' \ + -F 'record=@another-schema-record.json;type=application/json' + +HTTP-wise the call looks as follows: + + POST /metastore/api/v2/schemas/ HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=schema; filename=another-schema.json + Content-Type: application/xml + + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://www.example.org/schema/json/example", + "type": "object", + "title": "Another Json schema for tests", + "default": {}, + "required": [ + "description" + ], + "properties": { + "description": { + "type": "string", + "title": "Description", + "description": "Any description." + } + }, + "additionalProperties": false + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=another-schema-record.json + Content-Type: application/json + + {"id":"another_json","identifier":null,"creators":[],"titles":[{"id":null,"value":"Title for another_json","titleType":null,"lang":null}],"publisher":null,"publicationYear":null,"resourceType":null,"subjects":[],"contributors":[],"dates":[],"relatedIdentifiers":[],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[],"sizes":[],"formats":[],"version":null,"rights":[],"fundingReferences":[],"lastUpdate":null,"state":null,"embargoDate":null,"acls":[]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As Content-Type only 'multpart/form-data' is supported and should be +provided. The other headers are typically set by the HTTP client. After +validating the provided document, adding missing information where +possible and persisting the created resource, the result is sent back to +the user and will look that way: + + HTTP/1.1 201 Created + Location: http://localhost:8040/metastore/api/v2/schemas/another_json?version=1 + ETag: "-1932760524" + Content-Type: application/json + Content-Length: 799 + + { + "id" : "another_json", + "identifier" : { + "id" : 2, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 2, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 2, + "value" : "Title for another_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 2, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 2, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 2, + "value" : "another_json", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:27Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 2, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +Now there are two schemaIds registered in the metadata schema registry. + +### Getting a List of Metadata Schema Records + +For getting all accessible datacite records of schema documents type: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/' -i -X GET + +Same for HTTP request: + + GET /metastore/api/v2/schemas/ HTTP/1.1 + Host: localhost:8040 + +As a result, you receive a list of datacite records. + + HTTP/1.1 200 OK + Content-Range: 0-1/2 + Content-Type: application/json + Content-Length: 1820 + + [ { + "id" : "another_json", + "identifier" : { + "id" : 2, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 2, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 2, + "value" : "Title for another_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 2, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 2, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 2, + "value" : "another_json", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:27Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 2, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:26.94Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +Only the current version of each schemaId is listed. + +The header contains the field 'Content-Range" which displays delivered +indices and the maximum number of available schema records. If there are +more than 20 schemas registered you have to provide page and/or size as +additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** + +- size: Number of entries per page. + +The modified HTTP request with pagination looks like follows: + + GET /metastore/api/v2/schemas/?page=0&size=20 HTTP/1.1 + Host: localhost:8040 + +### Getting a List of all Schema Records for a Specific SchemaId + +If you want to obtain all versions of a specific schema you may add the +schemaId as a filter parameter. This may look like this: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/?schemaId=my_first_json' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/schemas/?schemaId=my_first_json HTTP/1.1 + Host: localhost:8040 + +As a result, you receive a list of datacite records in descending order. +(current version first) + + HTTP/1.1 200 OK + Content-Range: 0-2/3 + Content-Type: application/json + Content-Length: 2845 + + [ { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:26.94Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=1", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:26.854Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:26.664Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +### Getting current Version of Metadata Schema Document + +To get the current version of the metadata schema document just send an +HTTP GET with the linked 'schemaId': + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X GET \ + -H 'Accept: application/json' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/schemas/my_first_json HTTP/1.1 + Accept: application/json + Host: localhost:8040 + +As a result, you receive the XSD schema document sent before: + + HTTP/1.1 200 OK + Content-Type: text/plain + Content-Length: 661 + Accept-Ranges: bytes + + { + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "$id" : "http://www.example.org/schema/json", + "type" : "object", + "title" : "Json schema for tests", + "default" : { }, + "required" : [ "title", "date" ], + "properties" : { + "title" : { + "type" : "string", + "title" : "Title", + "description" : "Title of object." + }, + "date" : { + "type" : "string", + "format" : "date", + "title" : "Date", + "description" : "Date of object" + }, + "note" : { + "type" : "string", + "title" : "Note", + "description" : "Additonal information about object" + } + }, + "additionalProperties" : false + } + +For accessing schema document you have to provide 'application/json' as +'Accept' header. + +### Getting a specific Version of Metadata Schema Document + +To get a specific version of the metadata schema document just send an +HTTP GET with the linked 'schemaId' and the version number you are +looking for as query parameter: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1' -i -X GET \ + -H 'Accept: application/json' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/schemas/my_first_json?version=1 HTTP/1.1 + Accept: application/json + Host: localhost:8040 + +As a result, you receive the initial XSD schema document (version 1). + + HTTP/1.1 200 OK + Content-Type: text/plain + Content-Length: 388 + Accept-Ranges: bytes + + { + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "$id" : "http://www.example.org/schema/json", + "type" : "object", + "title" : "Json schema for tests", + "default" : { }, + "required" : [ "title" ], + "properties" : { + "title" : { + "type" : "string", + "title" : "Title", + "description" : "Title of object." + } + }, + "additionalProperties" : false + } + +As before you have to provide 'application/json' as 'Accept' header. + +### Validating Metadata Document + +Before an ingest of metadata is made the metadata should be successfully +validated. Otherwise the ingest may be rejected. Select the schema and +the schemaVersion to validate given document. + + metadata-v3.json: + { + "title": "My third JSON document", + "date": "2018-07-02", + "note": "since version 3 notes are allowed" + } + +On a first step validation with the old schema will be done: + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json/validate?version=1' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'document=@metadata-v3.json;type=application/json' + +Same for the HTTP request. The schemaVersion number is set by a query +parameter. + + POST /metastore/api/v2/schemas/my_first_json/validate?version=1 HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v3.json + Content-Type: application/json + + { + "title": "My third JSON document", + "date": "2018-07-02", + "note": "since version 3 notes are allowed" + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive 422 as HTTP status and an error message holding +some information about the error. + + HTTP/1.1 422 Unprocessable Entity + Content-Type: application/problem+json + Content-Length: 432 + + { + "type" : "about:blank", + "title" : "Unprocessable Entity", + "status" : 422, + "detail" : "400 BAD_REQUEST \"Error validating json!\n$: Eigenschaft 'date' ist im Schema nicht definiert und das Schema lässt keine zusätzlichen Eigenschaften zu\n$: Eigenschaft 'note' ist im Schema nicht definiert und das Schema lässt keine zusätzlichen Eigenschaften zu\"", + "instance" : "/metastore/api/v2/schemas/my_first_json/validate" + } + +The document holds a mandatory and an optional field introduced in the +second and third version of schema. Let’s try to validate with third +version of schema. Only version number will be different. (if no query +parameter is available the current version will be selected) + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json/validate' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'document=@metadata-v3.json;type=application/json' + +Same for the HTTP request. + + POST /metastore/api/v2/schemas/my_first_json/validate HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v3.json + Content-Type: application/json + + { + "title": "My third JSON document", + "date": "2018-07-02", + "note": "since version 3 notes are allowed" + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +Everything should be fine now. As a result, you receive 204 as HTTP +status and no further content. + + HTTP/1.1 204 No Content + +### Update Metadata Schema Record + +In case of authorization it may be neccessary to update datacite record +to be accessible by others. To do so an update has to be made. In this +example we introduce a user called 'admin' and give him all rights. + + schema-record4json-v4.json + { + "id": "my_first_json", + [...] + "acls": [ + { + "id": 1, + "sid": "SELF", + "permission": "ADMINISTRATE" + }, + { + "sid": "admin", + "permission": "ADMINISTRATE" + } + ] + } + + $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "254981415"' \ + -F 'record=@schema-record4json-v4.json;type=application/json' + +Same for the HTTP request. + + PUT /metastore/api/v2/schemas/my_first_json HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "254981415" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=schema-record4json-v4.json + Content-Type: application/json + + {"id":"my_first_json","identifier":{"id":1,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":1,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":1,"value":"Title for my_first_json","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":1,"value":"JSON_Schema","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":1,"value":"2024-11-22T14:26:26Z","type":"CREATED"}],"relatedIdentifiers":[{"id":1,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=2","relationType":"IS_NEW_VERSION_OF","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":1,"value":"my_first_json","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"3","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:26.94Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As a result, you receive 200 as HTTP status, the updated datacite record +and the updated ETag and location in the HTTP response header. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 + ETag: "395896196" + Content-Type: application/json + Content-Length: 1092 + + { + "id" : "my_first_json", + "identifier" : { + "id" : 1, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 1, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 1, + "value" : "Title for my_first_json" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 1, + "value" : "JSON_Schema", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 1, + "value" : "2024-11-22T14:26:26Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 1, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=2", + "relationType" : "IS_NEW_VERSION_OF" + } ], + "alternateIdentifiers" : [ { + "id" : 1, + "value" : "my_first_json", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:27.409Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 1, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + }, { + "id" : 3, + "sid" : "admin", + "permission" : "ADMINISTRATE" + } ] + } + +After the update the following fields has changed: + +- version number increased by one. + +- lastUpdate to the date of the last update (set by server) + +- acls additional ACL entry (set during update) + +## Metadata Management + +After registration of a schema metadata may be added to MetaStore. In +this section, the handling of metadata resources is explained. It all +starts with creating your first metadata resource. The model of a +datacite record is similar to the record of the schema document: + + { + "id" : "...", + "identifier" : { + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "givenName" : "..." + } ], + "titles" : [ { + "value" : "..." + } ], + "publisher" : "...", + "publicationYear" : "...", + "resourceType" : { + "value" : "...", + "typeGeneral" : "..." + }, + "dates" : [ { + "value" : "...", + "type" : "..." + } ], + "relatedIdentifiers" : [ { + "value" : "...", + "identifierType" : "...", + "relationType" : "..." + }} ], + "alternateIdentifiers" : [ { + "value" : "...", + "identifierType" : "..." + } ], + "version" : "...", + "rights": [ + { + "schemeId": "", + "schemeUri": "" + } + ], + "lastUpdate" : "...", + "state" : "...", + "acls" : [ { + "sid" : "...", + "permission" : "..." + } ] + } + +At least the following elements have to be provided by the user: + +- title: Any title for the metadata document + +- resourceType: *XML\_Metadata' or 'JSON\_Metadata' and type 'MODEL*. + +- relatedIdentifier/schema: Link to the related schema. + (identifierType: INTERNAL and URL are supported, relationType: + IS\_DERIVED\_FROM) + +- relatedIdentifier/data: Link to the (data) resource. + (identifierType: any, relationType: IS\_METADATA\_FOR) + +In addition, ACL may be useful to make metadata editable by others. +(This will be of interest while updating an existing metadata) + +If linked schema is identified by its schemaId the INTERNAL type has to +be used. It’s then linked to the current schema version at creation +time. + +License URI is optional. It’s new since 1.4.2. + +### Register/Ingest a Datacite Record with Metadata Document + +The following example shows the creation of the first metadata document +and its datacite record only providing mandatory fields mentioned above: + + metadata-record4json.json: + { + "titles": [ + { + "value": "Title of first metadata document", + } + ], + "publisher": null, + "publicationYear": null, + "resourceType": { + "value": "JSON_Metadata", + "typeGeneral": "MODEL" + }, + "relatedIdentifiers": [ + { + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", + "relationType": "IS_DERIVED_FROM" + }, + { + "identifierType": "URL", + "value": "https://repo/anyResourceId", + "relationType": "IS_METADATA_FOR" + } + ] + } + + metadata.json: + { + "title": "My first JSON document" + } + +The schemaId used while registering metadata schema has to be used to +link the metadata with the approbriate metadata schema. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/' -i -X POST \ + -H 'Content-Type: multipart/form-data' \ + -F 'record=@metadata-record4json.json;type=application/json' \ + -F 'document=@metadata.json;type=application/json' + +You can see, that most of the sent datacite record is empty. Only +schemaId and relatedResource are provided by the user. HTTP-wise the +call looks as follows: + + POST /metastore/api/v2/metadata/ HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=metadata-record4json.json + Content-Type: application/json + + {"id":null,"identifier":null,"creators":[],"titles":[{"id":null,"value":"Title of first JSON metadata document","titleType":null,"lang":null}],"publisher":null,"publicationYear":null,"resourceType":{"id":null,"value":"JSON_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[],"relatedIdentifiers":[{"id":null,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":null,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[],"sizes":[],"formats":[],"version":null,"rights":[],"fundingReferences":[],"lastUpdate":null,"state":null,"embargoDate":null,"acls":[]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata.json + Content-Type: application/json + + { + "title": "My first JSON document" + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +As Content-Type only 'multpart/form-data' is supported and should be +provided. The other headers are typically set by the HTTP client. After +validating the provided document, adding missing information where +possible and persisting the created resource, the result is sent back to +the user and will look that way: + + HTTP/1.1 201 Created + Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 + ETag: "536402562" + Content-Type: application/json + Content-Length: 1205 + + { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:27.43Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +What you see is, that the datacite record looks different from the +original document. Some of the elements received a value by the server. +In the header you’ll find a location URL to access the ingested metadata +and an ETag with the current ETag of the resource. This value is +returned by POST, GET and PUT calls and must be provided for all calls +modifying the resource, e.g. POST, PUT and DELETE, in order to avoid +conflicts. + +### Accessing Metadata Document + +For accessing the metadata the location URL provided before may be used. +The URL is compiled by the id of the metadata and its version. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1' -i -X GET \ + -H 'Accept: application/json' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 HTTP/1.1 + Accept: application/json + Host: localhost:8040 + +The linked metadata will be returned. The result is sent back to the +user and will look that way: + + HTTP/1.1 200 OK + Content-Length: 40 + Accept-Ranges: bytes + Content-Type: application/json + + { + "title" : "My first JSON document" + } + +What you see is, that the metadata is untouched. + +For accessing metadata document you have to provide *application/json* +as 'Accept' header. + +### Accessing Datacite Record of Metadata Document + +For accessing the datacite record the same URL as before has to be used. +The only difference is the content type. It has to be set to +"application/vnd.datacite.org+json". Then the command line looks like +this: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1' -i -X GET \ + -H 'Accept: application/vnd.datacite.org+json' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 HTTP/1.1 + Accept: application/vnd.datacite.org+json + Host: localhost:8040 + +The linked metadata will be returned. The result is sent back to the +user and will look that way: + + HTTP/1.1 200 OK + ETag: "536402562" + Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 + Content-Type: application/vnd.datacite.org+json + Content-Length: 1205 + + { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:27.43Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +You also get the datacite record seen before. + +### Updating a Datacite Record of Metadata Document (edit ACL entries) + +The following example shows the update of the datacite record. As +mentioned before the ETag is needed: + + metadata-record4json-v2.json: + { + "id": "d5439ccf-af4c-4727-b45b-1aa8b949d60e", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "URL", + "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] + "acls": [ + [...] + { + "sid": "guest", + "permission": "READ" + } + ] + } + + metadata-v2.json: + { + "title": "My second JSON document", + "date": "2018-07-02" + } + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "536402562"' \ + -F 'record=@metadata-record4json-v2.json;type=application/json' \ + -F 'document=@metadata-v2.json;type=application/xml' + +You can see, that the schema was set to version 2 (allowing additional +field for date) and the ACL entry for "guest" was added. All other +properties are still the same. HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "536402562" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=metadata-record4json-v2.json + Content-Type: application/json + + {"id":"a7484515-ba87-46e2-8a21-a1600039346d","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first JSON metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"JSON_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:27Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"a7484515-ba87-46e2-8a21-a1600039346d","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:27.43Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v2.json + Content-Type: application/xml + + { + "title": "My second JSON document", + "date": "2018-07-02" + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +The response provides the updated datacite record: + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2 + ETag: "2057586597" + Content-Type: application/json + Content-Length: 1478 + + { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 4, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:27.556Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +You will get the updated datacite record with the following changes: - +new schema (version) - an additional ACL entry - 'version' of record was +incremented by one - 'lastUpdate' was also modified by the server. + +### Updating Datacite Record of Metadata Document & Document + +Repeat the last step and update to the current version. As mentioned +before the ETag is needed. As the ETag has changed in the meanwhile you +first have to get the new ETag. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2' -i -X GET \ + -H 'Accept: application/vnd.datacite.org+json' + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2 HTTP/1.1 + Accept: application/vnd.datacite.org+json + Host: localhost:8040 + +You will get the new datacite record with the new ETag. + + HTTP/1.1 200 OK + ETag: "2057586597" + Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2 + Content-Type: application/vnd.datacite.org+json + Content-Length: 1478 + + { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 4, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:27.556Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +Now you can update metadata due to new version of schema using the new +Etag. + + metadata-record4json-v3.json: + { + "id": "d5439ccf-af4c-4727-b45b-1aa8b949d60e", + [...] + "relatedIdentifiers": [ + [...] + { + "id": 1, + "identifierType": "INTERNAL", + "value": "my_first_json", + "relationType": "IS_DERIVED_FROM" + } + ], + [...] + "acls": [ + [...] + { + "sid": "guest", + "permission": "READ" + } + ] + } + +In contrast to the previous update, the INTERNAL identifier is used. +This always refers to the latest version of the schema (in our case +version 3). + + metadata-v3.json: + { + "title": "My third JSON document", + "date": "2018-07-02", + "note": "since version 3 notes are allowed" + } + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d' -i -X PUT \ + -H 'Content-Type: multipart/form-data' \ + -H 'If-Match: "2057586597"' \ + -F 'record=@metadata-record4json-v3.json;type=application/json' \ + -F 'document=@metadata-v3.json;type=application/json' + +HTTP-wise the call looks as follows: + + PUT /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d HTTP/1.1 + Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + If-Match: "2057586597" + Host: localhost:8040 + + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=record; filename=metadata-record4json-v3.json + Content-Type: application/json + + {"id":"a7484515-ba87-46e2-8a21-a1600039346d","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first JSON metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"JSON_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:27Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"a7484515-ba87-46e2-8a21-a1600039346d","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:27.43Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm + Content-Disposition: form-data; name=document; filename=metadata-v3.json + Content-Type: application/json + + { + "title": "My third JSON document", + "date": "2018-07-02", + "note": "since version 3 notes are allowed" + } + --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- + +You will get the new datacite record. + + HTTP/1.1 200 OK + Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=3 + ETag: "1038379398" + Content-Type: application/json + Content-Length: 1478 + + { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:27.626Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } + +Now you can access the updated metadata via the URI in the HTTP response +header. + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=3' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=3 HTTP/1.1 + Host: localhost:8040 + +You will get the updated metadata. + + HTTP/1.1 200 OK + Content-Length: 113 + Accept-Ranges: bytes + Content-Type: application/json + + { + "title" : "My third JSON document", + "date" : "2018-07-02", + "note" : "since version 3 notes are allowed" + } + +### Find a Datacite Record of Metadata Document + +Search will find all current datacite records. There are some filters +available which may be combined. All filters for the datacite records +are set via query parameters. The following filters are allowed: + +- id + +- resourceId + +- from + +- until + +The header contains the field 'Content-Range" which displays delivered +indices and the maximum number of available schema records. If there are +more than 20 datacite records registered you have to provide page and/or +size as additional query parameters. + +- page: Number of the page you want to get **(starting with page 0)** + +- size: Number of entries per page. + +### Getting a List of all Datacite Records for a Specific Metadata Document + +If you want to obtain all versions of a specific resource you may add +'id' as a filter parameter. This may look like this: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?id=a7484515-ba87-46e2-8a21-a1600039346d' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?id=a7484515-ba87-46e2-8a21-a1600039346d HTTP/1.1 + Host: localhost:8040 + +As a result, you receive a list of datacite records in descending order. +(current version first) + + HTTP/1.1 200 OK + Content-Range: 0-2/3 + Content-Type: application/json + Content-Length: 4169 + + [ { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:27.626Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 4, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "2", + "lastUpdate" : "2024-11-22T14:26:27.556Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 5, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + }, { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "1", + "lastUpdate" : "2024-11-22T14:26:27.43Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +#### Find by resourceId + +If you want to find all records belonging to an external resource. +MetaStore may hold multiple metadata documents per resource. +(Nevertheless only one per registered schema) + +Command line: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?resoureId=https%3A%2F%2Frepo%2FanyResourceId HTTP/1.1 + Host: localhost:8040 + +You will get the current version datacite record. + + HTTP/1.1 200 OK + Content-Range: 0-0/1 + Content-Type: application/json + Content-Length: 1482 + + [ { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:27.626Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +#### Find after a specific date + +If you want to find all datacite records updated after a specific date. + +Command line: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z HTTP/1.1 + Host: localhost:8040 + +You will get the current version datacite records updated ln the last 2 +hours. + + HTTP/1.1 200 OK + Content-Range: 0-0/1 + Content-Type: application/json + Content-Length: 1482 + + [ { + "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifier" : { + "id" : 3, + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "id" : 3, + "givenName" : "SELF" + } ], + "titles" : [ { + "id" : 3, + "value" : "Title of first JSON metadata document" + } ], + "publisher" : "SELF", + "publicationYear" : "2024", + "resourceType" : { + "id" : 3, + "value" : "JSON_Metadata", + "typeGeneral" : "MODEL" + }, + "dates" : [ { + "id" : 3, + "value" : "2024-11-22T14:26:27Z", + "type" : "CREATED" + } ], + "relatedIdentifiers" : [ { + "id" : 2, + "identifierType" : "URL", + "value" : "https://repo/anyResourceId", + "relationType" : "IS_METADATA_FOR" + }, { + "id" : 5, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "relationType" : "IS_NEW_VERSION_OF" + }, { + "id" : 3, + "identifierType" : "URL", + "value" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", + "relationType" : "HAS_METADATA" + } ], + "alternateIdentifiers" : [ { + "id" : 3, + "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "identifierType" : "INTERNAL" + } ], + "version" : "3", + "lastUpdate" : "2024-11-22T14:26:27.626Z", + "state" : "VOLATILE", + "acls" : [ { + "id" : 6, + "sid" : "guest", + "permission" : "READ" + }, { + "id" : 4, + "sid" : "SELF", + "permission" : "ADMINISTRATE" + } ] + } ] + +#### Find in a specific date range + +If you want to find all datacite records updated in a specific date +range. + +Command line: + + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z&until=2024-11-22T13%3A26%3A27.730516191Z' -i -X GET + +HTTP-wise the call looks as follows: + + GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z&until=2024-11-22T13%3A26%3A27.730516191Z HTTP/1.1 + Host: localhost:8040 + +You will get an empty array as no datacite record exists in the given +range: + + HTTP/1.1 200 OK + Content-Range: */0 + Content-Type: application/json + Content-Length: 3 + + [ ] + +# Remarks on Working with Versions + +While working with versions you should keep some particularities in +mind. Access to version is only possible for single resources. There is +e.g. no way to obtain all resources in version 2 from the server. If a +specific version of a resource is returned, the obtained ETag also +relates to this specific version. Therefore, you should NOT use this +ETag for any update operation as the operation will fail with response +code 412 (PRECONDITION FAILED). Consequently, it is also NOT allowed to +modify a format version of a resource. If you want to rollback to a +previous version, you should obtain the resource and submit a PUT +request of the entire document which will result in a new version equal +to the previous state unless there were changes you are not allowed to +apply (anymore), e.g. if permissions have changed. From 27912d11ae21e8dafe79baabc88a5364cb319597 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Nov 2024 17:17:10 +0100 Subject: [PATCH 170/181] Update CHANGELOG.md --- CHANGELOG.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b073a1..a082df32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -## [1.4.4] 2024-08-20 +## [2.0.0] - 2024-11-25 +### Security + +### Added +- API V2 + +### Changed +- DataCite is now supported as administrative metadata (API V2) +- Update dependency gradle to v8.11.1 +- Update rabbitmq Docker tag to v4 +- Update eclipse-temurin Docker tag to v23 + +### Libs +- Update dependency ajv to v8.17.1 +- Update dependency com.google.errorprone:error_prone_core to v2.36.0 +- Update dependency com.networknt:json-schema-validator to v1.5.3 +- Update dependency commons-io:commons-io to v2.18.0 +- Update dependency edu.kit.datamanager:repo-core to v1.2.3 +- Update dependency edu.kit.datamanager:service-base to v1.3.2 +- Update plugin io.freefair.lombok to v8.11 +- Update plugin io.freefair.maven-publish-java to v8.11 +- Update dependency jacoco to v0.8.12 +- Update dependency org.javers:javers-core to v7.7.0 +- Update plugin net.ltgt.errorprone to v4.1.0 +- Update dependency org.apache.tika:tika-core to v3 +- Update plugin org.owasp.dependencycheck to v11 +- Update dependency org.mockito:mockito-core to v5.14.2 +- Bump org.postgresql:postgresql from 42.7.3 to 42.7.4 +- Update plugin org.springframework.boot to v3.3.6 +- Update dependency org.springframework.data:spring-data-elasticsearch to v5.4.0 +- Update dependency org.springframework.restdocs:spring-restdocs-mockmvc to v3.0.3 +- Update dependency org.springframework:spring-messaging to v6.2.0 +- Update dependency org.springframework.cloud:spring-cloud-starter-config to v4.1.3 +- Update dependency org.springframework.cloud:spring-cloud-starter-netflix-eureka-client to v4.1.3 +- Bump org.springframework.data:spring-data-elasticsearch from 5.3.3 to 5.3.4. + +### Deprecated +- API V1 + +## [1.4.4] - 2024-08-20 ### Changed - Bump gradle from 8.7 to 8.8. @@ -488,6 +527,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Registry for XSD files and support for XML metadata [Unreleased]: https://github.com/kit-data-manager/metastore2/compare/v1.4.4...HEAD +[2.0.0]: https://github.com/kit-data-manager/metastore2/compare/v1.4.4...v2.0.0 [1.4.4]: https://github.com/kit-data-manager/metastore2/compare/v1.4.3...v1.4.4 [1.4.3]: https://github.com/kit-data-manager/metastore2/compare/v1.4.2...v1.4.3 [1.4.2]: https://github.com/kit-data-manager/metastore2/compare/v1.4.1...v1.4.2 From 8be59d15aa6999468ae8fa9c24733df1ca78f3f6 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 22 Nov 2024 17:19:57 +0100 Subject: [PATCH 171/181] Update Java in GitHub actions. --- .github/workflows/gradle.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 80737eb6..a766a0e1 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,13 +5,13 @@ name: build with gradle on: push: - branches: [ master, main, testWorkflow, NEP ] + branches: [ main, testWorkflow, NEP ] pull_request: - branches: [ master, main, development, NEP ] + branches: [ main, development, NEP ] env: # JDK version used for building jar file - currentBuildVersion: 17 + currentBuildVersion: 21 jobs: build: runs-on: ${{ matrix.operating-system }} @@ -19,7 +19,7 @@ jobs: matrix: operating-system: [ubuntu-latest, macOS-latest, windows-latest] # Use both LTS releases and latest one for tests - jdk: [ 17, 19 ] + jdk: [ 21, 23 ] steps: - name: Checkout repo uses: actions/checkout@v4 From d9105a5f2a384c96786af36f2b425fdcc10ecef5 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 25 Nov 2024 14:11:32 +0100 Subject: [PATCH 172/181] Message now contains complete URL like before. Determining indices and ids will now be managed by indexing-service. --- .../messaging/MetadataResourceMessage.java | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java index 996b740b..54401803 100644 --- a/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java +++ b/src/main/java/edu/kit/datamanager/entities/messaging/MetadataResourceMessage.java @@ -103,12 +103,10 @@ public static MetadataResourceMessage createMessage(MetadataRecord metadataRecor MetadataResourceMessage msg = new MetadataResourceMessage(); Map<String, String> properties = new HashMap<>(); if (metadataRecord != null) { - properties.put(RESOLVING_URL_PROPERTY, removeFilterFromUri(metadataRecord.getMetadataDocumentUri())); - // remove base path of endpoint from URL e.g.: http://x.y.z/metastore/api/v2/schemas/id_of_schema -> id_of_schema + String metadataDocumentUri = metadataRecord.getMetadataDocumentUri(); String schemaDocumentUri = metadataRecord.getSchema().getIdentifier(); - String[] split = schemaDocumentUri.split("\\/", -1); - String schemaId = removeFilterFromUri(split[split.length - 1]); - properties.put(DOCUMENT_TYPE_PROPERTY, schemaId); + properties.put(RESOLVING_URL_PROPERTY, metadataDocumentUri); + properties.put(DOCUMENT_TYPE_PROPERTY, schemaDocumentUri); msg.setEntityId(metadataRecord.getId()); } @@ -165,12 +163,9 @@ public static MetadataResourceMessage createMessage(DataResource dataResource, A if (dataResource != null) { String metadataDocumentUri = DataResourceRecordUtil.getMetadataDocumentUri(dataResource.getId(), dataResource.getVersion()).toString(); String schemaDocumentUri = DataResourceRecordUtil.getSchemaIdentifier(dataResource).getValue(); - // remove base path of endpoint from URL e.g.: http://x.y.z/metastore/api/v2/schemas/id_of_schema -> id_of_schema - String[] split = schemaDocumentUri.split("\\/", -1); - String schemaId = removeFilterFromUri(split[split.length - 1]); - properties.put(RESOLVING_URL_PROPERTY, removeFilterFromUri(metadataDocumentUri)); - properties.put(DOCUMENT_TYPE_PROPERTY, schemaId); + properties.put(RESOLVING_URL_PROPERTY, metadataDocumentUri); + properties.put(DOCUMENT_TYPE_PROPERTY, schemaDocumentUri); msg.setEntityId(dataResource.getId()); } if (action != null) { @@ -185,19 +180,7 @@ public static MetadataResourceMessage createMessage(DataResource dataResource, A msg.setCurrentTimestamp(); return msg; } - - /** - * Remove version and other stuff added to the URI. - * @param uri URI of the object. - * @return URI without additional parameter. - */ - public static String removeFilterFromUri(String uri) { - String strippedUri = null; - if (uri != null) { - strippedUri = uri.split("\\?", -1)[0]; - } - return strippedUri; - } + @Override public String getEntityName() { return "metadata"; From a8006629c5738f4dd4ab492f157195770a6f0736 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Mon, 25 Nov 2024 14:30:22 +0100 Subject: [PATCH 173/181] Updating indexing-service for docker compose. --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 9328be5d..eeba2091 100644 --- a/.env +++ b/.env @@ -14,7 +14,7 @@ RABBIT_MQ_PASSWORD=rabbitpasswd ######################################## METASTORE_VERSION=v2.0.0 FRONTEND_COLLECTION_VERSION=metastore-v1.0.1 -INDEXING_SERVICE_VERSION=v1.0.2 +INDEXING_SERVICE_VERSION=v1.0.3 ELASTICSEARCH_VERSION=8.11.1 ######################################## # Don't edit following lines From 0f2cd7c43f3681daa130d919aa614b0fe9abf316 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 07:28:45 +0000 Subject: [PATCH 174/181] Update dependency org.springframework.cloud:spring-cloud-gateway-mvc to v4.1.6 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a97979f7..8176746b 100644 --- a/build.gradle +++ b/build.gradle @@ -50,7 +50,7 @@ if (System.getProperty('profile') == 'minimal') { dependencies { // Spring implementation 'org.springframework:spring-messaging:6.2.0' - implementation 'org.springframework.cloud:spring-cloud-gateway-mvc:4.1.5' + implementation 'org.springframework.cloud:spring-cloud-gateway-mvc:4.1.6' // Spring Boot implementation "org.springframework.boot:spring-boot-starter-data-rest" From e20ae8b3afc42e940d18b7756e8fc476d5bae811 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:51:52 +0000 Subject: [PATCH 175/181] Update dependency org.springframework.cloud:spring-cloud-starter-netflix-eureka-client to v4.1.4 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8176746b..44414aef 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { // cloud support implementation "org.springframework.cloud:spring-cloud-starter-config:4.1.3" - implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.3" + implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.4" // springdoc implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springDocVersion}" From 083b5b658b366f5174ca7c2f2ac2fd4b416d97b7 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 27 Nov 2024 13:16:42 +0100 Subject: [PATCH 176/181] Fix typos in REST documentation. --- docs/documentation.adoc | 4 +- docs/documentationV2.adoc | 4 +- restDocu.md | 452 ++++++++++++++-------------- restDocuV2.md | 600 +++++++++++++++++++------------------- 4 files changed, 538 insertions(+), 522 deletions(-) diff --git a/docs/documentation.adoc b/docs/documentation.adoc index 986454c3..de869681 100644 --- a/docs/documentation.adoc +++ b/docs/documentation.adoc @@ -860,7 +860,7 @@ In the actual HTTP request there is nothing special. You just access the path of include::{snippets}/get-json-schema-document/http-request.adoc[] -As a result, you receive the XSD schema send before. +As a result, you receive the JSON schema send before. include::{snippets}/get-json-schema-document/http-response.adoc[] @@ -1071,7 +1071,7 @@ HTTP-wise the call looks as follows: include::{snippets}/get-json-schema-v3/http-request.adoc[] -As a result, you receive the XSD schema document sent before. +As a result, you receive the JSON schema document sent before. include::{snippets}/get-json-schema-v3/http-response.adoc[] diff --git a/docs/documentationV2.adoc b/docs/documentationV2.adoc index 9a52441b..96a0b800 100644 --- a/docs/documentationV2.adoc +++ b/docs/documentationV2.adoc @@ -967,7 +967,7 @@ In the actual HTTP request there is nothing special. You just access the path of include::{snippets}/v2-get-json-schema-document/http-request.adoc[] -As a result, you receive the XSD schema send before. +As a result, you receive the JSON schema send before. include::{snippets}/v2-get-json-schema-document/http-response.adoc[] @@ -1178,7 +1178,7 @@ HTTP-wise the call looks as follows: include::{snippets}/v2-get-json-schema-v3/http-request.adoc[] -As a result, you receive the XSD schema document sent before: +As a result, you receive the JSON schema document sent before: include::{snippets}/v2-get-json-schema-v3/http-response.adoc[] diff --git a/restDocu.md b/restDocu.md index 1b0deba4..3746af29 100644 --- a/restDocu.md +++ b/restDocu.md @@ -164,7 +164,7 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1 - ETag: "1162986171" + ETag: "-153319501" Content-Type: application/json Content-Length: 467 @@ -173,8 +173,8 @@ the user and will look that way: "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.632Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.169Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -216,7 +216,7 @@ As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. HTTP/1.1 200 OK - ETag: "1162986171" + ETag: "-153319501" Content-Type: application/vnd.datamanager.schema-record+json Content-Length: 467 @@ -225,8 +225,8 @@ again the corresponding ETag in the HTTP response header. "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.632Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.169Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -306,14 +306,14 @@ updated metadata schema document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1162986171"' \ + -H 'If-Match: "-153319501"' \ -F 'schema=@schema-v2.xsd;type=application/xml' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1162986171" + If-Match: "-153319501" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -342,7 +342,7 @@ response header the new location URL and the ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2 - ETag: "2037192494" + ETag: "-2107715674" Content-Type: application/json Content-Length: 467 @@ -351,8 +351,8 @@ response header the new location URL and the ETag. "schemaVersion" : 2, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.883Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.318Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -388,14 +388,14 @@ document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "2037192494"' \ + -H 'If-Match: "-2107715674"' \ -F 'schema=@schema-v3.xsd;type=application/xml' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "2037192494" + If-Match: "-2107715674" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -425,7 +425,7 @@ response header the new location URL and the ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 - ETag: "-1900509294" + ETag: "395928458" Content-Type: application/json Content-Length: 467 @@ -434,8 +434,8 @@ response header the new location URL and the ETag. "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.957Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.354Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -518,7 +518,7 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/another_xsd?version=1 - ETag: "2129018532" + ETag: "-1734576420" Content-Type: application/json Content-Length: 465 @@ -527,8 +527,8 @@ the user and will look that way: "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.983Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.375Z", "acl" : [ { "id" : 2, "sid" : "SELF", @@ -564,8 +564,8 @@ As a result, you receive a list of metadata schema records. "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.983Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.375Z", "acl" : [ { "id" : 2, "sid" : "SELF", @@ -579,8 +579,8 @@ As a result, you receive a list of metadata schema records. "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.957Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.354Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -632,8 +632,8 @@ order. (current version first) "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.957Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.354Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -647,8 +647,8 @@ order. (current version first) "schemaVersion" : 2, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.883Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.318Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -662,8 +662,8 @@ order. (current version first) "schemaVersion" : 1, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:29.632Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.169Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -869,21 +869,21 @@ example we introduce a user called 'admin' and give him all rights. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-1900509294"' \ + -H 'If-Match: "395928458"' \ -F 'record=@schema-record-v4.json;type=application/json' Same for the HTTP request. PUT /metastore/api/v1/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-1900509294" + If-Match: "395928458" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=schema-record-v4.json Content-Type: application/json - {"schemaId":"my_first_xsd","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/xml","type":"XML","createdAt":"2024-11-22T14:26:29Z","lastUpdate":"2024-11-22T14:26:29.957Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"licenseUri":null,"schemaDocumentUri":"http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3","schemaHash":"sha1:1baea3a07d95faea70707fcf46d114315613b970","doNotSync":true} + {"schemaId":"my_first_xsd","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/xml","type":"XML","createdAt":"2024-11-25T13:50:09Z","lastUpdate":"2024-11-25T13:50:09.354Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"licenseUri":null,"schemaDocumentUri":"http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3","schemaHash":"sha1:1baea3a07d95faea70707fcf46d114315613b970","doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- As a result, you receive 200 as HTTP status, the updated metadata schema @@ -891,17 +891,17 @@ record and the updated ETag and location in the HTTP response header. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 - ETag: "-152071665" + ETag: "137475294" Content-Type: application/json - Content-Length: 543 + Content-Length: 541 { "schemaId" : "my_first_xsd", "schemaVersion" : 3, "mimeType" : "application/xml", "type" : "XML", - "createdAt" : "2024-11-22T14:26:29Z", - "lastUpdate" : "2024-11-22T14:26:30.255Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.6Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -1032,19 +1032,19 @@ possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1 - ETag: "-617455241" + Location: http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1 + ETag: "527111421" Content-Type: application/json Content-Length: 711 { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.394Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.671Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "identifierType" : "URL" @@ -1056,7 +1056,7 @@ the user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1", "documentHash" : "sha1:ac92891f6377919446143e0a8639f12715397228" } @@ -1073,12 +1073,12 @@ avoid conflicts. For accessing the metadata the location URL provided before may be used. The URL is compiled by the id of the metadata and its version. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1' -i -X GET \ -H 'Accept: application/xml' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1 HTTP/1.1 Accept: application/xml Host: localhost:8040 @@ -1106,12 +1106,12 @@ The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". Then the command line looks like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 @@ -1119,18 +1119,18 @@ The linked metadata will be returned. The result is sent back to the user and will look that way: HTTP/1.1 200 OK - ETag: "-617455241" + ETag: "527111421" Content-Type: application/vnd.datamanager.metadata-record+json Content-Length: 711 { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.394Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.671Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "identifierType" : "URL" @@ -1142,7 +1142,7 @@ user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1", "documentHash" : "sha1:ac92891f6377919446143e0a8639f12715397228" } @@ -1179,25 +1179,25 @@ the ETag is needed: <example:date>2018-07-02</example:date> </example:metadata> - $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-617455241"' \ + -H 'If-Match: "527111421"' \ -F 'record=@metadata-record-v2.json;type=application/json' \ -F 'document=@metadata-v2.xml;type=application/xml' You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934 HTTP/1.1 + PUT /metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-617455241" + If-Match: "527111421" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record-v2.json Content-Type: application/json - {"id":"d2950149-073d-4e27-bbe8-47d995c00934","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:30Z","lastUpdate":"2024-11-22T14:26:30.394Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} + {"id":"5beca237-d9ed-4c15-96da-86c932194091","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-25T13:50:09Z","lastUpdate":"2024-11-25T13:50:09.671Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v2.xml Content-Type: application/xml @@ -1214,19 +1214,19 @@ Version number of record was incremented by one and 'lastUpdate' was also modified by the server. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2 - ETag: "-1243255844" + Location: http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=2 + ETag: "-1790392852" Content-Type: application/json - Content-Length: 779 + Content-Length: 778 { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.618Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.86Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "identifierType" : "URL" @@ -1242,7 +1242,7 @@ also modified by the server. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=2", "documentHash" : "sha1:e13a87884df391a611fb6257ea53883811d9451a" } @@ -1255,30 +1255,30 @@ Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=2' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2 HTTP/1.1 + GET /metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=2 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 You will get the new metadata record with the new ETag. HTTP/1.1 200 OK - ETag: "-1243255844" + ETag: "-1790392852" Content-Type: application/vnd.datamanager.metadata-record+json - Content-Length: 779 + Content-Length: 778 { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.618Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.86Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "identifierType" : "URL" @@ -1294,7 +1294,7 @@ You will get the new metadata record with the new ETag. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=2", "documentHash" : "sha1:e13a87884df391a611fb6257ea53883811d9451a" } @@ -1322,24 +1322,24 @@ Etag. <example:note>since version 3 notes are allowed</example:note> </example:metadata> - $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-1243255844"' \ + -H 'If-Match: "-1790392852"' \ -F 'record=@metadata-record-v3.json;type=application/json' \ -F 'document=@metadata-v3.xml;type=application/xml' HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934 HTTP/1.1 + PUT /metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-1243255844" + If-Match: "-1790392852" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record-v3.json Content-Type: application/json - {"id":"d2950149-073d-4e27-bbe8-47d995c00934","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:30Z","lastUpdate":"2024-11-22T14:26:30.394Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} + {"id":"5beca237-d9ed-4c15-96da-86c932194091","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-25T13:50:09Z","lastUpdate":"2024-11-25T13:50:09.671Z","schema":{"id":null,"identifier":"my_first_xsd","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1","documentHash":"sha1:ac92891f6377919446143e0a8639f12715397228"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v3.xml Content-Type: application/xml @@ -1355,19 +1355,19 @@ HTTP-wise the call looks as follows: You will get the new metadata record. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3 - ETag: "-49539178" + Location: http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=3 + ETag: "1155421926" Content-Type: application/json - Content-Length: 779 + Content-Length: 778 { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.721Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.97Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" @@ -1383,18 +1383,18 @@ You will get the new metadata record. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } Now you can access the updated metadata via the URI in the HTTP response header. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=3' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3 HTTP/1.1 + GET /metastore/api/v1/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=3 HTTP/1.1 Host: localhost:8040 You will get the updated metadata. @@ -1443,11 +1443,11 @@ size as additional query parameters. If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/?id=d2950149-073d-4e27-bbe8-47d995c00934' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?id=5beca237-d9ed-4c15-96da-86c932194091' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/?id=d2950149-073d-4e27-bbe8-47d995c00934 HTTP/1.1 + GET /metastore/api/v1/metadata/?id=5beca237-d9ed-4c15-96da-86c932194091 HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata records in descending order. @@ -1456,16 +1456,16 @@ As a result, you receive a list of metadata records in descending order. HTTP/1.1 200 OK Content-Range: 0-2/3 Content-Type: application/json - Content-Length: 2277 + Content-Length: 2275 [ { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.721Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.97Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" @@ -1481,16 +1481,16 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" }, { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.618Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.86Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", "identifierType" : "URL" @@ -1506,16 +1506,16 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=2", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" }, { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.394Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.671Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", "identifierType" : "URL" @@ -1527,7 +1527,7 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=1", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } ] @@ -1551,16 +1551,16 @@ You will get the current version of the metadata record(s). HTTP/1.1 200 OK Content-Range: 0-0/1 Content-Type: application/json - Content-Length: 783 + Content-Length: 782 [ { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.721Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.97Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" @@ -1576,7 +1576,7 @@ You will get the current version of the metadata record(s). "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } ] @@ -1586,11 +1586,11 @@ If you want to find all metadata records updated after a specific date. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A10.056702973Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A10.056702973Z HTTP/1.1 Host: localhost:8040 You will get the current version metadata records updated ln the last 2 @@ -1599,16 +1599,16 @@ hours. HTTP/1.1 200 OK Content-Range: 0-0/1 Content-Type: application/json - Content-Length: 783 + Content-Length: 782 [ { - "id" : "d2950149-073d-4e27-bbe8-47d995c00934", + "id" : "5beca237-d9ed-4c15-96da-86c932194091", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:30Z", - "lastUpdate" : "2024-11-22T14:26:30.721Z", + "createdAt" : "2024-11-25T13:50:09Z", + "lastUpdate" : "2024-11-25T13:50:09.97Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3", "identifierType" : "URL" @@ -1624,7 +1624,7 @@ hours. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/d2950149-073d-4e27-bbe8-47d995c00934?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/5beca237-d9ed-4c15-96da-86c932194091?version=3", "documentHash" : "sha1:55547a0ad07445cfbc11a76484da3b21d23ceb82" } ] @@ -1635,11 +1635,11 @@ range. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z&until=2024-11-22T13%3A26%3A30.799639633Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A10.056702973Z&until=2024-11-25T12%3A50%3A10.056699330Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A30.799642917Z&until=2024-11-22T13%3A26%3A30.799639633Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A10.056702973Z&until=2024-11-25T12%3A50%3A10.056699330Z HTTP/1.1 Host: localhost:8040 You will get an empty array as no metadata record exists in the given @@ -1773,17 +1773,17 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1 - ETag: "20745429" + ETag: "1059542107" Content-Type: application/json - Content-Length: 470 + Content-Length: 471 { "schemaId" : "my_first_json", "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:22.38Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:01.099Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -1825,17 +1825,17 @@ As a result, you receive the metadata schema record send before and again the corresponding ETag in the HTTP response header. HTTP/1.1 200 OK - ETag: "20745429" + ETag: "1059542107" Content-Type: application/vnd.datamanager.schema-record+json - Content-Length: 470 + Content-Length: 471 { "schemaId" : "my_first_json", "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:22.38Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:01.099Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -1860,7 +1860,7 @@ path of the resource using the base path and the 'schemaId'. GET /metastore/api/v1/schemas/my_first_json HTTP/1.1 Host: localhost:8040 -As a result, you receive the XSD schema send before. +As a result, you receive the JSON schema send before. HTTP/1.1 200 OK Content-Type: application/json @@ -1924,14 +1924,14 @@ updated metadata schema document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "20745429"' \ + -H 'If-Match: "1059542107"' \ -F 'schema=@schema-v2.json;type=application/json' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "20745429" + If-Match: "1059542107" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -1970,7 +1970,7 @@ response header the new location URL and the ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2 - ETag: "1904643808" + ETag: "-582983683" Content-Type: application/json Content-Length: 471 @@ -1979,8 +1979,8 @@ response header the new location URL and the ETag. "schemaVersion" : 2, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:23.028Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:01.912Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2031,14 +2031,14 @@ document and/or metadata schema record. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1904643808"' \ + -H 'If-Match: "-582983683"' \ -F 'schema=@schema-v3.json;type=application/json' HTTP-wise the call looks as follows: PUT /metastore/api/v1/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1904643808" + If-Match: "-582983683" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -2082,7 +2082,7 @@ response header the new location URL and the ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 - ETag: "850923140" + ETag: "1216336138" Content-Type: application/json Content-Length: 471 @@ -2091,8 +2091,8 @@ response header the new location URL and the ETag. "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:23.165Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:02.048Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2185,7 +2185,7 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/another_json?version=1 - ETag: "-1491761616" + ETag: "-900414026" Content-Type: application/json Content-Length: 469 @@ -2194,8 +2194,8 @@ the user and will look that way: "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:23.249Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.128Z", "acl" : [ { "id" : 2, "sid" : "SELF", @@ -2231,8 +2231,8 @@ As a result, you receive a list of metadata schema records. "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:23.249Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.128Z", "acl" : [ { "id" : 2, "sid" : "SELF", @@ -2246,8 +2246,8 @@ As a result, you receive a list of metadata schema records. "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:23.165Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:02.048Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2292,15 +2292,15 @@ order. (current version first) HTTP/1.1 200 OK Content-Range: 0-2/3 Content-Type: application/json - Content-Length: 1420 + Content-Length: 1421 [ { "schemaId" : "my_first_json", "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:23.165Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:02.048Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2314,8 +2314,8 @@ order. (current version first) "schemaVersion" : 2, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:23.028Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:01.912Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2329,8 +2329,8 @@ order. (current version first) "schemaVersion" : 1, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:22.38Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:01.099Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2353,7 +2353,7 @@ HTTP-wise the call looks as follows: GET /metastore/api/v1/schemas/my_first_json HTTP/1.1 Host: localhost:8040 -As a result, you receive the XSD schema document sent before. +As a result, you receive the JSON schema document sent before. HTTP/1.1 200 OK Content-Type: application/json @@ -2533,21 +2533,21 @@ example we introduce a user called 'admin' and give him all rights. $ curl 'http://localhost:8040/metastore/api/v1/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "850923140"' \ + -H 'If-Match: "1216336138"' \ -F 'record=@schema-record4json-v4.json;type=application/json' Same for the HTTP request. PUT /metastore/api/v1/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "850923140" + If-Match: "1216336138" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=schema-record4json-v4.json Content-Type: application/json - {"schemaId":"my_first_json","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/json","type":"JSON","createdAt":"2024-11-22T14:26:22Z","lastUpdate":"2024-11-22T14:26:23.165Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"licenseUri":null,"schemaDocumentUri":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3","schemaHash":"sha1:16221eb6fd0177135b873acd78da1a221a8b621d","doNotSync":true} + {"schemaId":"my_first_json","pid":null,"schemaVersion":3,"label":null,"definition":null,"comment":null,"mimeType":"application/json","type":"JSON","createdAt":"2024-11-25T13:50:01Z","lastUpdate":"2024-11-25T13:50:02.048Z","acl":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}],"licenseUri":null,"schemaDocumentUri":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3","schemaHash":"sha1:16221eb6fd0177135b873acd78da1a221a8b621d","doNotSync":true} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- As a result, you receive 200 as HTTP status, the updated metadata schema @@ -2555,7 +2555,7 @@ record and the updated ETag and location in the HTTP response header. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 - ETag: "601310168" + ETag: "-1011309538" Content-Type: application/json Content-Length: 547 @@ -2564,8 +2564,8 @@ record and the updated ETag and location in the HTTP response header. "schemaVersion" : 3, "mimeType" : "application/json", "type" : "JSON", - "createdAt" : "2024-11-22T14:26:22Z", - "lastUpdate" : "2024-11-22T14:26:23.715Z", + "createdAt" : "2024-11-25T13:50:01Z", + "lastUpdate" : "2024-11-25T13:50:02.595Z", "acl" : [ { "id" : 1, "sid" : "SELF", @@ -2691,19 +2691,19 @@ possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 - ETag: "-1572304547" + Location: http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1 + ETag: "572714723" Content-Type: application/json Content-Length: 712 { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:23.866Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.729Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "identifierType" : "URL" @@ -2715,7 +2715,7 @@ the user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1", "documentHash" : "sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a" } @@ -2732,12 +2732,12 @@ avoid conflicts. For accessing the metadata the location URL provided before may be used. The URL is compiled by the id of the metadata and its version. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1' -i -X GET \ -H 'Accept: application/json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1 HTTP/1.1 Accept: application/json Host: localhost:8040 @@ -2762,12 +2762,12 @@ The only difference is the content type. It has to be set to "application/vnd.datamanager.metadata-record+json". Then the command line looks like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 HTTP/1.1 + GET /metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 @@ -2775,18 +2775,18 @@ The linked metadata will be returned. The result is sent back to the user and will look that way: HTTP/1.1 200 OK - ETag: "-1572304547" + ETag: "572714723" Content-Type: application/vnd.datamanager.metadata-record+json Content-Length: 712 { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:23.866Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.729Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "identifierType" : "URL" @@ -2798,7 +2798,7 @@ user and will look that way: "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1", "documentHash" : "sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a" } @@ -2833,25 +2833,25 @@ mentioned before the ETag is needed: "date": "2018-07-02" } - $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-1572304547"' \ + -H 'If-Match: "572714723"' \ -F 'record=@metadata-record4json-v2.json;type=application/json' \ -F 'document=@metadata-v2.json;type=application/xml' You can see, that only the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1 HTTP/1.1 + PUT /metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-1572304547" + If-Match: "572714723" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record4json-v2.json Content-Type: application/json - {"id":"1607d566-7869-41a5-9c17-0f12badcebb7","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:23Z","lastUpdate":"2024-11-22T14:26:23.866Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} + {"id":"547fc0e5-7ae1-4b51-9a2e-180782c6a5c9","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-25T13:50:02Z","lastUpdate":"2024-11-25T13:50:02.729Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":2,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v2.json Content-Type: application/xml @@ -2867,19 +2867,19 @@ Version number of record was incremented by one and 'lastUpdate' was also modified by the server. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2 - ETag: "1226014924" + Location: http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=2 + ETag: "-616587167" Content-Type: application/json Content-Length: 780 { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:24.168Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.996Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "identifierType" : "URL" @@ -2895,7 +2895,7 @@ also modified by the server. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=2", "documentHash" : "sha1:1844c8057b673ae260fcc6b6ba146529b2b52771" } @@ -2908,30 +2908,30 @@ Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=2' -i -X GET \ -H 'Accept: application/vnd.datamanager.metadata-record+json' HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2 HTTP/1.1 + GET /metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=2 HTTP/1.1 Accept: application/vnd.datamanager.metadata-record+json Host: localhost:8040 You will get the new metadata record with the new ETag. HTTP/1.1 200 OK - ETag: "1226014924" + ETag: "-616587167" Content-Type: application/vnd.datamanager.metadata-record+json Content-Length: 780 { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:24.168Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.996Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "identifierType" : "URL" @@ -2947,7 +2947,7 @@ You will get the new metadata record with the new ETag. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=2", "documentHash" : "sha1:1844c8057b673ae260fcc6b6ba146529b2b52771" } @@ -2974,24 +2974,24 @@ Etag. "note": "since version 3 notes are allowed" } - $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1226014924"' \ + -H 'If-Match: "-616587167"' \ -F 'record=@metadata-record4json-v3.json;type=application/json' \ -F 'document=@metadata-v3.json;type=application/xml' HTTP-wise the call looks as follows: - PUT /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7 HTTP/1.1 + PUT /metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1226014924" + If-Match: "-616587167" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record4json-v3.json Content-Type: application/json - {"id":"1607d566-7869-41a5-9c17-0f12badcebb7","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-22T14:26:23Z","lastUpdate":"2024-11-22T14:26:23.866Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} + {"id":"547fc0e5-7ae1-4b51-9a2e-180782c6a5c9","pid":null,"relatedResource":{"id":null,"identifier":"https://repo/anyResourceId","identifierType":"URL"},"createdAt":"2024-11-25T13:50:02Z","lastUpdate":"2024-11-25T13:50:02.729Z","schema":{"id":null,"identifier":"my_first_json","identifierType":"INTERNAL"},"schemaVersion":3,"recordVersion":1,"acl":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}],"licenseUri":null,"metadataDocumentUri":"http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1","documentHash":"sha1:97ac2fb17cd40aac07a55444dc161d615c70af8a"} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v3.json Content-Type: application/xml @@ -3006,19 +3006,19 @@ HTTP-wise the call looks as follows: You will get the new metadata record. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3 - ETag: "838189446" + Location: http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=3 + ETag: "41151108" Content-Type: application/json Content-Length: 780 { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:24.299Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:03.158Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" @@ -3034,18 +3034,18 @@ You will get the new metadata record. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } Now you can access the updated metadata via the URI in the HTTP response header. - $ curl 'http://localhost:8040/metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=3' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3 HTTP/1.1 + GET /metastore/api/v1/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=3 HTTP/1.1 Host: localhost:8040 You will get the updated metadata. @@ -3089,11 +3089,11 @@ size as additional query parameters. If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/?id=1607d566-7869-41a5-9c17-0f12badcebb7' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?id=547fc0e5-7ae1-4b51-9a2e-180782c6a5c9' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/?id=1607d566-7869-41a5-9c17-0f12badcebb7 HTTP/1.1 + GET /metastore/api/v1/metadata/?id=547fc0e5-7ae1-4b51-9a2e-180782c6a5c9 HTTP/1.1 Host: localhost:8040 As a result, you receive a list of metadata records in descending order. @@ -3105,13 +3105,13 @@ As a result, you receive a list of metadata records in descending order. Content-Length: 2280 [ { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:24.299Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:03.158Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" @@ -3127,16 +3127,16 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" }, { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:24.168Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.996Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", "identifierType" : "URL" @@ -3152,16 +3152,16 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=2", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=2", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" }, { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:23.866Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:02.729Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", "identifierType" : "URL" @@ -3173,7 +3173,7 @@ As a result, you receive a list of metadata records in descending order. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=1", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=1", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } ] @@ -3200,13 +3200,13 @@ You will get the current version metadata record. Content-Length: 784 [ { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:24.299Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:03.158Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" @@ -3222,7 +3222,7 @@ You will get the current version metadata record. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } ] @@ -3232,11 +3232,11 @@ If you want to find all metadata records updated after a specific date. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A03.297365353Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A03.297365353Z HTTP/1.1 Host: localhost:8040 You will get the current version metadata records updated ln the last 2 @@ -3248,13 +3248,13 @@ hours. Content-Length: 784 [ { - "id" : "1607d566-7869-41a5-9c17-0f12badcebb7", + "id" : "547fc0e5-7ae1-4b51-9a2e-180782c6a5c9", "relatedResource" : { "identifier" : "https://repo/anyResourceId", "identifierType" : "URL" }, - "createdAt" : "2024-11-22T14:26:23Z", - "lastUpdate" : "2024-11-22T14:26:24.299Z", + "createdAt" : "2024-11-25T13:50:02Z", + "lastUpdate" : "2024-11-25T13:50:03.158Z", "schema" : { "identifier" : "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3", "identifierType" : "URL" @@ -3270,7 +3270,7 @@ hours. "sid" : "SELF", "permission" : "ADMINISTRATE" } ], - "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/1607d566-7869-41a5-9c17-0f12badcebb7?version=3", + "metadataDocumentUri" : "http://localhost:8040/metastore/api/v2/metadata/547fc0e5-7ae1-4b51-9a2e-180782c6a5c9?version=3", "documentHash" : "sha1:737762db675032231ac3cb872fccd32a83ac24d1" } ] @@ -3281,11 +3281,11 @@ range. Command line: - $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z&until=2024-11-22T13%3A26%3A24.409014540Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A03.297365353Z&until=2024-11-25T12%3A50%3A03.297359801Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v1/metadata/?from=2024-11-22T12%3A26%3A24.409018251Z&until=2024-11-22T13%3A26%3A24.409014540Z HTTP/1.1 + GET /metastore/api/v1/metadata/?from=2024-11-25T11%3A50%3A03.297365353Z&until=2024-11-25T12%3A50%3A03.297359801Z HTTP/1.1 Host: localhost:8040 You will get an empty array as no metadata record exists in the given diff --git a/restDocuV2.md b/restDocuV2.md index 7f07bd15..5996583b 100644 --- a/restDocuV2.md +++ b/restDocuV2.md @@ -118,8 +118,6 @@ In addition, ACL may be useful to make schema readable/editable by others. This will be of interest while accessing/updating an existing schema.(if authorization is enabled) -License URI is optional. It’s new since 1.4.2. - ## Registering a Metadata Schema Document The following example shows the creation of the first XSD schema only @@ -196,7 +194,7 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1 - ETag: "1432044929" + ETag: "58150585" Content-Type: application/json Content-Length: 801 @@ -224,7 +222,7 @@ the user and will look that way: }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -233,7 +231,7 @@ the user and will look that way: "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:32.58Z", + "lastUpdate" : "2024-11-25T13:50:12.08Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -261,7 +259,7 @@ field 'Id'. As 'Accept' field you have to provide 'application/vnd.datacite.org+json' otherwise you will get the landing -page of the digital object schema instead. +page of the digital object instead. $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X GET \ -H 'Accept: application/vnd.datacite.org+json' @@ -278,7 +276,7 @@ As a result, you receive the datacite record send before and again the corresponding ETag in the HTTP response header. HTTP/1.1 200 OK - ETag: "1432044929" + ETag: "58150585" Content-Type: application/vnd.datacite.org+json Content-Length: 801 @@ -306,7 +304,7 @@ corresponding ETag in the HTTP response header. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -315,7 +313,7 @@ corresponding ETag in the HTTP response header. "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:32.58Z", + "lastUpdate" : "2024-11-25T13:50:12.08Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -324,7 +322,7 @@ corresponding ETag in the HTTP response header. } ] } -### Getting a Metadata Schema Document +### Getting a Schema Document For obtaining accessible metadata schemas you also have to provide the 'schemaId'. For accessing schema document you have to provide @@ -395,14 +393,14 @@ metadata schema document and/or datacite record. $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1432044929"' \ + -H 'If-Match: "58150585"' \ -F 'schema=@schema-v2.xsd;type=application/xml' HTTP-wise the call looks as follows: PUT /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1432044929" + If-Match: "58150585" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -432,7 +430,7 @@ ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2 - ETag: "1398092788" + ETag: "-879539924" Content-Type: application/json Content-Length: 1011 @@ -460,7 +458,7 @@ ETag. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -475,7 +473,7 @@ ETag. "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:32.695Z", + "lastUpdate" : "2024-11-25T13:50:12.211Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -509,14 +507,14 @@ document and/or datacite record. $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1398092788"' \ + -H 'If-Match: "-879539924"' \ -F 'schema=@schema-v3.xsd;type=application/xml' HTTP-wise the call looks as follows: PUT /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1398092788" + If-Match: "-879539924" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -547,7 +545,7 @@ ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 - ETag: "-393230376" + ETag: "1398169616" Content-Type: application/json Content-Length: 1011 @@ -575,7 +573,7 @@ ETag. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -590,7 +588,7 @@ ETag. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:32.731Z", + "lastUpdate" : "2024-11-25T13:50:12.251Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -675,7 +673,7 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/another_xsd?version=1 - ETag: "1370518322" + ETag: "-1133048982" Content-Type: application/json Content-Length: 799 @@ -703,7 +701,7 @@ the user and will look that way: }, "dates" : [ { "id" : 2, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -712,7 +710,7 @@ the user and will look that way: "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:32.747Z", + "lastUpdate" : "2024-11-25T13:50:12.267Z", "state" : "VOLATILE", "acls" : [ { "id" : 2, @@ -723,7 +721,7 @@ the user and will look that way: Now there are two schemaIds registered in the metadata schema registry. -### Getting a List of Metadata Schema Records +### Getting a List of Datacite Records of a Schema For getting all accessible datacite records of schema documents type: @@ -765,7 +763,7 @@ As a result, you receive a list of datacite records. }, "dates" : [ { "id" : 2, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -774,7 +772,7 @@ As a result, you receive a list of datacite records. "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:32.747Z", + "lastUpdate" : "2024-11-25T13:50:12.267Z", "state" : "VOLATILE", "acls" : [ { "id" : 2, @@ -805,7 +803,7 @@ As a result, you receive a list of datacite records. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -820,7 +818,7 @@ As a result, you receive a list of datacite records. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:32.731Z", + "lastUpdate" : "2024-11-25T13:50:12.251Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -845,7 +843,7 @@ The modified HTTP request with pagination looks like follows: GET /metastore/api/v2/schemas/?page=0&size=20 HTTP/1.1 Host: localhost:8040 -### Getting a List of all Schema Records for a Specific SchemaId +### Getting a List of all Datacite Records of a Schema for a Specific SchemaId If you want to obtain all versions of a specific schema you may add the schemaId as a filter parameter. This may look like this: @@ -889,7 +887,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -904,7 +902,7 @@ As a result, you receive a list of datacite records in descending order. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:32.731Z", + "lastUpdate" : "2024-11-25T13:50:12.251Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -935,7 +933,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -950,7 +948,7 @@ As a result, you receive a list of datacite records in descending order. "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:32.695Z", + "lastUpdate" : "2024-11-25T13:50:12.211Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -981,7 +979,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -990,7 +988,7 @@ As a result, you receive a list of datacite records in descending order. "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:32.58Z", + "lastUpdate" : "2024-11-25T13:50:12.08Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -1200,21 +1198,21 @@ example we introduce a user called 'admin' and give him all rights. $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_xsd' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-393230376"' \ + -H 'If-Match: "1398169616"' \ -F 'record=@schema-record-v4.json;type=application/json' Same for the HTTP request. PUT /metastore/api/v2/schemas/my_first_xsd HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-393230376" + If-Match: "1398169616" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=schema-record-v4.json Content-Type: application/json - {"id":"my_first_xsd","identifier":{"id":1,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":1,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":1,"value":"Title for my_first_xsd","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":1,"value":"XML_Schema","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":1,"value":"2024-11-22T14:26:32Z","type":"CREATED"}],"relatedIdentifiers":[{"id":1,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=2","relationType":"IS_NEW_VERSION_OF","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":1,"value":"my_first_xsd","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"3","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:32.731Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}]} + {"id":"my_first_xsd","identifier":{"id":1,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":1,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":1,"value":"Title for my_first_xsd","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":1,"value":"XML_Schema","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":1,"value":"2024-11-25T13:50:12Z","type":"CREATED"}],"relatedIdentifiers":[{"id":1,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/metadata/my_first_xsd?version=2","relationType":"IS_NEW_VERSION_OF","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":1,"value":"my_first_xsd","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"3","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-25T13:50:12.251Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- As a result, you receive 200 as HTTP status, the updated datacite record @@ -1222,9 +1220,9 @@ and the updated ETag and location in the HTTP response header. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=3 - ETag: "137544821" + ETag: "1337460292" Content-Type: application/json - Content-Length: 1087 + Content-Length: 1086 { "id" : "my_first_xsd", @@ -1250,7 +1248,7 @@ and the updated ETag and location in the HTTP response header. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:32Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -1265,7 +1263,7 @@ and the updated ETag and location in the HTTP response header. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:33.003Z", + "lastUpdate" : "2024-11-25T13:50:12.56Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -1347,7 +1345,7 @@ At least the following elements have to be provided by the user: - relatedIdentifier/schema: Link to the related schema. (identifierType: INTERNAL and URL are supported, relationType: - IS\_DERIVED\_FROM) + HAS\_METADATA) - relatedIdentifier/data: Link to the (data) resource. (identifierType: any, relationType: IS\_METADATA\_FOR) @@ -1359,8 +1357,6 @@ If linked schema is identified by its schemaId the INTERNAL type has to be used. It’s then linked to the current schema version at creation time. -License URI is optional. It’s new since 1.4.2. - ### Register/Ingest a Datacite Record with Metadata Document The following example shows the creation of the first metadata document @@ -1381,7 +1377,7 @@ and its datacite record only providing mandatory fields mentioned above: { "identifierType": "URL", "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=1", - "relationType": "IS_DERIVED_FROM" + "relationType": "HAS_METADATA" }, { "identifierType": "URL", @@ -1435,13 +1431,13 @@ possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 - ETag: "-1820103729" + Location: http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1 + ETag: "-1376240683" Content-Type: application/json Content-Length: 1203 { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -1464,7 +1460,7 @@ the user and will look that way: }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -1480,11 +1476,11 @@ the user and will look that way: } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:33.023Z", + "lastUpdate" : "2024-11-25T13:50:12.582Z", "state" : "VOLATILE", "acls" : [ { "id" : 4, @@ -1506,12 +1502,12 @@ conflicts. For accessing the metadata the location URL provided before may be used. The URL is compiled by the id of the metadata and its version. - $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1' -i -X GET \ -H 'Accept: application/xml' HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 HTTP/1.1 + GET /metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1 HTTP/1.1 Accept: application/xml Host: localhost:8040 @@ -1542,12 +1538,12 @@ The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". Then the command line looks like this: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1' -i -X GET \ -H 'Accept: application/vnd.datacite.org+json' HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 HTTP/1.1 + GET /metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1 HTTP/1.1 Accept: application/vnd.datacite.org+json Host: localhost:8040 @@ -1555,13 +1551,13 @@ The linked metadata will be returned. The result is sent back to the user and will look that way: HTTP/1.1 200 OK - ETag: "-1820103729" - Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1 + ETag: "-1376240683" + Location: http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1 Content-Type: application/vnd.datacite.org+json Content-Length: 1203 { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -1584,7 +1580,7 @@ user and will look that way: }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -1600,11 +1596,11 @@ user and will look that way: } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:33.023Z", + "lastUpdate" : "2024-11-25T13:50:12.582Z", "state" : "VOLATILE", "acls" : [ { "id" : 4, @@ -1631,7 +1627,7 @@ the ETag is needed: "id": 1, "identifierType": "URL", "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2", - "relationType": "IS_DERIVED_FROM" + "relationType": "HAS_METADATA" } ], [...] @@ -1651,9 +1647,9 @@ the ETag is needed: <example:date>2018-07-02</example:date> </example:metadata> - $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-1820103729"' \ + -H 'If-Match: "-1376240683"' \ -F 'record=@metadata-record-v2.json;type=application/json' \ -F 'document=@metadata-v2.xml;type=application/xml' @@ -1661,16 +1657,16 @@ You can see, that the schema was set to version 2 (allowing additional field for date) and the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: - PUT /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9 HTTP/1.1 + PUT /metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-1820103729" + If-Match: "-1376240683" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record-v2.json Content-Type: application/json - {"id":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first XML metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"XML_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:33Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:33.023Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + {"id":"23dd1d7d-f4fa-40ad-a846-0a2c50b06399","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first XML metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"XML_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-25T13:50:12Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_xsd?version=2","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"23dd1d7d-f4fa-40ad-a846-0a2c50b06399","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-25T13:50:12.582Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v2.xml Content-Type: application/xml @@ -1685,13 +1681,13 @@ properties are still the same. HTTP-wise the call looks as follows: The response provides the updated datacite record: HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2 - ETag: "1547105945" + Location: http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2 + ETag: "-727104155" Content-Type: application/json Content-Length: 1475 { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -1714,7 +1710,7 @@ The response provides the updated datacite record: }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -1730,16 +1726,16 @@ The response provides the updated datacite record: }, { "id" : 4, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1", + "value" : "http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1", "relationType" : "IS_NEW_VERSION_OF" } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:33.105Z", + "lastUpdate" : "2024-11-25T13:50:12.678Z", "state" : "VOLATILE", "acls" : [ { "id" : 5, @@ -1762,25 +1758,25 @@ Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. - $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2' -i -X GET \ -H 'Accept: application/vnd.datacite.org+json' HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2 HTTP/1.1 + GET /metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2 HTTP/1.1 Accept: application/vnd.datacite.org+json Host: localhost:8040 You will get the new datacite record with the new ETag. HTTP/1.1 200 OK - ETag: "1547105945" - Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2 + ETag: "-727104155" + Location: http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2 Content-Type: application/vnd.datacite.org+json Content-Length: 1475 { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -1803,7 +1799,7 @@ You will get the new datacite record with the new ETag. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -1819,16 +1815,16 @@ You will get the new datacite record with the new ETag. }, { "id" : 4, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1", + "value" : "http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1", "relationType" : "IS_NEW_VERSION_OF" } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:33.105Z", + "lastUpdate" : "2024-11-25T13:50:12.678Z", "state" : "VOLATILE", "acls" : [ { "id" : 5, @@ -1854,7 +1850,7 @@ Etag. "id": 1, "identifierType": "INTERNAL", "value": "my_first_xsd", - "relationType": "IS_DERIVED_FROM" + "relationType": "HAS_METADATA" } ], [...] @@ -1872,24 +1868,24 @@ version 3). <example:note>since version 3 notes are allowed</example:note> </example:metadata> - $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "1547105945"' \ + -H 'If-Match: "-727104155"' \ -F 'record=@metadata-record-v3.json;type=application/json' \ -F 'document=@metadata-v3.xml;type=application/xml' HTTP-wise the call looks as follows: - PUT /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9 HTTP/1.1 + PUT /metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "1547105945" + If-Match: "-727104155" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record-v3.json Content-Type: application/json - {"id":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first XML metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"XML_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:33Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"INTERNAL","value":"my_first_xsd","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"db2379c7-1952-406d-ae69-5a9a9d39c9c9","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:33.023Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + {"id":"23dd1d7d-f4fa-40ad-a846-0a2c50b06399","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first XML metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"XML_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-25T13:50:12Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"INTERNAL","value":"my_first_xsd","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"23dd1d7d-f4fa-40ad-a846-0a2c50b06399","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-25T13:50:12.582Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v3.xml Content-Type: application/xml @@ -1905,13 +1901,13 @@ HTTP-wise the call looks as follows: You will get the new datacite record. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=3 - ETag: "1996473594" + Location: http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=3 + ETag: "-503671098" Content-Type: application/json Content-Length: 1475 { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -1934,7 +1930,7 @@ You will get the new datacite record. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -1945,7 +1941,7 @@ You will get the new datacite record. }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -1955,11 +1951,11 @@ You will get the new datacite record. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:33.149Z", + "lastUpdate" : "2024-11-25T13:50:12.726Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -1975,11 +1971,11 @@ You will get the new datacite record. Now you can access the updated metadata via the URI in the HTTP response header. - $ curl 'http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=3' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=3' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=3 HTTP/1.1 + GET /metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=3 HTTP/1.1 Host: localhost:8040 You will get the updated metadata. @@ -2028,11 +2024,11 @@ size as additional query parameters. If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/?id=db2379c7-1952-406d-ae69-5a9a9d39c9c9' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?id=23dd1d7d-f4fa-40ad-a846-0a2c50b06399' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/?id=db2379c7-1952-406d-ae69-5a9a9d39c9c9 HTTP/1.1 + GET /metastore/api/v2/metadata/?id=23dd1d7d-f4fa-40ad-a846-0a2c50b06399 HTTP/1.1 Host: localhost:8040 As a result, you receive a list of datacite records in descending order. @@ -2044,7 +2040,7 @@ As a result, you receive a list of datacite records in descending order. Content-Length: 4161 [ { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -2067,7 +2063,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -2078,7 +2074,7 @@ As a result, you receive a list of datacite records in descending order. }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -2088,11 +2084,11 @@ As a result, you receive a list of datacite records in descending order. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:33.149Z", + "lastUpdate" : "2024-11-25T13:50:12.726Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -2104,7 +2100,7 @@ As a result, you receive a list of datacite records in descending order. "permission" : "ADMINISTRATE" } ] }, { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -2127,7 +2123,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -2143,16 +2139,16 @@ As a result, you receive a list of datacite records in descending order. }, { "id" : 4, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=1", + "value" : "http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=1", "relationType" : "IS_NEW_VERSION_OF" } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:33.105Z", + "lastUpdate" : "2024-11-25T13:50:12.678Z", "state" : "VOLATILE", "acls" : [ { "id" : 5, @@ -2164,7 +2160,7 @@ As a result, you receive a list of datacite records in descending order. "permission" : "ADMINISTRATE" } ] }, { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -2187,7 +2183,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -2203,11 +2199,11 @@ As a result, you receive a list of datacite records in descending order. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:33.023Z", + "lastUpdate" : "2024-11-25T13:50:12.582Z", "state" : "VOLATILE", "acls" : [ { "id" : 4, @@ -2220,7 +2216,6 @@ As a result, you receive a list of datacite records in descending order. If you want to find all records belonging to an external resource. MetaStore may hold multiple metadata documents per resource. -(Nevertheless only one per registered schema) Command line: @@ -2239,7 +2234,7 @@ You will get the current version of the datacite record(s). Content-Length: 1479 [ { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -2262,7 +2257,7 @@ You will get the current version of the datacite record(s). }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -2273,7 +2268,7 @@ You will get the current version of the datacite record(s). }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -2283,11 +2278,11 @@ You will get the current version of the datacite record(s). } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:33.149Z", + "lastUpdate" : "2024-11-25T13:50:12.726Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -2306,11 +2301,11 @@ If you want to find all datacite records updated after a specific date. Command line: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A12.797908423Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z HTTP/1.1 + GET /metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A12.797908423Z HTTP/1.1 Host: localhost:8040 You will get the current version datacite records updated ln the last 2 @@ -2322,7 +2317,7 @@ hours. Content-Length: 1479 [ { - "id" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "id" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -2345,7 +2340,7 @@ hours. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:33Z", + "value" : "2024-11-25T13:50:12Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -2356,7 +2351,7 @@ hours. }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/db2379c7-1952-406d-ae69-5a9a9d39c9c9?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/23dd1d7d-f4fa-40ad-a846-0a2c50b06399?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -2366,11 +2361,11 @@ hours. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "db2379c7-1952-406d-ae69-5a9a9d39c9c9", + "value" : "23dd1d7d-f4fa-40ad-a846-0a2c50b06399", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:33.149Z", + "lastUpdate" : "2024-11-25T13:50:12.726Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -2390,11 +2385,11 @@ range. Command line: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z&until=2024-11-22T13%3A26%3A33.209219312Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A12.797908423Z&until=2024-11-25T12%3A50%3A12.797905403Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A33.209222545Z&until=2024-11-22T13%3A26%3A33.209219312Z HTTP/1.1 + GET /metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A12.797908423Z&until=2024-11-25T12%3A50%3A12.797905403Z HTTP/1.1 Host: localhost:8040 You will get an empty array as no datacite record exists in the given @@ -2416,37 +2411,60 @@ all starts with creating your first json schema resource. The model of a datacite record looks like this: { - "schemaId" : "...", - "schemaVersion" : 1, - "mimeType" : "...", - "type" : "...", - "createdAt" : "...", + "id" : "...", + "identifier" : { + "value" : "(:tba)", + "identifierType" : "DOI" + }, + "creators" : [ { + "givenName" : "..." + } ], + "titles" : [ { + "value" : "..." + } ], + "publisher" : "...", + "publicationYear" : "...", + "resourceType" : { + "value" : "...", + "typeGeneral" : "..." + }, + "dates" : [ { + "value" : "...", + "type" : "..." + } ], + "relatedIdentifiers" : [ { + "value" : "...", + "identifierType" : "...", + "relationType" : "..." + }} ], + "alternateIdentifiers" : [ { + "value" : "...", + "identifierType" : "..." + } ], + "version" : "...", + "rights": [ + { + "schemeId": "", + "schemeUri": "" + } + ], "lastUpdate" : "...", - "acl" : [ { - "id" : 1, + "state" : "...", + "acls" : [ { "sid" : "...", "permission" : "..." - } ], - "licenseUri" : "...", - "schemaDocumentUri" : "...", - "schemaHash" : "...", - "locked" : false + } ] } At least the following elements are expected to be provided by the user: -- schemaId: A unique label for the schema. - -- mimeType: The resource type must be assigned by the user. For JSON - schemas this should be *application/json* +- id: A unique label for the schema. -- type: XML or JSON. For JSON schemas this should be *JSON* +- title: Any title for the schema. In addition, ACL may be useful to make schema editable by others. (This will be of interest while updating an existing schema) -License URI is optional. It’s new since 1.5.0. - ## Registering a Metadata Schema Document The following example shows the creation of the first json schema only @@ -2454,8 +2472,12 @@ providing mandatory fields mentioned above: schema-record4json.json: { - "schemaId" : "my_first_json", - "type" : "JSON" + "id": "my_first_json", + "titles": [ + { + "value": "Title for my_first_json", + } + ] } schema.json: @@ -2528,7 +2550,7 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1 - ETag: "-759012511" + ETag: "855670887" Content-Type: application/json Content-Length: 806 @@ -2556,7 +2578,7 @@ the user and will look that way: }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -2565,7 +2587,7 @@ the user and will look that way: "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:26.664Z", + "lastUpdate" : "2024-11-25T13:50:05.753Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -2588,8 +2610,7 @@ There are two possible values for DataResource: *XML\_Schema* and ### Getting a Datacite Schema Record -For obtaining one datacite record you have to provide the value of the -field 'schemaId'. +For obtaining one datacite record you have to provide the 'schemaId'. As 'Accept' field you have to provide 'application/vnd.datacite.org+json' otherwise you will get the landing @@ -2610,7 +2631,7 @@ As a result, you receive the datacite record send before and again the corresponding ETag in the HTTP response header. HTTP/1.1 200 OK - ETag: "-759012511" + ETag: "855670887" Content-Type: application/vnd.datacite.org+json Content-Length: 806 @@ -2638,7 +2659,7 @@ corresponding ETag in the HTTP response header. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -2647,7 +2668,7 @@ corresponding ETag in the HTTP response header. "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:26.664Z", + "lastUpdate" : "2024-11-25T13:50:05.753Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -2656,7 +2677,7 @@ corresponding ETag in the HTTP response header. } ] } -### Getting a Metadata Schema Document +### Getting a Schema Document For obtaining accessible metadata schemas you also have to provide the 'schemaId'. For accessing schema document you have to provide @@ -2672,7 +2693,7 @@ path of the resource using the base path and the 'schemaId'. Accept: application/json Host: localhost:8040 -As a result, you receive the XSD schema send before. +As a result, you receive the JSON schema send before. HTTP/1.1 200 OK Content-Type: text/plain @@ -2736,14 +2757,14 @@ metadata schema document and/or datacite record. $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "-759012511"' \ + -H 'If-Match: "855670887"' \ -F 'schema=@schema-v2.json;type=application/json' HTTP-wise the call looks as follows: PUT /metastore/api/v2/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "-759012511" + If-Match: "855670887" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -2783,7 +2804,7 @@ ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2 - ETag: "575519683" + ETag: "-1991796919" Content-Type: application/json Content-Length: 1016 @@ -2811,7 +2832,7 @@ ETag. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -2826,7 +2847,7 @@ ETag. "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:26.854Z", + "lastUpdate" : "2024-11-25T13:50:05.941Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -2875,14 +2896,14 @@ document and/or datacite record. $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "575519683"' \ + -H 'If-Match: "-1991796919"' \ -F 'schema=@schema-v3.json;type=application/json' HTTP-wise the call looks as follows: PUT /metastore/api/v2/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "575519683" + If-Match: "-1991796919" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm @@ -2927,9 +2948,9 @@ ETag. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 - ETag: "254981415" + ETag: "2066868822" Content-Type: application/json - Content-Length: 1015 + Content-Length: 1016 { "id" : "my_first_json", @@ -2955,7 +2976,7 @@ ETag. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -2970,7 +2991,7 @@ ETag. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:26.94Z", + "lastUpdate" : "2024-11-25T13:50:06.037Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -3061,9 +3082,9 @@ the user and will look that way: HTTP/1.1 201 Created Location: http://localhost:8040/metastore/api/v2/schemas/another_json?version=1 - ETag: "-1932760524" + ETag: "-769946310" Content-Type: application/json - Content-Length: 799 + Content-Length: 803 { "id" : "another_json", @@ -3089,7 +3110,7 @@ the user and will look that way: }, "dates" : [ { "id" : 2, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -3098,7 +3119,7 @@ the user and will look that way: "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:27Z", + "lastUpdate" : "2024-11-25T13:50:06.097Z", "state" : "VOLATILE", "acls" : [ { "id" : 2, @@ -3109,7 +3130,7 @@ the user and will look that way: Now there are two schemaIds registered in the metadata schema registry. -### Getting a List of Metadata Schema Records +### Getting a List of Datacite Records of a Schema For getting all accessible datacite records of schema documents type: @@ -3125,7 +3146,7 @@ As a result, you receive a list of datacite records. HTTP/1.1 200 OK Content-Range: 0-1/2 Content-Type: application/json - Content-Length: 1820 + Content-Length: 1825 [ { "id" : "another_json", @@ -3151,7 +3172,7 @@ As a result, you receive a list of datacite records. }, "dates" : [ { "id" : 2, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -3160,7 +3181,7 @@ As a result, you receive a list of datacite records. "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:27Z", + "lastUpdate" : "2024-11-25T13:50:06.097Z", "state" : "VOLATILE", "acls" : [ { "id" : 2, @@ -3191,7 +3212,7 @@ As a result, you receive a list of datacite records. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -3206,7 +3227,7 @@ As a result, you receive a list of datacite records. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:26.94Z", + "lastUpdate" : "2024-11-25T13:50:06.037Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -3231,7 +3252,7 @@ The modified HTTP request with pagination looks like follows: GET /metastore/api/v2/schemas/?page=0&size=20 HTTP/1.1 Host: localhost:8040 -### Getting a List of all Schema Records for a Specific SchemaId +### Getting a List of all Datacite Records of a Schema for a Specific SchemaId If you want to obtain all versions of a specific schema you may add the schemaId as a filter parameter. This may look like this: @@ -3249,7 +3270,7 @@ As a result, you receive a list of datacite records in descending order. HTTP/1.1 200 OK Content-Range: 0-2/3 Content-Type: application/json - Content-Length: 2845 + Content-Length: 2846 [ { "id" : "my_first_json", @@ -3275,7 +3296,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -3290,7 +3311,7 @@ As a result, you receive a list of datacite records in descending order. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:26.94Z", + "lastUpdate" : "2024-11-25T13:50:06.037Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -3321,7 +3342,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -3336,7 +3357,7 @@ As a result, you receive a list of datacite records in descending order. "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:26.854Z", + "lastUpdate" : "2024-11-25T13:50:05.941Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -3367,7 +3388,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "alternateIdentifiers" : [ { @@ -3376,7 +3397,7 @@ As a result, you receive a list of datacite records in descending order. "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:26.664Z", + "lastUpdate" : "2024-11-25T13:50:05.753Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -3399,7 +3420,7 @@ HTTP-wise the call looks as follows: Accept: application/json Host: localhost:8040 -As a result, you receive the XSD schema document sent before: +As a result, you receive the JSON schema document sent before: HTTP/1.1 200 OK Content-Type: text/plain @@ -3452,7 +3473,7 @@ HTTP-wise the call looks as follows: Accept: application/json Host: localhost:8040 -As a result, you receive the initial XSD schema document (version 1). +As a result, you receive the initial JSON schema document (version 1). HTTP/1.1 200 OK Content-Type: text/plain @@ -3586,21 +3607,21 @@ example we introduce a user called 'admin' and give him all rights. $ curl 'http://localhost:8040/metastore/api/v2/schemas/my_first_json' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "254981415"' \ + -H 'If-Match: "2066868822"' \ -F 'record=@schema-record4json-v4.json;type=application/json' Same for the HTTP request. PUT /metastore/api/v2/schemas/my_first_json HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "254981415" + If-Match: "2066868822" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=schema-record4json-v4.json Content-Type: application/json - {"id":"my_first_json","identifier":{"id":1,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":1,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":1,"value":"Title for my_first_json","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":1,"value":"JSON_Schema","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":1,"value":"2024-11-22T14:26:26Z","type":"CREATED"}],"relatedIdentifiers":[{"id":1,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=2","relationType":"IS_NEW_VERSION_OF","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":1,"value":"my_first_json","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"3","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:26.94Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}]} + {"id":"my_first_json","identifier":{"id":1,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":1,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":1,"value":"Title for my_first_json","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":1,"value":"JSON_Schema","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":1,"value":"2024-11-25T13:50:05Z","type":"CREATED"}],"relatedIdentifiers":[{"id":1,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/metadata/my_first_json?version=2","relationType":"IS_NEW_VERSION_OF","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":1,"value":"my_first_json","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"3","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-25T13:50:06.037Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":1,"sid":"SELF","permission":"ADMINISTRATE"},{"id":null,"sid":"admin","permission":"ADMINISTRATE"}]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- As a result, you receive 200 as HTTP status, the updated datacite record @@ -3608,7 +3629,7 @@ and the updated ETag and location in the HTTP response header. HTTP/1.1 200 OK Location: http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3 - ETag: "395896196" + ETag: "1277397194" Content-Type: application/json Content-Length: 1092 @@ -3636,7 +3657,7 @@ and the updated ETag and location in the HTTP response header. }, "dates" : [ { "id" : 1, - "value" : "2024-11-22T14:26:26Z", + "value" : "2024-11-25T13:50:05Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -3651,7 +3672,7 @@ and the updated ETag and location in the HTTP response header. "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:27.409Z", + "lastUpdate" : "2024-11-25T13:50:06.549Z", "state" : "VOLATILE", "acls" : [ { "id" : 1, @@ -3733,7 +3754,7 @@ At least the following elements have to be provided by the user: - relatedIdentifier/schema: Link to the related schema. (identifierType: INTERNAL and URL are supported, relationType: - IS\_DERIVED\_FROM) + HAS\_METADATA) - relatedIdentifier/data: Link to the (data) resource. (identifierType: any, relationType: IS\_METADATA\_FOR) @@ -3745,8 +3766,6 @@ If linked schema is identified by its schemaId the INTERNAL type has to be used. It’s then linked to the current schema version at creation time. -License URI is optional. It’s new since 1.4.2. - ### Register/Ingest a Datacite Record with Metadata Document The following example shows the creation of the first metadata document @@ -3759,8 +3778,6 @@ and its datacite record only providing mandatory fields mentioned above: "value": "Title of first metadata document", } ], - "publisher": null, - "publicationYear": null, "resourceType": { "value": "JSON_Metadata", "typeGeneral": "MODEL" @@ -3769,7 +3786,7 @@ and its datacite record only providing mandatory fields mentioned above: { "identifierType": "URL", "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=1", - "relationType": "IS_DERIVED_FROM" + "relationType": "HAS_METADATA" }, { "identifierType": "URL", @@ -3821,13 +3838,13 @@ possible and persisting the created resource, the result is sent back to the user and will look that way: HTTP/1.1 201 Created - Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 - ETag: "536402562" + Location: http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1 + ETag: "2079736072" Content-Type: application/json - Content-Length: 1205 + Content-Length: 1206 { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -3850,7 +3867,7 @@ the user and will look that way: }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -3866,11 +3883,11 @@ the user and will look that way: } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:27.43Z", + "lastUpdate" : "2024-11-25T13:50:06.577Z", "state" : "VOLATILE", "acls" : [ { "id" : 4, @@ -3892,12 +3909,12 @@ conflicts. For accessing the metadata the location URL provided before may be used. The URL is compiled by the id of the metadata and its version. - $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1' -i -X GET \ -H 'Accept: application/json' HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 HTTP/1.1 + GET /metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1 HTTP/1.1 Accept: application/json Host: localhost:8040 @@ -3925,12 +3942,12 @@ The only difference is the content type. It has to be set to "application/vnd.datacite.org+json". Then the command line looks like this: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1' -i -X GET \ -H 'Accept: application/vnd.datacite.org+json' HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 HTTP/1.1 + GET /metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1 HTTP/1.1 Accept: application/vnd.datacite.org+json Host: localhost:8040 @@ -3938,13 +3955,13 @@ The linked metadata will be returned. The result is sent back to the user and will look that way: HTTP/1.1 200 OK - ETag: "536402562" - Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 + ETag: "2079736072" + Location: http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1 Content-Type: application/vnd.datacite.org+json - Content-Length: 1205 + Content-Length: 1206 { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -3967,7 +3984,7 @@ user and will look that way: }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -3983,11 +4000,11 @@ user and will look that way: } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:27.43Z", + "lastUpdate" : "2024-11-25T13:50:06.577Z", "state" : "VOLATILE", "acls" : [ { "id" : 4, @@ -4013,7 +4030,7 @@ mentioned before the ETag is needed: "id": 1, "identifierType": "URL", "value": "http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2", - "relationType": "IS_DERIVED_FROM" + "relationType": "HAS_METADATA" } ], [...] @@ -4032,9 +4049,9 @@ mentioned before the ETag is needed: "date": "2018-07-02" } - $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "536402562"' \ + -H 'If-Match: "2079736072"' \ -F 'record=@metadata-record4json-v2.json;type=application/json' \ -F 'document=@metadata-v2.json;type=application/xml' @@ -4042,16 +4059,16 @@ You can see, that the schema was set to version 2 (allowing additional field for date) and the ACL entry for "guest" was added. All other properties are still the same. HTTP-wise the call looks as follows: - PUT /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1 HTTP/1.1 + PUT /metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "536402562" + If-Match: "2079736072" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record4json-v2.json Content-Type: application/json - {"id":"a7484515-ba87-46e2-8a21-a1600039346d","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first JSON metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"JSON_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:27Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"a7484515-ba87-46e2-8a21-a1600039346d","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:27.43Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + {"id":"d9c917ee-160e-460c-b169-8f952ab505c7","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first JSON metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"JSON_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-25T13:50:06Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=2","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"d9c917ee-160e-460c-b169-8f952ab505c7","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-25T13:50:06.577Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v2.json Content-Type: application/xml @@ -4065,13 +4082,13 @@ properties are still the same. HTTP-wise the call looks as follows: The response provides the updated datacite record: HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2 - ETag: "2057586597" + Location: http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2 + ETag: "-324611405" Content-Type: application/json - Content-Length: 1478 + Content-Length: 1477 { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4094,7 +4111,7 @@ The response provides the updated datacite record: }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4105,7 +4122,7 @@ The response provides the updated datacite record: }, { "id" : 4, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1", + "value" : "http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -4115,11 +4132,11 @@ The response provides the updated datacite record: } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:27.556Z", + "lastUpdate" : "2024-11-25T13:50:06.71Z", "state" : "VOLATILE", "acls" : [ { "id" : 5, @@ -4142,25 +4159,25 @@ Repeat the last step and update to the current version. As mentioned before the ETag is needed. As the ETag has changed in the meanwhile you first have to get the new ETag. - $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2' -i -X GET \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2' -i -X GET \ -H 'Accept: application/vnd.datacite.org+json' HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2 HTTP/1.1 + GET /metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2 HTTP/1.1 Accept: application/vnd.datacite.org+json Host: localhost:8040 You will get the new datacite record with the new ETag. HTTP/1.1 200 OK - ETag: "2057586597" - Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2 + ETag: "-324611405" + Location: http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2 Content-Type: application/vnd.datacite.org+json - Content-Length: 1478 + Content-Length: 1477 { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4183,7 +4200,7 @@ You will get the new datacite record with the new ETag. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4194,7 +4211,7 @@ You will get the new datacite record with the new ETag. }, { "id" : 4, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1", + "value" : "http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -4204,11 +4221,11 @@ You will get the new datacite record with the new ETag. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:27.556Z", + "lastUpdate" : "2024-11-25T13:50:06.71Z", "state" : "VOLATILE", "acls" : [ { "id" : 5, @@ -4234,7 +4251,7 @@ Etag. "id": 1, "identifierType": "INTERNAL", "value": "my_first_json", - "relationType": "IS_DERIVED_FROM" + "relationType": "HAS_METADATA" } ], [...] @@ -4258,24 +4275,24 @@ version 3). "note": "since version 3 notes are allowed" } - $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d' -i -X PUT \ + $ curl 'http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7' -i -X PUT \ -H 'Content-Type: multipart/form-data' \ - -H 'If-Match: "2057586597"' \ + -H 'If-Match: "-324611405"' \ -F 'record=@metadata-record4json-v3.json;type=application/json' \ -F 'document=@metadata-v3.json;type=application/json' HTTP-wise the call looks as follows: - PUT /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d HTTP/1.1 + PUT /metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7 HTTP/1.1 Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm - If-Match: "2057586597" + If-Match: "-324611405" Host: localhost:8040 --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=record; filename=metadata-record4json-v3.json Content-Type: application/json - {"id":"a7484515-ba87-46e2-8a21-a1600039346d","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first JSON metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"JSON_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-22T14:26:27Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"a7484515-ba87-46e2-8a21-a1600039346d","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-22T14:26:27.43Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} + {"id":"d9c917ee-160e-460c-b169-8f952ab505c7","identifier":{"id":3,"value":"(:tba)","identifierType":"DOI"},"creators":[{"id":3,"familyName":null,"givenName":"SELF","affiliations":[]}],"titles":[{"id":3,"value":"Title of first JSON metadata document","titleType":null,"lang":null}],"publisher":"SELF","publicationYear":"2024","resourceType":{"id":3,"value":"JSON_Metadata","typeGeneral":"MODEL"},"subjects":[],"contributors":[],"dates":[{"id":3,"value":"2024-11-25T13:50:06Z","type":"CREATED"}],"relatedIdentifiers":[{"id":2,"identifierType":"URL","value":"https://repo/anyResourceId","relationType":"IS_METADATA_FOR","scheme":null,"relatedMetadataScheme":null},{"id":3,"identifierType":"URL","value":"http://localhost:8040/metastore/api/v2/schemas/my_first_json?version=3","relationType":"HAS_METADATA","scheme":null,"relatedMetadataScheme":null}],"descriptions":[],"geoLocations":[],"language":null,"alternateIdentifiers":[{"id":3,"value":"d9c917ee-160e-460c-b169-8f952ab505c7","identifierType":"INTERNAL"}],"sizes":[],"formats":[],"version":"1","rights":[],"fundingReferences":[],"lastUpdate":"2024-11-25T13:50:06.577Z","state":"VOLATILE","embargoDate":null,"acls":[{"id":null,"sid":"guest","permission":"READ"},{"id":4,"sid":"SELF","permission":"ADMINISTRATE"}]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm Content-Disposition: form-data; name=document; filename=metadata-v3.json Content-Type: application/json @@ -4290,13 +4307,13 @@ HTTP-wise the call looks as follows: You will get the new datacite record. HTTP/1.1 200 OK - Location: http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=3 - ETag: "1038379398" + Location: http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=3 + ETag: "973115988" Content-Type: application/json Content-Length: 1478 { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4319,7 +4336,7 @@ You will get the new datacite record. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4330,7 +4347,7 @@ You will get the new datacite record. }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -4340,11 +4357,11 @@ You will get the new datacite record. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:27.626Z", + "lastUpdate" : "2024-11-25T13:50:06.777Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -4360,11 +4377,11 @@ You will get the new datacite record. Now you can access the updated metadata via the URI in the HTTP response header. - $ curl 'http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=3' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=3' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=3 HTTP/1.1 + GET /metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=3 HTTP/1.1 Host: localhost:8040 You will get the updated metadata. @@ -4408,11 +4425,11 @@ size as additional query parameters. If you want to obtain all versions of a specific resource you may add 'id' as a filter parameter. This may look like this: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/?id=a7484515-ba87-46e2-8a21-a1600039346d' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?id=d9c917ee-160e-460c-b169-8f952ab505c7' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/?id=a7484515-ba87-46e2-8a21-a1600039346d HTTP/1.1 + GET /metastore/api/v2/metadata/?id=d9c917ee-160e-460c-b169-8f952ab505c7 HTTP/1.1 Host: localhost:8040 As a result, you receive a list of datacite records in descending order. @@ -4424,7 +4441,7 @@ As a result, you receive a list of datacite records in descending order. Content-Length: 4169 [ { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4447,7 +4464,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4458,7 +4475,7 @@ As a result, you receive a list of datacite records in descending order. }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -4468,11 +4485,11 @@ As a result, you receive a list of datacite records in descending order. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:27.626Z", + "lastUpdate" : "2024-11-25T13:50:06.777Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -4484,7 +4501,7 @@ As a result, you receive a list of datacite records in descending order. "permission" : "ADMINISTRATE" } ] }, { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4507,7 +4524,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4518,7 +4535,7 @@ As a result, you receive a list of datacite records in descending order. }, { "id" : 4, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=1", + "value" : "http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=1", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -4528,11 +4545,11 @@ As a result, you receive a list of datacite records in descending order. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "2", - "lastUpdate" : "2024-11-22T14:26:27.556Z", + "lastUpdate" : "2024-11-25T13:50:06.71Z", "state" : "VOLATILE", "acls" : [ { "id" : 5, @@ -4544,7 +4561,7 @@ As a result, you receive a list of datacite records in descending order. "permission" : "ADMINISTRATE" } ] }, { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4567,7 +4584,7 @@ As a result, you receive a list of datacite records in descending order. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4583,11 +4600,11 @@ As a result, you receive a list of datacite records in descending order. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "1", - "lastUpdate" : "2024-11-22T14:26:27.43Z", + "lastUpdate" : "2024-11-25T13:50:06.577Z", "state" : "VOLATILE", "acls" : [ { "id" : 4, @@ -4600,7 +4617,6 @@ As a result, you receive a list of datacite records in descending order. If you want to find all records belonging to an external resource. MetaStore may hold multiple metadata documents per resource. -(Nevertheless only one per registered schema) Command line: @@ -4619,7 +4635,7 @@ You will get the current version datacite record. Content-Length: 1482 [ { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4642,7 +4658,7 @@ You will get the current version datacite record. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4653,7 +4669,7 @@ You will get the current version datacite record. }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -4663,11 +4679,11 @@ You will get the current version datacite record. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:27.626Z", + "lastUpdate" : "2024-11-25T13:50:06.777Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -4686,11 +4702,11 @@ If you want to find all datacite records updated after a specific date. Command line: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A06.893252207Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z HTTP/1.1 + GET /metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A06.893252207Z HTTP/1.1 Host: localhost:8040 You will get the current version datacite records updated ln the last 2 @@ -4702,7 +4718,7 @@ hours. Content-Length: 1482 [ { - "id" : "a7484515-ba87-46e2-8a21-a1600039346d", + "id" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifier" : { "id" : 3, "value" : "(:tba)", @@ -4725,7 +4741,7 @@ hours. }, "dates" : [ { "id" : 3, - "value" : "2024-11-22T14:26:27Z", + "value" : "2024-11-25T13:50:06Z", "type" : "CREATED" } ], "relatedIdentifiers" : [ { @@ -4736,7 +4752,7 @@ hours. }, { "id" : 5, "identifierType" : "URL", - "value" : "http://localhost:8040/metastore/api/v2/metadata/a7484515-ba87-46e2-8a21-a1600039346d?version=2", + "value" : "http://localhost:8040/metastore/api/v2/metadata/d9c917ee-160e-460c-b169-8f952ab505c7?version=2", "relationType" : "IS_NEW_VERSION_OF" }, { "id" : 3, @@ -4746,11 +4762,11 @@ hours. } ], "alternateIdentifiers" : [ { "id" : 3, - "value" : "a7484515-ba87-46e2-8a21-a1600039346d", + "value" : "d9c917ee-160e-460c-b169-8f952ab505c7", "identifierType" : "INTERNAL" } ], "version" : "3", - "lastUpdate" : "2024-11-22T14:26:27.626Z", + "lastUpdate" : "2024-11-25T13:50:06.777Z", "state" : "VOLATILE", "acls" : [ { "id" : 6, @@ -4770,11 +4786,11 @@ range. Command line: - $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z&until=2024-11-22T13%3A26%3A27.730516191Z' -i -X GET + $ curl 'http://localhost:8040/metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A06.893252207Z&until=2024-11-25T12%3A50%3A06.893247495Z' -i -X GET HTTP-wise the call looks as follows: - GET /metastore/api/v2/metadata/?from=2024-11-22T12%3A26%3A27.730520539Z&until=2024-11-22T13%3A26%3A27.730516191Z HTTP/1.1 + GET /metastore/api/v2/metadata/?from=2024-11-25T11%3A50%3A06.893252207Z&until=2024-11-25T12%3A50%3A06.893247495Z HTTP/1.1 Host: localhost:8040 You will get an empty array as no datacite record exists in the given From c9c4d62170ab1c4566d99e3ab82b582bdd6f36e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:09:54 +0000 Subject: [PATCH 177/181] Update dependency org.springframework.cloud:spring-cloud-starter-config to v4.1.4 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 44414aef..d4773445 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ dependencies { // cloud support - implementation "org.springframework.cloud:spring-cloud-starter-config:4.1.3" + implementation "org.springframework.cloud:spring-cloud-starter-config:4.1.4" implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.4" // springdoc From c7981fe33ceded51f3c4aaca029cab832f28c46b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:47:14 +0000 Subject: [PATCH 178/181] Update dependency com.networknt:json-schema-validator to v1.5.4 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d4773445..2785c8cf 100644 --- a/build.gradle +++ b/build.gradle @@ -103,7 +103,7 @@ dependencies { implementation "org.apache.tika:tika-core:3.0.0" // JSON validator - implementation "com.networknt:json-schema-validator:1.5.3" + implementation "com.networknt:json-schema-validator:1.5.4" // XML validator // https://mvnrepository.com/artifact/xerces/xercesImpl implementation "xerces:xercesImpl:2.12.2" From d0c3628b88e445ef131eb11952a7861e098af5fd Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Wed, 27 Nov 2024 16:09:01 +0100 Subject: [PATCH 179/181] Add latest updates for dependencies. --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a082df32..6a102ee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -## [2.0.0] - 2024-11-25 +## [2.0.0] - 2024-11-29 ### Security ### Added @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - DataCite is now supported as administrative metadata (API V2) +- Update to Java 21 - Update dependency gradle to v8.11.1 - Update rabbitmq Docker tag to v4 - Update eclipse-temurin Docker tag to v23 @@ -28,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Libs - Update dependency ajv to v8.17.1 - Update dependency com.google.errorprone:error_prone_core to v2.36.0 -- Update dependency com.networknt:json-schema-validator to v1.5.3 +- Update dependency com.networknt:json-schema-validator to v1.5.4 - Update dependency commons-io:commons-io to v2.18.0 - Update dependency edu.kit.datamanager:repo-core to v1.2.3 - Update dependency edu.kit.datamanager:service-base to v1.3.2 @@ -45,8 +46,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update dependency org.springframework.data:spring-data-elasticsearch to v5.4.0 - Update dependency org.springframework.restdocs:spring-restdocs-mockmvc to v3.0.3 - Update dependency org.springframework:spring-messaging to v6.2.0 +- Update dependency org.springframework.cloud:spring-cloud-gateway-mvc to v4.1.6 - Update dependency org.springframework.cloud:spring-cloud-starter-config to v4.1.3 -- Update dependency org.springframework.cloud:spring-cloud-starter-netflix-eureka-client to v4.1.3 +- Update dependency org.springframework.cloud:spring-cloud-starter-netflix-eureka-client to v4.1.4 - Bump org.springframework.data:spring-data-elasticsearch from 5.3.3 to 5.3.4. ### Deprecated From c45db0f1449e23d328801177da50c2c4758d0c37 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 29 Nov 2024 08:16:01 +0100 Subject: [PATCH 180/181] Update CHANGELOG.md --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a102ee6..ecf5a892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ## [2.0.0] - 2024-11-29 -### Security ### Added -- API V2 +- REST API V2 ### Changed - DataCite is now supported as administrative metadata (API V2) @@ -528,7 +527,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Registry for XSD files and support for XML metadata -[Unreleased]: https://github.com/kit-data-manager/metastore2/compare/v1.4.4...HEAD +[Unreleased]: https://github.com/kit-data-manager/metastore2/compare/v2.0.0...HEAD [2.0.0]: https://github.com/kit-data-manager/metastore2/compare/v1.4.4...v2.0.0 [1.4.4]: https://github.com/kit-data-manager/metastore2/compare/v1.4.3...v1.4.4 [1.4.3]: https://github.com/kit-data-manager/metastore2/compare/v1.4.2...v1.4.3 From 780f6cbf08ceaf06c3e24deb108fe22dcf58d583 Mon Sep 17 00:00:00 2001 From: Volker Hartmann <volker.hartmann@kit.edu> Date: Fri, 29 Nov 2024 08:20:24 +0100 Subject: [PATCH 181/181] Update CITATION.cff --- CITATION.cff | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 57c2874c..8bb52b82 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -19,8 +19,8 @@ authors: title: "MetaStore" type: software abstract: MetaStore is a research data repository software for storing metadata documents and schemas. Quality and consistency are ensured by associating and validating each document against a schema. It supports JSON and XML. -version: 1.4.4 -date-released: 2024-08-20 +version: 2.0.0 +date-released: 2024-11-29 doi: 10.5281/zenodo.7685007 url: "https://github.com/kit-data-manager/metastore2" repository-code: "https://github.com/kit-data-manager/metastore2"