diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5cd7168f9..06f7940bf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: options: --name mongo steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Fetch all history for all tags and branches run: git fetch --prune --unshallow @@ -38,13 +38,13 @@ jobs: # (1) -> Prepare cache and Java - name: Cache ~/.m2 - uses: actions/cache@v2.1.7 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - name: Cache JDK folder - uses: actions/cache@v2.1.7 + uses: actions/cache@v3 with: path: ~/jdk key: ${{ env.JDK_FILE }} @@ -58,7 +58,7 @@ jobs: cp ~/jdk/$JDK_FILE . - name: Setup Java - uses: actions/setup-java@v2.5.0 + uses: actions/setup-java@v3 with: distribution: 'jdkfile' java-version: ${{ env.JDK_VERSION }} diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 000000000..fffe4747b --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,131 @@ +name: "Security Audit" + +on: + push: + branches: [ develop, master ] + pull_request: + branches: [ develop ] + schedule: + - cron: '23 4 * * 1' + +jobs: + codeql: + name: CodeQL + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + env: + JDK_VERSION: 17 + JDK_FILE: openjdk-17_linux-x64_bin.tar.gz + JDK_URL: https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_linux-x64_bin.tar.gz + + steps: + - uses: actions/checkout@v2 + + - name: Fetch all history for all tags and branches + run: git fetch --prune --unshallow + + - name: Prepare JDK folder + run: mkdir -p ~/jdk + + # (1) -> Prepare cache and Java + - name: Cache ~/.m2 + uses: actions/cache@v2.1.7 + with: + path: ~/.m2 + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + + - name: Cache JDK folder + uses: actions/cache@v2.1.7 + with: + path: ~/jdk + key: ${{ env.JDK_FILE }} + + # (2) -> Prepare Java + - name: Download JDK + run: | + if [ ! -f ~/jdk/$JDK_FILE ]; then + wget --quiet $JDK_URL -O ~/jdk/$JDK_FILE + fi + cp ~/jdk/$JDK_FILE . + + - name: Setup Java + uses: actions/setup-java@v2.4.0 + with: + distribution: 'jdkfile' + java-version: ${{ env.JDK_VERSION }} + jdkFile: ${{ env.JDK_FILE }} + architecture: x64 + + - name: Verify Maven and Java + run: | + mvn --version + + # (3) -> Init CodeQL + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: 'java' + + # (3) -> Build + - name: Build package + run: | + mvn --quiet -B -U --fail-fast -DskipTests package + + # (4) -> CodeQL Analysis + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 + + snyk: + name: Snyk (Maven) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + + - name: Checkout repository + uses: actions/checkout@master + + - name: Perform Snyk Check (Maven) + uses: snyk/actions/maven@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --severity-threshold=high + + snyk-docker: + name: Snyk (Docker) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + env: + PUBLIC_IMAGE: fairdata/fairdatapoint + TAG_DEVELOP: develop + + steps: + + - name: Checkout repository + uses: actions/checkout@master + + - name: Docker build + run: | + docker pull $PUBLIC_IMAGE:$TAG_DEVELOP + docker build --cache-from $PUBLIC_IMAGE:$TAG_DEVELOP -t fdp:snyk-test -f Dockerfile.build . + + - name: Perform Snyk Check (Docker) + uses: snyk/actions/docker@master + continue-on-error: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: fdp:snyk-test + args: --severity-threshold=high diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c0e55007..98b83cbed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [1.14.0] + +### Added + +- Security audit via GitHub Actions (Snyk and CodeQL) + +### Changed + +- Introduced metadata schemas (as replacement of shapes) including versioning and importing +- Updated RDF4J to 4.0 +- Several dependencies updated + ## [1.13.2] ### Fixed @@ -36,7 +48,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed -- Missing `xsd` prefix in some default shapes +- Missing `xsd` prefix in some default metadataSchemas ## [1.12.4] @@ -92,13 +104,13 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Changed -- Resource definitions are related directly to shapes +- Resource definitions are related directly to metadataSchemas ## [1.10.0] ### Added -- Allow to change internal shapes +- Allow to change internal metadataSchemas - Reset to "factory defaults" (users, resource definitions, metadata) ### Changed @@ -109,7 +121,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added -- Publishing and sharing SHACL shapes between FDPs +- Publishing and sharing SHACL metadataSchemas between FDPs - Pagination for child resources ### Changed @@ -193,8 +205,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Shape definitions with DASH support - Endpoint for bootstrapping [Client] -- Validation for SHACL definitions in shapes -- Production migration for shape definitions +- Validation for SHACL definitions in metadataSchemas +- Production migration for metadataSchema definitions ### Changed @@ -300,3 +312,5 @@ The first release of reference FAIR Data Point implementation. [1.12.4]: /../../tree/v1.12.4 [1.13.0]: /../../tree/v1.13.0 [1.13.1]: /../../tree/v1.13.1 +[1.13.2]: /../../tree/v1.13.2 +[1.14.0]: /../../tree/v1.14.0 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..2a25d7817 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +We support the latest major and minor version with patch versions that fix vulnerabilities and critical bugs. For older versions, we highly recommend upgrading to the latest version. + +| Version | Supported | +|---------| ------------------ | +| 1.14.0 | :white_check_mark: | +| < 1.14 | :x: | + +## Current Recommendations + +* Use 1.14.0 with the newest dependencies (and no known vulnerabilities) + +## Reporting a Vulnerability + +In case you encounter a vulnerability, please let us know via [GitHub issues](https://github.com/FAIRDataTeam/FAIRDataPoint/issues). If you need to share sensitive information, indicate that in the issue and we will provide a secured channel how you can privately send us such information. diff --git a/pom.xml b/pom.xml index 04c9031ee..6a5b83371 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ org.springframework.boot spring-boot-starter-parent - 2.6.6 + 2.7.0 nl.dtls fairdatapoint - 1.13.2 + 1.14.0 jar FairDataPoint @@ -51,17 +51,16 @@ 17 - 1.1.0.RELEASE + 1.2.0.RELEASE 5.2.4.RELEASE - 1.6.5 - 5.0.35 - 3.3.1 - 3.7.4 - 1.4.9 - 0.11.2 - 1.18.22 + 1.6.9 + 5.1.0 + 3.4.0 + 4.0.2 + 0.11.5 + 1.18.24 0.1.2-SNAPSHOT 2.17.1 @@ -222,11 +221,6 @@ rdf4j-sail-nativerdf ${rdf4j.version} - - com.mashape.unirest - unirest-java - ${unirest.version} - io.jsonwebtoken jjwt-api diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/apikey/ApiKeyController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/apikey/ApiKeyController.java index 1f1fadc3b..716e6cae3 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/controller/apikey/ApiKeyController.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/apikey/ApiKeyController.java @@ -59,7 +59,7 @@ public ResponseEntity createApiKey() { @DeleteMapping("/{uuid}") @ResponseStatus(HttpStatus.NO_CONTENT) - public ResponseEntity deleteShape(@PathVariable final String uuid) throws ResourceNotFoundException { + public ResponseEntity deleteApiKey(@PathVariable final String uuid) throws ResourceNotFoundException { boolean result = apiKeyService.delete(uuid); if (result) { return ResponseEntity.noContent().build(); diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/exception/ExceptionControllerAdvice.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/exception/ExceptionControllerAdvice.java index c07683efc..96fbe86cb 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/controller/exception/ExceptionControllerAdvice.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/exception/ExceptionControllerAdvice.java @@ -72,6 +72,7 @@ public class ExceptionControllerAdvice { ) public ErrorDTO handleBadRequest(Exception e) { log.warn(e.getMessage()); + log.debug("Handling bad request (ValidationException)", e); return new ErrorDTO(HttpStatus.BAD_REQUEST, e.getMessage()); } @@ -88,6 +89,7 @@ public ErrorDTO handleBadRequest(Exception e) { ) public Model handleBadRequest(RdfValidationException e) { Model validationReportModel = e.getModel(); + log.debug("Handling bad request (RdfValidationException)", e); // Log number of errors IRI validationResultIri = i("http://www.w3.org/ns/shacl#ValidationResult"); @@ -115,6 +117,7 @@ public Model handleBadRequest(RdfValidationException e) { ) public ErrorDTO handleUnauthorized(Exception e) { log.error(e.getMessage()); + e.printStackTrace(); return new ErrorDTO(HttpStatus.UNAUTHORIZED, e.getMessage()); } @@ -131,6 +134,7 @@ public ErrorDTO handleUnauthorized(Exception e) { ) public ErrorDTO handleForbidden(Exception e) { log.error(e.getMessage()); + log.debug("Handling forbidden", e); return new ErrorDTO(HttpStatus.FORBIDDEN, e.getMessage()); } @@ -147,6 +151,7 @@ public ErrorDTO handleForbidden(Exception e) { ) public ErrorDTO handleResourceNotFound(ResourceNotFoundException e) { log.error(e.getMessage()); + log.debug("Handling resource not found", e); return new ErrorDTO(HttpStatus.NOT_FOUND, e.getMessage()); } @@ -163,12 +168,14 @@ public ErrorDTO handleResourceNotFound(ResourceNotFoundException e) { ) public ErrorDTO handleInternalServerError(Exception e) { log.error(e.getMessage()); + log.debug("Handling internal server error (MetadataServiceException)", e); return new ErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); } @ExceptionHandler(IndexException.class) - public ResponseEntity handleIndexException(IndexException exception) { - return new ResponseEntity<>(exception.getErrorDTO(), exception.getStatus()); + public ResponseEntity handleIndexException(IndexException e) { + log.debug("Handling index exception", e); + return new ResponseEntity<>(e.getErrorDTO(), e.getStatus()); } } diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericController.java index 55e8cee6c..47738a3e3 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericController.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericController.java @@ -39,7 +39,7 @@ import nl.dtls.fairdatapoint.service.metadata.factory.MetadataServiceFactory; import nl.dtls.fairdatapoint.service.metadata.state.MetadataStateService; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionService; -import nl.dtls.fairdatapoint.service.shape.ShapeService; +import nl.dtls.fairdatapoint.service.schema.MetadataSchemaService; import nl.dtls.fairdatapoint.service.user.CurrentUserService; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Model; @@ -82,7 +82,7 @@ public class GenericController { private ResourceDefinitionService resourceDefinitionService; @Autowired - private ShapeService shapeService; + private MetadataSchemaService metadataSchemaService; @Autowired private MetadataStateService metadataStateService; @@ -101,7 +101,9 @@ public class GenericController { public Model getFormMetadata( @PathVariable final Optional oUrlPrefix ) { - return shapeService.getShaclFromShapes(); + String urlPrefix = oUrlPrefix.orElse(""); + ResourceDefinition rd = resourceDefinitionService.getByUrlPrefix(urlPrefix); + return metadataSchemaService.getShaclFromSchemas(rd.getMetadataSchemaUuids()); } @Operation(hidden = true, deprecated = true) diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/profile/ProfileController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/profile/ProfileController.java index 970871a9b..04174f1ab 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/controller/profile/ProfileController.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/profile/ProfileController.java @@ -63,7 +63,7 @@ public class ProfileController { "application/xml", "text/xml", }) - public ResponseEntity getShapeContent(HttpServletRequest request, @PathVariable final String uuid) + public ResponseEntity getSchemaContent(HttpServletRequest request, @PathVariable final String uuid) throws ResourceNotFoundException { IRI uri = i(getRequestURL(request, persistentUrl)); Optional oDto = profileService.getProfileByUuid(uuid, uri); diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/schema/MetadataSchemaController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/schema/MetadataSchemaController.java new file mode 100644 index 000000000..64f8463d2 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/schema/MetadataSchemaController.java @@ -0,0 +1,246 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.api.controller.schema; + +import io.swagger.v3.oas.annotations.tags.Tag; +import nl.dtls.fairdatapoint.api.dto.schema.*; +import nl.dtls.fairdatapoint.entity.exception.ResourceNotFoundException; +import nl.dtls.fairdatapoint.entity.exception.UnauthorizedException; +import nl.dtls.fairdatapoint.service.schema.MetadataSchemaService; +import nl.dtls.fairdatapoint.service.user.CurrentUserService; +import org.eclipse.rdf4j.model.Model; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; +import java.util.Optional; + +import static java.lang.String.format; + +@Tag(name = "Metadata Model") +@RestController +@RequestMapping("/metadata-schemas") +public class MetadataSchemaController { + + private static final String NOT_FOUND_MSG = "Metadata schema '%s' doesn't exist"; + private static final String NOT_FOUND_VERSION_MSG = "Metadata Schema '%s' doesn't exist with version '%s'"; + + @Autowired + private MetadataSchemaService metadataSchemaService; + + @Autowired + private CurrentUserService currentUserService; + + @GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getSchemas( + @RequestParam(name = "drafts", required = false, defaultValue = "false") boolean includeDrafts, + @RequestParam(name = "abstract", required = false, defaultValue = "true") boolean includeAbstract + ) throws UnauthorizedException { + if (includeDrafts && currentUserService.isAdmin()) { + return new ResponseEntity<>(metadataSchemaService.getSchemasWithDrafts(includeAbstract), HttpStatus.OK); + } else if (includeDrafts) { + throw new UnauthorizedException("Unauthorized to see drafts of metadata schemas"); + } + return new ResponseEntity<>(metadataSchemaService.getSchemasWithoutDrafts(includeAbstract), HttpStatus.OK); + } + + @GetMapping(path = "/{uuid}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getSchema(@PathVariable final String uuid) + throws ResourceNotFoundException { + Optional oDto = metadataSchemaService.getSchemaByUuid(uuid); + if (oDto.isPresent()) { + return new ResponseEntity<>(oDto.get(), HttpStatus.OK); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_MSG, uuid)); + } + } + + @GetMapping(path = "/{uuid}", produces = { + "text/turtle", + "application/x-turtle", + "text/n3", + "text/rdf+n3", + "application/ld+json", + "application/rdf+xml", + "application/xml", + "text/xml", + }) + public ResponseEntity getSchemaContent(@PathVariable final String uuid) + throws ResourceNotFoundException { + Optional oDto = metadataSchemaService.getSchemaContentByUuid(uuid); + if (oDto.isPresent()) { + return new ResponseEntity<>(oDto.get(), HttpStatus.OK); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_MSG, uuid)); + } + } + + @DeleteMapping(path = "/{uuid}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public ResponseEntity deleteSchemaFull( + @PathVariable final String uuid + ) throws ResourceNotFoundException { + boolean result = metadataSchemaService.deleteSchemaFull(uuid); + if (result) { + return ResponseEntity.noContent().build(); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_MSG, uuid)); + } + } + + @PreAuthorize("hasRole('ADMIN')") + @PostMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createSchemaDraft(@RequestBody @Valid MetadataSchemaChangeDTO reqDto) { + MetadataSchemaDraftDTO dto = metadataSchemaService.createSchemaDraft(reqDto); + return new ResponseEntity<>(dto, HttpStatus.OK); + } + + @PreAuthorize("hasRole('ADMIN')") + @GetMapping(path = "/{uuid}/draft", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getSchemaDraft(@PathVariable final String uuid) throws ResourceNotFoundException { + Optional oDto = metadataSchemaService.getSchemaDraft(uuid); + if (oDto.isPresent()) { + return new ResponseEntity<>(oDto.get(), HttpStatus.OK); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_MSG, uuid)); + } + } + + @PreAuthorize("hasRole('ADMIN')") + @PutMapping(path = "/{uuid}/draft", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateSchemaDraft( + @PathVariable final String uuid, + @RequestBody @Valid MetadataSchemaChangeDTO reqDto + ) throws ResourceNotFoundException { + Optional oDto = metadataSchemaService.updateSchemaDraft(uuid, reqDto); + if (oDto.isPresent()) { + return new ResponseEntity<>(oDto.get(), HttpStatus.OK); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_MSG, uuid)); + } + } + + @PreAuthorize("hasRole('ADMIN')") + @DeleteMapping(path = "/{uuid}/draft") + @ResponseStatus(HttpStatus.NO_CONTENT) + public ResponseEntity deleteSchemaDraft(@PathVariable final String uuid) + throws ResourceNotFoundException { + boolean result = metadataSchemaService.deleteSchemaDraft(uuid); + if (result) { + return ResponseEntity.noContent().build(); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_MSG, uuid)); + } + } + + @PreAuthorize("hasRole('ADMIN')") + @PostMapping(path = "/{uuid}/versions") + @ResponseStatus(HttpStatus.NO_CONTENT) + public ResponseEntity releaseSchemaVersion( + @PathVariable final String uuid, + @RequestBody @Valid MetadataSchemaReleaseDTO reqDto + ) throws ResourceNotFoundException { + Optional oDto = metadataSchemaService.releaseDraft(uuid, reqDto); + if (oDto.isPresent()) { + return new ResponseEntity<>(oDto.get(), HttpStatus.OK); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_MSG, uuid)); + } + } + + @PreAuthorize("hasRole('ADMIN')") + @GetMapping(path = "/{uuid}/versions/{version}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getSchemaVersion( + @PathVariable final String uuid, + @PathVariable final String version + ) throws ResourceNotFoundException { + Optional oDto = metadataSchemaService.getVersion(uuid, version); + if (oDto.isPresent()) { + return new ResponseEntity<>(oDto.get(), HttpStatus.OK); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_VERSION_MSG, uuid, version)); + } + } + + @PreAuthorize("hasRole('ADMIN')") + @PutMapping(path = "/{uuid}/versions/{version}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateSchemaVersion( + @PathVariable final String uuid, + @PathVariable final String version, + @RequestBody @Valid MetadataSchemaUpdateDTO reqDto + ) throws ResourceNotFoundException { + Optional oDto = metadataSchemaService.updateVersion(uuid, version, reqDto); + if (oDto.isPresent()) { + return new ResponseEntity<>(oDto.get(), HttpStatus.OK); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_VERSION_MSG, uuid, version)); + } + } + + @PreAuthorize("hasRole('ADMIN')") + @DeleteMapping(path = "/{uuid}/versions/{version}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public ResponseEntity deleteSchemaVersion( + @PathVariable final String uuid, + @PathVariable final String version + ) throws ResourceNotFoundException { + boolean result = metadataSchemaService.deleteVersion(uuid, version); + if (result) { + return ResponseEntity.noContent().build(); + } else { + throw new ResourceNotFoundException(format(NOT_FOUND_VERSION_MSG, uuid, version)); + } + } + + @GetMapping(path = "/public", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getPublishedSchemas() { + List dto = metadataSchemaService.getPublishedSchemas(); + return new ResponseEntity<>(dto, HttpStatus.OK); + } + + @PreAuthorize("hasRole('ADMIN')") + @GetMapping(path = "/import", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getImportableSchemas(@RequestParam(name = "from") String fdpUrl) { + List dto = metadataSchemaService.getRemoteSchemas(fdpUrl); + return new ResponseEntity<>(dto, HttpStatus.OK); + } + + @PreAuthorize("hasRole('ADMIN')") + @PostMapping(path = "/import", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> importSchemas(@RequestBody @Valid List<@Valid MetadataSchemaVersionDTO> reqDtos) { + List dto = metadataSchemaService.importSchemas(reqDtos); + return new ResponseEntity<>(dto, HttpStatus.OK); + } + + @PreAuthorize("hasRole('ADMIN')") + @GetMapping(path = "/updates", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> checkForUpdates() { + List dtos = metadataSchemaService.checkForUpdates(); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/shape/ShapeController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/shape/ShapeController.java deleted file mode 100644 index bd9a2912c..000000000 --- a/src/main/java/nl/dtls/fairdatapoint/api/controller/shape/ShapeController.java +++ /dev/null @@ -1,142 +0,0 @@ -/** - * The MIT License - * Copyright © 2017 DTL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package nl.dtls.fairdatapoint.api.controller.shape; - -import io.swagger.v3.oas.annotations.tags.Tag; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeChangeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeRemoteDTO; -import nl.dtls.fairdatapoint.entity.exception.ResourceNotFoundException; -import nl.dtls.fairdatapoint.service.shape.ShapeService; -import org.eclipse.rdf4j.model.Model; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; -import java.util.List; -import java.util.Optional; - -import static java.lang.String.format; - -@Tag(name = "Metadata Model") -@RestController -@RequestMapping("/shapes") -public class ShapeController { - - @Autowired - private ShapeService shapeService; - - @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getShapes() { - List dto = shapeService.getShapes(); - return new ResponseEntity<>(dto, HttpStatus.OK); - } - - @GetMapping(path = "/public", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getPublishedShapes() { - List dto = shapeService.getPublishedShapes(); - return new ResponseEntity<>(dto, HttpStatus.OK); - } - - @PreAuthorize("hasRole('ADMIN')") - @GetMapping(path = "/import", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getImportableShapes(@RequestParam(name = "from") String fdpUrl) { - List dto = shapeService.getRemoteShapes(fdpUrl); - return new ResponseEntity<>(dto, HttpStatus.OK); - } - - @PreAuthorize("hasRole('ADMIN')") - @PostMapping(path = "/import", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> importShapes(@RequestBody @Valid List reqDtos) { - List dto = shapeService.importShapes(reqDtos); - return new ResponseEntity<>(dto, HttpStatus.OK); - } - - @PreAuthorize("hasRole('ADMIN')") - @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity createShape(@RequestBody @Valid ShapeChangeDTO reqDto) { - ShapeDTO dto = shapeService.createShape(reqDto); - return new ResponseEntity<>(dto, HttpStatus.OK); - } - - @GetMapping(path = "/{uuid}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getShape(@PathVariable final String uuid) - throws ResourceNotFoundException { - Optional oDto = shapeService.getShapeByUuid(uuid); - if (oDto.isPresent()) { - return new ResponseEntity<>(oDto.get(), HttpStatus.OK); - } else { - throw new ResourceNotFoundException(format("Shape '%s' doesn't exist", uuid)); - } - } - - @GetMapping(path = "/{uuid}", produces = { - "text/turtle", - "application/x-turtle", - "text/n3", - "text/rdf+n3", - "application/ld+json", - "application/rdf+xml", - "application/xml", - "text/xml", - }) - public ResponseEntity getShapeContent(@PathVariable final String uuid) - throws ResourceNotFoundException { - Optional oDto = shapeService.getShapeContentByUuid(uuid); - if (oDto.isPresent()) { - return new ResponseEntity<>(oDto.get(), HttpStatus.OK); - } else { - throw new ResourceNotFoundException(format("Shape '%s' doesn't exist", uuid)); - } - } - - @PreAuthorize("hasRole('ADMIN')") - @PutMapping(path = "/{uuid}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity putShape(@PathVariable final String uuid, - @RequestBody @Valid ShapeChangeDTO reqDto) throws ResourceNotFoundException { - Optional oDto = shapeService.updateShape(uuid, reqDto); - if (oDto.isPresent()) { - return new ResponseEntity<>(oDto.get(), HttpStatus.OK); - } else { - throw new ResourceNotFoundException(format("Shape '%s' doesn't exist", uuid)); - } - } - - @PreAuthorize("hasRole('ADMIN')") - @DeleteMapping("/{uuid}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public ResponseEntity deleteShape(@PathVariable final String uuid) - throws ResourceNotFoundException { - boolean result = shapeService.deleteShape(uuid); - if (result) { - return ResponseEntity.noContent().build(); - } else { - throw new ResourceNotFoundException(format("Shape '%s' doesn't exist", uuid)); - } - } - -} diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionChangeDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionChangeDTO.java index c957992ef..69e404270 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionChangeDTO.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionChangeDTO.java @@ -48,7 +48,7 @@ public class ResourceDefinitionChangeDTO { protected String urlPrefix; @NotNull - protected List shapeUuids; + protected List metadataSchemaUuids; @NotNull @Valid diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionDTO.java index fa2c800eb..15d47d716 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionDTO.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/resource/ResourceDefinitionDTO.java @@ -50,7 +50,7 @@ public class ResourceDefinitionDTO { protected String urlPrefix; @NotNull - protected List shapeUuids; + protected List metadataSchemaUuids; @NotNull protected List targetClassUris; diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/shape/Shape.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaChangeDTO.java similarity index 72% rename from src/main/java/nl/dtls/fairdatapoint/entity/shape/Shape.java rename to src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaChangeDTO.java index 7a6188757..47009302b 100644 --- a/src/main/java/nl/dtls/fairdatapoint/entity/shape/Shape.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaChangeDTO.java @@ -20,35 +20,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.entity.shape; +package nl.dtls.fairdatapoint.api.dto.schema; import lombok.*; -import org.bson.types.ObjectId; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; -import java.util.Set; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; -@Document -@Getter -@Setter @NoArgsConstructor @AllArgsConstructor -@Builder(toBuilder = true) -public class Shape { - - @Id - protected ObjectId id; - - private String uuid; +@Getter +@Setter +@Builder +public class MetadataSchemaChangeDTO { + @NotBlank + @NotNull private String name; - private boolean published; + @NotNull + private String description; - private ShapeType type; + private boolean abstractSchema; + @NotNull private String definition; - private Set targetClasses; + @NotNull + private List extendsSchemaUuids; + + private String suggestedResourceName; + + private String suggestedUrlPrefix; + } diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaDTO.java new file mode 100644 index 000000000..366a760a5 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaDTO.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.api.dto.schema; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaType; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class MetadataSchemaDTO { + + @NotNull + @NotBlank + private String uuid; + + @NotNull + @NotBlank + private String name; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private MetadataSchemaVersionDTO latest; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private MetadataSchemaDraftDTO draft; + + @NotNull + private List versions; + + @NotNull + private List extendSchemaUuids; + + @NotNull + private List childSchemaUuids; +} diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaDraftDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaDraftDTO.java new file mode 100644 index 000000000..112c14d4f --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaDraftDTO.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.api.dto.schema; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class MetadataSchemaDraftDTO { + + @NotNull + @NotBlank + private String uuid; + + @NotNull + @NotBlank + private String name; + + @NotNull + private String description; + + private boolean abstractSchema; + + @NotNull + private String definition; + + @NotNull + private List extendsSchemaUuids; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String suggestedResourceName; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String suggestedUrlPrefix; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String lastVersion; +} diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaReleaseDTO.java similarity index 81% rename from src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeDTO.java rename to src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaReleaseDTO.java index a9844dc51..1ddd38bdc 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeDTO.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaReleaseDTO.java @@ -20,31 +20,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.api.dto.shape; +package nl.dtls.fairdatapoint.api.dto.schema; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import nl.dtls.fairdatapoint.entity.shape.ShapeType; +import nl.dtls.fairdatapoint.api.validator.ValidSemVer; -import java.util.List; +import javax.validation.constraints.NotBlank; @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class ShapeDTO { +public class MetadataSchemaReleaseDTO { - private String uuid; + @NotBlank + @ValidSemVer + protected String version; - private String name; + @NotBlank + protected String description; private boolean published; - - private ShapeType type; - - private String definition; - - private List targetClasses; } + diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeRemoteDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaRemoteDTO.java similarity index 77% rename from src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeRemoteDTO.java rename to src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaRemoteDTO.java index 8dfde6c8c..b91e0cf3d 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeRemoteDTO.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaRemoteDTO.java @@ -20,23 +20,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.api.dto.shape; +package nl.dtls.fairdatapoint.api.dto.schema; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class ShapeRemoteDTO { - private String from; +@Builder +public class MetadataSchemaRemoteDTO { - private String uuid; + @NotNull + @NotBlank + private MetadataSchemaVersionDTO schema; - private String name; + private MetadataSchemaRemoteState status; - private String definition; + private boolean canImport; } diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaRemoteState.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaRemoteState.java new file mode 100644 index 000000000..b4aca75a4 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaRemoteState.java @@ -0,0 +1,27 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.api.dto.schema; + +public enum MetadataSchemaRemoteState { + DIRTY, CONFLICT, ALREADY_IMPORTED, NOT_IMPORTED +} diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeChangeDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaUpdateDTO.java similarity index 89% rename from src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeChangeDTO.java rename to src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaUpdateDTO.java index 625c7a805..9cc201c44 100644 --- a/src/main/java/nl/dtls/fairdatapoint/api/dto/shape/ShapeChangeDTO.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaUpdateDTO.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.api.dto.shape; +package nl.dtls.fairdatapoint.api.dto.schema; import lombok.AllArgsConstructor; import lombok.Getter; @@ -29,18 +29,21 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import java.util.List; @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class ShapeChangeDTO { +public class MetadataSchemaUpdateDTO { @NotBlank + @NotNull private String name; - private boolean published; + @NotNull + private String description; - private String definition; + private boolean published; } diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaVersionDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaVersionDTO.java new file mode 100644 index 000000000..9b3a9849b --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/schema/MetadataSchemaVersionDTO.java @@ -0,0 +1,90 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.api.dto.schema; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaType; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.Set; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class MetadataSchemaVersionDTO { + + @NotNull + @NotBlank + private String uuid; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String version; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String versionUuid; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String previousVersionUuid; + + @NotNull + @NotBlank + private String name; + + private boolean published; + + private boolean abstractSchema; + + private boolean latest; + + @NotNull + private MetadataSchemaType type; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String origin; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String importedFrom; + + @NotNull + private String definition; + + @NotNull + private String description; + + @NotNull + private Set targetClasses; + + @NotNull + private List extendsSchemaUuids; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String suggestedResourceName; + + @JsonInclude(JsonInclude.Include.ALWAYS) + private String suggestedUrlPrefix; +} diff --git a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeValidator.java b/src/main/java/nl/dtls/fairdatapoint/api/validator/SemVerValidator.java similarity index 66% rename from src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeValidator.java rename to src/main/java/nl/dtls/fairdatapoint/api/validator/SemVerValidator.java index c34bda8e4..f63f2c6f5 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeValidator.java +++ b/src/main/java/nl/dtls/fairdatapoint/api/validator/SemVerValidator.java @@ -20,23 +20,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.service.shape; +package nl.dtls.fairdatapoint.api.validator; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeChangeDTO; -import nl.dtls.fairdatapoint.entity.exception.ValidationException; -import nl.dtls.fairdatapoint.util.RdfIOUtil; -import org.springframework.stereotype.Service; +import nl.dtls.fairdatapoint.entity.schema.SemVer; -@Service -public class ShapeValidator { +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; - public void validate(ShapeChangeDTO reqDto) { - // Try to parse SHACL definition +public class SemVerValidator implements ConstraintValidator { + + @Override + public void initialize(ValidSemVer text) { + } + + @Override + public boolean isValid(String text, ConstraintValidatorContext cxt) { try { - RdfIOUtil.read(reqDto.getDefinition(), ""); - } catch (ValidationException e) { - throw new ValidationException("Unable to read SHACL definition"); + SemVer version = new SemVer(text); + return text.equals(version.toString()); + } catch (Exception e) { + return false; } } - } diff --git a/src/main/java/nl/dtls/fairdatapoint/api/validator/ValidSemVer.java b/src/main/java/nl/dtls/fairdatapoint/api/validator/ValidSemVer.java new file mode 100644 index 000000000..4864f7cd2 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/api/validator/ValidSemVer.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.api.validator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = SemVerValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE_USE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidSemVer { + + String message() default "Invalid version specification"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/nl/dtls/fairdatapoint/config/RepositoryConfig.java b/src/main/java/nl/dtls/fairdatapoint/config/RepositoryConfig.java index 322896148..4c5f46bd0 100644 --- a/src/main/java/nl/dtls/fairdatapoint/config/RepositoryConfig.java +++ b/src/main/java/nl/dtls/fairdatapoint/config/RepositoryConfig.java @@ -52,7 +52,7 @@ public class RepositoryConfig { @Autowired private RepositoryProperties repositoryProperties; - @Bean(initMethod = "initialize", destroyMethod = "shutDown") + @Bean(initMethod = "init", destroyMethod = "shutDown") public Repository repository(ApplicationContext context) throws RepositoryException { @@ -133,6 +133,7 @@ private Repository getBlazeGraphRepository() { private Repository getGraphDBRepository() { log.info("Setting up GraphDB Store"); try { + System.setProperty("org.eclipse.rdf4j.rio.binary.format_version", "1"); if (!repositoryProperties.getGraphDb().getUrl().isEmpty() && !repositoryProperties.getGraphDb().getRepository().isEmpty()) { final RepositoryManager repositoryManager; diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/MigrationRunner.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/MigrationRunner.java index b7366e762..cfa630768 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/MigrationRunner.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/MigrationRunner.java @@ -30,7 +30,7 @@ import nl.dtls.fairdatapoint.database.mongo.migration.development.membership.MembershipMigration; import nl.dtls.fairdatapoint.database.mongo.migration.development.metadata.MetadataMigration; import nl.dtls.fairdatapoint.database.mongo.migration.development.resource.ResourceDefinitionMigration; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.ShapeMigration; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.MetadataSchemaMigration; import nl.dtls.fairdatapoint.database.mongo.migration.development.user.UserMigration; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionCache; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionTargetClassesCache; @@ -57,7 +57,7 @@ public class MigrationRunner { private ResourceDefinitionMigration resourceDefinitionMigration; @Autowired - private ShapeMigration shapeMigration; + private MetadataSchemaMigration metadataSchemaMigration; @Autowired private ApiKeyMigration apiKeyMigration; @@ -83,7 +83,7 @@ public void run() { membershipMigration.runMigration(); aclMigration.runMigration(); resourceDefinitionMigration.runMigration(); - shapeMigration.runMigration(); + metadataSchemaMigration.runMigration(); apiKeyMigration.runMigration(); metadataMigration.runMigration(); indexEntryMigration.runMigration(); diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/resource/data/ResourceDefinitionFixtures.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/resource/data/ResourceDefinitionFixtures.java index 8a17f5839..6d0dc5f7f 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/resource/data/ResourceDefinitionFixtures.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/resource/data/ResourceDefinitionFixtures.java @@ -40,11 +40,7 @@ public ResourceDefinition fdpDefinition() { KnownUUIDs.RD_FDP_UUID, "FAIR Data Point", "", - List.of(KnownUUIDs.SHAPE_RESOURCE_UUID, - KnownUUIDs.SHAPE_DATASERVICE_UUID, - KnownUUIDs.SHAPE_METADATASERVICE_UUID, - KnownUUIDs.SHAPE_FDP_UUID - ), + List.of(KnownUUIDs.SCHEMA_FDP_UUID), List.of(new ResourceDefinitionChild( KnownUUIDs.RD_CATALOG_UUID, FDP.METADATACATALOG.stringValue(), @@ -63,7 +59,7 @@ public ResourceDefinition catalogDefinition() { KnownUUIDs.RD_CATALOG_UUID, "Catalog", "catalog", - List.of(KnownUUIDs.SHAPE_RESOURCE_UUID, KnownUUIDs.SHAPE_CATALOG_UUID), + List.of(KnownUUIDs.SCHEMA_CATALOG_UUID), List.of(new ResourceDefinitionChild( KnownUUIDs.RD_DATASET_UUID, DCAT.HAS_DATASET.stringValue(), @@ -82,7 +78,7 @@ public ResourceDefinition datasetDefinition() { KnownUUIDs.RD_DATASET_UUID, "Dataset", "dataset", - List.of(KnownUUIDs.SHAPE_RESOURCE_UUID, KnownUUIDs.SHAPE_DATASET_UUID), + List.of(KnownUUIDs.SCHEMA_DATASET_UUID), List.of(new ResourceDefinitionChild( KnownUUIDs.RD_DISTRIBUTION_UUID, DCAT.HAS_DISTRIBUTION.stringValue(), @@ -102,7 +98,7 @@ public ResourceDefinition distributionDefinition() { KnownUUIDs.RD_DISTRIBUTION_UUID, "Distribution", "distribution", - List.of(KnownUUIDs.SHAPE_RESOURCE_UUID, KnownUUIDs.SHAPE_DISTRIBUTION_UUID), + List.of(KnownUUIDs.SCHEMA_DISTRIBUTION_UUID), List.of(), List.of( new ResourceDefinitionLink("Access online", DCAT.ACCESS_URL.stringValue()), @@ -114,9 +110,9 @@ public ResourceDefinition distributionDefinition() { public ResourceDefinition ontologyDefinition() { return new ResourceDefinition( ONTOLOGY_DEFINITION_UUID, - "Ontology", - "ontology", - List.of(KnownUUIDs.SHAPE_RESOURCE_UUID), + "Data Service", + "data-service", + List.of(KnownUUIDs.SCHEMA_DATASERVICE_UUID), List.of(), List.of() ); diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/ShapeMigration.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/MetadataSchemaMigration.java similarity index 62% rename from src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/ShapeMigration.java rename to src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/MetadataSchemaMigration.java index 223034160..fae590f6f 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/ShapeMigration.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/MetadataSchemaMigration.java @@ -20,32 +20,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.database.mongo.migration.development.shape; +package nl.dtls.fairdatapoint.database.mongo.migration.development.schema; import nl.dtls.fairdatapoint.database.common.migration.Migration; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service -public class ShapeMigration implements Migration { +public class MetadataSchemaMigration implements Migration { @Autowired - private ShapeFixtures shapeFixtures; + private MetadataSchemaFixtures metadataSchemaFixtures; @Autowired - private ShapeRepository shapeRepository; + private MetadataSchemaRepository metadataSchemaRepository; public void runMigration() { - shapeRepository.deleteAll(); - shapeRepository.save(shapeFixtures.resourceShape()); - shapeRepository.save(shapeFixtures.fdpShape()); - shapeRepository.save(shapeFixtures.dataServiceShape()); - shapeRepository.save(shapeFixtures.metadataServiceShape()); - shapeRepository.save(shapeFixtures.catalogShape()); - shapeRepository.save(shapeFixtures.datasetShape()); - shapeRepository.save(shapeFixtures.distributionShape()); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.save(metadataSchemaFixtures.resourceSchema()); + metadataSchemaRepository.save(metadataSchemaFixtures.fdpSchema()); + metadataSchemaRepository.save(metadataSchemaFixtures.dataServiceSchema()); + metadataSchemaRepository.save(metadataSchemaFixtures.metadataServiceSchema()); + metadataSchemaRepository.save(metadataSchemaFixtures.catalogSchema()); + metadataSchemaRepository.save(metadataSchemaFixtures.datasetSchema()); + metadataSchemaRepository.save(metadataSchemaFixtures.distributionSchema()); } } \ No newline at end of file diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/MetadataSchemaFixtures.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/MetadataSchemaFixtures.java new file mode 100644 index 000000000..7fcf66367 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/MetadataSchemaFixtures.java @@ -0,0 +1,274 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data; + +import lombok.SneakyThrows; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaType; +import nl.dtls.fairdatapoint.entity.schema.SemVer; +import nl.dtls.fairdatapoint.util.KnownUUIDs; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static nl.dtls.fairdatapoint.util.ResourceReader.loadClassResource; + +@Service +public class MetadataSchemaFixtures { + + private static final SemVer VERSION = new SemVer("1.0.0"); + + private MetadataSchema createSchemaFixture( + String uuid, + String versionUuid, + String name, + String definition, + Set targetClasses, + List extendsSchemas, + MetadataSchemaType type + ) { + return MetadataSchema.builder() + .uuid(uuid) + .versionUuid(versionUuid) + .version(VERSION) + .versionString(VERSION.toString()) + .name(name) + .definition(definition) + .targetClasses(targetClasses) + .extendSchemas(extendsSchemas) + .type(type) + .origin(null) + .latest(true) + .published(true) + .abstractSchema(false) + .suggestedResourceName(null) + .suggestedUrlPrefix(null) + .previousVersionUuid(null) + .createdAt(Instant.now()) + .build(); + } + + private MetadataSchemaDraft createSchemaDraftFixture( + String uuid, + String name, + String definition, + Set targetClasses + ) { + return MetadataSchemaDraft.builder() + .uuid(uuid) + .name(name) + .description("") + .abstractSchema(false) + .definition(definition) + .targetClasses(targetClasses) + .extendSchemas(Collections.emptyList()) + .createdAt(Instant.now()) + .updatedAt(Instant.now()) + .build(); + } + + @SneakyThrows + public MetadataSchema resourceSchema() { + String definition = loadClassResource("shape-resource.ttl", getClass()); + MetadataSchema schema = createSchemaFixture( + KnownUUIDs.SCHEMA_RESOURCE_UUID, + KnownUUIDs.SCHEMA_V1_RESOURCE_UUID, + "Resource", + definition, + Set.of("http://www.w3.org/ns/dcat#Resource"), + Collections.emptyList(), + MetadataSchemaType.INTERNAL + ); + schema.setAbstractSchema(true); + return schema; + } + + @SneakyThrows + public MetadataSchema fdpSchema() { + String definition = loadClassResource("shape-fdp.ttl", getClass()); + return createSchemaFixture( + KnownUUIDs.SCHEMA_FDP_UUID, + KnownUUIDs.SCHEMA_V1_FDP_UUID, + "FAIR Data Point", + definition, + Set.of("https://w3id.org/fdp/fdp-o#FAIRDataPoint"), + Collections.singletonList(KnownUUIDs.SCHEMA_METADATASERVICE_UUID), + MetadataSchemaType.INTERNAL + ); + } + + @SneakyThrows + public MetadataSchema dataServiceSchema() { + String definition = loadClassResource("shape-data-service.ttl", getClass()); + return createSchemaFixture( + KnownUUIDs.SCHEMA_DATASERVICE_UUID, + KnownUUIDs.SCHEMA_V1_DATASERVICE_UUID, + "Data Service", + definition, + Set.of("http://www.w3.org/ns/dcat#DataService"), + Collections.singletonList(KnownUUIDs.SCHEMA_RESOURCE_UUID), + MetadataSchemaType.INTERNAL + ); + } + + @SneakyThrows + public MetadataSchema metadataServiceSchema() { + String definition = loadClassResource("shape-metadata-service.ttl", getClass()); + return createSchemaFixture( + KnownUUIDs.SCHEMA_METADATASERVICE_UUID, + KnownUUIDs.SCHEMA_V1_METADATASERVICE_UUID, + "Metadata Service", + definition, + Set.of("https://w3id.org/fdp/fdp-o#MetadataService"), + Collections.singletonList(KnownUUIDs.SCHEMA_DATASERVICE_UUID), + MetadataSchemaType.INTERNAL + ); + } + + @SneakyThrows + public MetadataSchema catalogSchema() { + String definition = loadClassResource("shape-catalog.ttl", getClass()); + return createSchemaFixture( + KnownUUIDs.SCHEMA_CATALOG_UUID, + KnownUUIDs.SCHEMA_V1_CATALOG_UUID, + "Catalog", + definition, + Set.of("http://www.w3.org/ns/dcat#Catalog"), + Collections.singletonList(KnownUUIDs.SCHEMA_RESOURCE_UUID), + MetadataSchemaType.INTERNAL + ); + } + + @SneakyThrows + public MetadataSchema datasetSchema() { + String definition = loadClassResource("shape-dataset.ttl", getClass()); + return createSchemaFixture( + KnownUUIDs.SCHEMA_DATASET_UUID, + KnownUUIDs.SCHEMA_V1_DATASET_UUID, + "Dataset", + definition, + Set.of("http://www.w3.org/ns/dcat#Dataset"), + Collections.singletonList(KnownUUIDs.SCHEMA_RESOURCE_UUID), + MetadataSchemaType.CUSTOM + ); + } + + @SneakyThrows + public MetadataSchema distributionSchema() { + String definition = loadClassResource("shape-distribution.ttl", getClass()); + return createSchemaFixture( + KnownUUIDs.SCHEMA_DISTRIBUTION_UUID, + KnownUUIDs.SCHEMA_V1_DISTRIBUTION_UUID, + "Distribution", + definition, + Set.of("http://www.w3.org/ns/dcat#Distribution"), + Collections.singletonList(KnownUUIDs.SCHEMA_RESOURCE_UUID), + MetadataSchemaType.CUSTOM + ); + } + + @SneakyThrows + public MetadataSchema customSchema() { + String definition = loadClassResource("shape-custom.ttl", getClass()); + return createSchemaFixture( + "ceba9984-9838-4be2-a2a7-12213016fd96", + "ceba9984-9838-4be2-a2a7-12213016fd97", + "Custom Shape", + definition, + Set.of("http://example.org/Dog"), + Collections.singletonList(KnownUUIDs.SCHEMA_RESOURCE_UUID), + MetadataSchemaType.CUSTOM + ); + } + + @SneakyThrows + public MetadataSchema customSchemaEdited(){ + String definition = loadClassResource("shape-custom-edited.ttl", getClass()); + return createSchemaFixture( + customSchema().getUuid(), + "ceba9984-9838-4be2-a2a7-12213016fd98", + customSchema().getName(), + definition, + Set.of("http://example.org/Dog"), + Collections.singletonList(KnownUUIDs.SCHEMA_RESOURCE_UUID), + customSchema().getType() + ); + } + + @SneakyThrows + public MetadataSchemaDraft customSchemaDraft1() { + String definition = loadClassResource("shape-custom.ttl", getClass()); + return createSchemaDraftFixture( + customSchema().getUuid(), + customSchema().getName(), + definition, + Set.of("http://example.org/Dog") + ); + } + + @SneakyThrows + public MetadataSchemaDraft customSchemaDraft2() { + String definition = loadClassResource("shape-custom.ttl", getClass()); + return createSchemaDraftFixture( + "ceba9984-9838-4be2-a2a7-12213016fd97", + "Custom Shape 2", + definition, + Set.of("http://example.org/Dog") + ); + } + + @SneakyThrows + public MetadataSchema customSchema_v1(boolean latest) { + MetadataSchema schema = customSchema(); + schema.setName("Schema v1.0.0"); + schema.setVersionString("1.0.0"); + schema.setLatest(latest); + return schema; + } + + @SneakyThrows + public MetadataSchema customSchema_v2(MetadataSchema previousVersion, boolean latest) { + MetadataSchema schema = customSchema(); + schema.setName("Schema v2.0.0"); + schema.setVersionString("2.0.0"); + schema.setLatest(latest); + schema.setVersionUuid("ceba9984-9838-4be2-a2a7-12213016fd99"); + schema.setPreviousVersionUuid(previousVersion.getVersionUuid()); + return schema; + } + + @SneakyThrows + public MetadataSchema customSchema_v3(MetadataSchema previousVersion, boolean latest) { + MetadataSchema schema = customSchema(); + schema.setName("Schema v2.1.0"); + schema.setVersionString("2.1.0"); + schema.setLatest(latest); + schema.setVersionUuid("ceba9984-9838-4be2-a2a7-12213016fd00"); + schema.setPreviousVersionUuid(previousVersion.getVersionUuid()); + return schema; + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/ShapeFixtures.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/ShapeFixtures.java deleted file mode 100644 index b9a784793..000000000 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/ShapeFixtures.java +++ /dev/null @@ -1,164 +0,0 @@ -/** - * The MIT License - * Copyright © 2017 DTL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data; - -import lombok.SneakyThrows; -import nl.dtls.fairdatapoint.entity.shape.Shape; -import nl.dtls.fairdatapoint.entity.shape.ShapeType; -import nl.dtls.fairdatapoint.util.KnownUUIDs; -import org.springframework.stereotype.Service; - -import java.util.Set; - -import static nl.dtls.fairdatapoint.util.ResourceReader.loadClassResource; - -@Service -public class ShapeFixtures { - - @SneakyThrows - public Shape resourceShape() { - String definition = loadClassResource("shape-resource.ttl", getClass()); - return new Shape( - null, - KnownUUIDs.SHAPE_RESOURCE_UUID, - "Resource", - false, - ShapeType.INTERNAL, - definition, - Set.of("http://www.w3.org/ns/dcat#Resource") - ); - } - - @SneakyThrows - public Shape fdpShape() { - String definition = loadClassResource("shape-fdp.ttl", getClass()); - return new Shape( - null, - KnownUUIDs.SHAPE_FDP_UUID, - "FAIR Data Point", - false, - ShapeType.INTERNAL, - definition, - Set.of("https://w3id.org/fdp/fdp-o#FAIRDataPoint") - ); - } - - @SneakyThrows - public Shape dataServiceShape() { - String definition = loadClassResource("shape-data-service.ttl", getClass()); - return new Shape( - null, - KnownUUIDs.SHAPE_DATASERVICE_UUID, - "Data Service", - false, - ShapeType.INTERNAL, - definition, - Set.of("http://www.w3.org/ns/dcat#DataService") - ); - } - - @SneakyThrows - public Shape metadataServiceShape() { - String definition = loadClassResource("shape-metadata-service.ttl", getClass()); - return new Shape( - null, - KnownUUIDs.SHAPE_METADATASERVICE_UUID, - "Metadata Service", - false, - ShapeType.INTERNAL, - definition, - Set.of("https://w3id.org/fdp/fdp-o#MetadataService") - ); - } - - @SneakyThrows - public Shape catalogShape() { - String definition = loadClassResource("shape-catalog.ttl", getClass()); - return new Shape( - null, - KnownUUIDs.SHAPE_CATALOG_UUID, - "Catalog", - false, - ShapeType.INTERNAL, - definition, - Set.of("http://www.w3.org/ns/dcat#Catalog") - ); - } - - @SneakyThrows - public Shape datasetShape() { - String definition = loadClassResource("shape-dataset.ttl", getClass()); - return new Shape( - null, - KnownUUIDs.SHAPE_DATASET_UUID, - "Dataset", - false, - ShapeType.CUSTOM, - definition, - Set.of("http://www.w3.org/ns/dcat#Dataset") - ); - } - - @SneakyThrows - public Shape distributionShape() { - String definition = loadClassResource("shape-distribution.ttl", getClass()); - return new Shape( - null, - KnownUUIDs.SHAPE_DISTRIBUTION_UUID, - "Distribution", - false, - ShapeType.CUSTOM, - definition, - Set.of("http://www.w3.org/ns/dcat#Distribution") - ); - } - - @SneakyThrows - public Shape customShape() { - String definition = loadClassResource("shape-custom.ttl", getClass()); - return new Shape( - null, - "ceba9984-9838-4be2-a2a7-12213016fd96", - "Custom Shape", - false, - ShapeType.CUSTOM, - definition, - Set.of("http://example.org/Dog") - ); - } - - @SneakyThrows - public Shape customShapeEdited(){ - String definition = loadClassResource("shape-custom-edited.ttl", getClass()); - return new Shape( - null, - customShape().getUuid(), - customShape().getName(), - false, - customShape().getType(), - definition, - Set.of("http://example.org/Dog") - ); - } - -} diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0003_ShapeDefinition.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0003_ShapeDefinition.java index 438c034f7..a13d9efda 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0003_ShapeDefinition.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0003_ShapeDefinition.java @@ -54,7 +54,7 @@ private void addShapeDefinitions(MongoDatabase db) throws Exception { private Document resourceDefinition() throws Exception { String shaclDefinition = loadClassResource("0003_shape-resource.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_RESOURCE_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_RESOURCE_UUID); definition.append("name", "Resource"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -66,7 +66,7 @@ private Document resourceDefinition() throws Exception { private Document repositoryDefinition() throws Exception { String shaclDefinition = loadClassResource("0003_shape-repository.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_REPOSITORY_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_REPOSITORY_UUID); definition.append("name", "Repository"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -77,7 +77,7 @@ private Document repositoryDefinition() throws Exception { private Document catalogDefinition() throws Exception { String shaclDefinition = loadClassResource("0003_shape-catalog.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_CATALOG_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_CATALOG_UUID); definition.append("name", "Catalog"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -88,7 +88,7 @@ private Document catalogDefinition() throws Exception { private Document datasetDefinition() throws Exception { String shaclDefinition = loadClassResource("0003_shape-dataset.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_DATASET_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_DATASET_UUID); definition.append("name", "Dataset"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -99,7 +99,7 @@ private Document datasetDefinition() throws Exception { private Document distributionDefinition() throws Exception { String shaclDefinition = loadClassResource("0003_shape-distribution.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_DISTRIBUTION_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_DISTRIBUTION_UUID); definition.append("name", "Distribution"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0005_UpdateShapeDefinition.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0005_UpdateShapeDefinition.java index 9fd946ceb..4271dec46 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0005_UpdateShapeDefinition.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0005_UpdateShapeDefinition.java @@ -45,11 +45,11 @@ public void run(MongoDatabase db) throws Exception { private void addShapeDefinitions(MongoDatabase db) throws Exception { MongoCollection shapeCol = db.getCollection("shape"); - shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SHAPE_RESOURCE_UUID)); - shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SHAPE_REPOSITORY_UUID)); - shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SHAPE_CATALOG_UUID)); - shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SHAPE_DATASET_UUID)); - shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SHAPE_DISTRIBUTION_UUID)); + shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SCHEMA_RESOURCE_UUID)); + shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SCHEMA_REPOSITORY_UUID)); + shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SCHEMA_CATALOG_UUID)); + shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SCHEMA_DATASET_UUID)); + shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SCHEMA_DISTRIBUTION_UUID)); shapeCol.insertOne(resourceDefinition()); shapeCol.insertOne(repositoryDefinition()); @@ -61,7 +61,7 @@ private void addShapeDefinitions(MongoDatabase db) throws Exception { private Document resourceDefinition() throws Exception { String shaclDefinition = loadClassResource("0005_shape-resource.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_RESOURCE_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_RESOURCE_UUID); definition.append("name", "Resource"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -73,7 +73,7 @@ private Document resourceDefinition() throws Exception { private Document repositoryDefinition() throws Exception { String shaclDefinition = loadClassResource("0005_shape-repository.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_REPOSITORY_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_REPOSITORY_UUID); definition.append("name", "Repository"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -84,7 +84,7 @@ private Document repositoryDefinition() throws Exception { private Document catalogDefinition() throws Exception { String shaclDefinition = loadClassResource("0005_shape-catalog.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_CATALOG_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_CATALOG_UUID); definition.append("name", "Catalog"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -95,7 +95,7 @@ private Document catalogDefinition() throws Exception { private Document datasetDefinition() throws Exception { String shaclDefinition = loadClassResource("0005_shape-dataset.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_DATASET_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_DATASET_UUID); definition.append("name", "Dataset"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); @@ -106,7 +106,7 @@ private Document datasetDefinition() throws Exception { private Document distributionDefinition() throws Exception { String shaclDefinition = loadClassResource("0005_shape-distribution.ttl", getClass()); Document definition = new Document(); - definition.append("uuid", KnownUUIDs.SHAPE_DISTRIBUTION_UUID); + definition.append("uuid", KnownUUIDs.SCHEMA_DISTRIBUTION_UUID); definition.append("name", "Distribution"); definition.append("type", "INTERNAL"); definition.append("definition", shaclDefinition); diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0008_ShapesInternalChange.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0008_ShapesInternalChange.java index 669732aac..66f0335b5 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0008_ShapesInternalChange.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0008_ShapesInternalChange.java @@ -46,12 +46,12 @@ private void updateInternalShapesType(MongoDatabase db) { MongoCollection shapeCol = db.getCollection("shape"); // DATASET shapeCol.updateOne( - Filters.eq("uuid", KnownUUIDs.SHAPE_DATASET_UUID), + Filters.eq("uuid", KnownUUIDs.SCHEMA_DATASET_UUID), Updates.set("type", "CUSTOM") ); // DISTRIBUTION shapeCol.updateOne( - Filters.eq("uuid", KnownUUIDs.SHAPE_DISTRIBUTION_UUID), + Filters.eq("uuid", KnownUUIDs.SCHEMA_DISTRIBUTION_UUID), Updates.set("type", "CUSTOM") ); } diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0009_ShapeTargetClasses.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0009_ShapeTargetClasses.java index 847ee523b..f21d30dc6 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0009_ShapeTargetClasses.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0009_ShapeTargetClasses.java @@ -31,7 +31,7 @@ import nl.dtls.fairdatapoint.Profiles; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionCache; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionTargetClassesCache; -import nl.dtls.fairdatapoint.service.shape.ShapeShaclUtils; +import nl.dtls.fairdatapoint.service.schema.MetadataSchemaShaclUtils; import org.bson.Document; import org.springframework.context.annotation.Profile; @@ -55,7 +55,7 @@ private void updateShapesAndResources(MongoDatabase db) { for (Document document : shapeCol.find()) { String definition = (String) document.get("definition"); String uuid = (String) document.get("uuid"); - Set targetClasses = ShapeShaclUtils.extractTargetClasses(definition); + Set targetClasses = MetadataSchemaShaclUtils.extractTargetClasses(definition); targetClassesMap.put(uuid, targetClasses); shapeCol.updateOne( Filters.eq("uuid", uuid), diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0011_ComplyFDPO.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0011_ComplyFDPO.java index 5beb1c533..c7d7df82c 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0011_ComplyFDPO.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0011_ComplyFDPO.java @@ -55,7 +55,7 @@ private void updateShapes(MongoDatabase db) throws Exception { MongoCollection shapeCol = db.getCollection("shape"); // Delete Repository Shape - shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SHAPE_REPOSITORY_UUID)); + shapeCol.deleteOne(new Document("uuid", KnownUUIDs.SCHEMA_REPOSITORY_UUID)); // Insert New Shapes shapeCol.insertOne(dataServiceShape()); @@ -66,7 +66,7 @@ private void updateShapes(MongoDatabase db) throws Exception { private Document fdpShape() throws Exception { String shaclDefinition = loadClassResource("0011_shape-fdp.ttl", getClass()); Document shape = new Document(); - shape.append("uuid", KnownUUIDs.SHAPE_FDP_UUID); + shape.append("uuid", KnownUUIDs.SCHEMA_FDP_UUID); shape.append("name", "FAIR Data Point"); shape.append("type", "INTERNAL"); shape.append("definition", shaclDefinition); @@ -78,7 +78,7 @@ private Document fdpShape() throws Exception { private Document dataServiceShape() throws Exception { String shaclDefinition = loadClassResource("0011_shape-data-service.ttl", getClass()); Document shape = new Document(); - shape.append("uuid", KnownUUIDs.SHAPE_DATASERVICE_UUID); + shape.append("uuid", KnownUUIDs.SCHEMA_DATASERVICE_UUID); shape.append("name", "Data Service"); shape.append("type", "INTERNAL"); shape.append("definition", shaclDefinition); @@ -90,7 +90,7 @@ private Document dataServiceShape() throws Exception { private Document metadataServiceShape() throws Exception { String shaclDefinition = loadClassResource("0011_shape-metadata-service.ttl", getClass()); Document shape = new Document(); - shape.append("uuid", KnownUUIDs.SHAPE_METADATASERVICE_UUID); + shape.append("uuid", KnownUUIDs.SCHEMA_METADATASERVICE_UUID); shape.append("name", "Metadata Service"); shape.append("type", "INTERNAL"); shape.append("definition", shaclDefinition); @@ -115,10 +115,10 @@ private Document fdpResourceDefinition() { definition.append("name", "FAIR Data Point"); definition.append("urlPrefix", ""); definition.append("shapeUuids", List.of( - KnownUUIDs.SHAPE_RESOURCE_UUID, - KnownUUIDs.SHAPE_DATASERVICE_UUID, - KnownUUIDs.SHAPE_METADATASERVICE_UUID, - KnownUUIDs.SHAPE_FDP_UUID + KnownUUIDs.SCHEMA_RESOURCE_UUID, + KnownUUIDs.SCHEMA_DATASERVICE_UUID, + KnownUUIDs.SCHEMA_METADATASERVICE_UUID, + KnownUUIDs.SCHEMA_FDP_UUID )); // Child diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0012_MetadataSchemas.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0012_MetadataSchemas.java new file mode 100644 index 000000000..3c170a1de --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/migration/production/Migration_0012_MetadataSchemas.java @@ -0,0 +1,217 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.database.mongo.migration.production; + +import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import nl.dtls.fairdatapoint.Profiles; +import nl.dtls.fairdatapoint.entity.schema.SemVer; +import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionCache; +import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionTargetClassesCache; +import nl.dtls.fairdatapoint.service.schema.MetadataSchemaShaclUtils; +import nl.dtls.fairdatapoint.util.KnownUUIDs; +import org.bson.Document; +import org.springframework.context.annotation.Profile; +import org.springframework.data.mongodb.core.MongoTemplate; + +import java.time.Instant; +import java.util.*; + +@ChangeUnit(id="Migration_0012_MetadataSchemas", order = "0012", author = "migrationBot") +@Profile(Profiles.PRODUCTION) +public class Migration_0012_MetadataSchemas { + + private final MongoTemplate db; + + private static final String FDP_APP_URL = "https://purl.org/fairdatapoint/app"; + + public Migration_0012_MetadataSchemas(MongoTemplate template) { + this.db = template; + } + + @Execution + public void run(ResourceDefinitionCache resourceDefinitionCache, ResourceDefinitionTargetClassesCache targetClassesCache) { + updateInternalShapesType(); + updateResourceDefinitionLinks(); + + resourceDefinitionCache.computeCache(); + targetClassesCache.computeCache(); + } + + private void updateInternalShapesType() { + MongoCollection shapeCol = db.getCollection("shape"); + MongoCollection schemaCol = db.createCollection("metadataSchema"); + db.createCollection("metadataSchemaDraft"); + SemVer version = new SemVer("1.0.0"); + Instant now = Instant.now(); + + // Check default schemas presence + boolean resourceExists = docWithUuidExists(shapeCol, KnownUUIDs.SCHEMA_RESOURCE_UUID); + boolean dataServiceExists = docWithUuidExists(shapeCol, KnownUUIDs.SCHEMA_DATASERVICE_UUID); + boolean metadataServiceExists = docWithUuidExists(shapeCol, KnownUUIDs.SCHEMA_METADATASERVICE_UUID); + + // Migrate shapes to schemas + shapeCol.find().forEach(shapeDoc -> { + String schemaUuid = shapeDoc.getString("uuid"); + // Internal shapes + String origin = null; + String versionUuid = UUID.randomUUID().toString(); + String suggestedResourceName = null; + String suggestedUrlPrefix = null; + boolean isAbstract = false; + if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_RESOURCE_UUID)) { + isAbstract = true; + versionUuid = KnownUUIDs.SCHEMA_V1_RESOURCE_UUID; + origin = FDP_APP_URL; + } else if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_FDP_UUID)) { + versionUuid = KnownUUIDs.SCHEMA_V1_FDP_UUID; + origin = FDP_APP_URL; + suggestedResourceName = "FAIR Data Point"; + suggestedUrlPrefix = ""; + } else if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_DATASERVICE_UUID)) { + versionUuid = KnownUUIDs.SCHEMA_V1_DATASERVICE_UUID; + origin = FDP_APP_URL; + suggestedResourceName = "Data Service"; + suggestedUrlPrefix = "data-service"; + } else if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_METADATASERVICE_UUID)) { + versionUuid = KnownUUIDs.SCHEMA_V1_METADATASERVICE_UUID; + origin = FDP_APP_URL; + suggestedResourceName = "Metadata Service"; + suggestedUrlPrefix = "metadata-service"; + } else if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_CATALOG_UUID)) { + versionUuid = KnownUUIDs.SCHEMA_V1_CATALOG_UUID; + origin = FDP_APP_URL; + suggestedResourceName = "Catalog"; + suggestedUrlPrefix = "catalog"; + } else if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_DATASET_UUID)) { + versionUuid = KnownUUIDs.SCHEMA_V1_DATASET_UUID; + origin = FDP_APP_URL; + suggestedResourceName = "Dataset"; + suggestedUrlPrefix = "dataset"; + } else if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_DISTRIBUTION_UUID)) { + versionUuid = KnownUUIDs.SCHEMA_V1_DISTRIBUTION_UUID; + origin = FDP_APP_URL; + suggestedResourceName = "Distribution"; + suggestedUrlPrefix = "distribution"; + } + // Extends + List extendSchemas = new ArrayList<>(); + if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_FDP_UUID) && metadataServiceExists) { + // FAIRDataPoint extends MetadataService + extendSchemas.add(KnownUUIDs.SCHEMA_METADATASERVICE_UUID); + } else if (Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_METADATASERVICE_UUID) && dataServiceExists) { + // MetadataService extends DataService + extendSchemas.add(KnownUUIDs.SCHEMA_DATASERVICE_UUID); + } else if (!Objects.equals(schemaUuid, KnownUUIDs.SCHEMA_RESOURCE_UUID) && resourceExists) { + // Everything else (except Resource) extends Resource + extendSchemas.add(KnownUUIDs.SCHEMA_RESOURCE_UUID); + } + // Prepare + Document schemaDoc = new Document(); + schemaDoc.append("uuid", schemaUuid); + schemaDoc.append("versionUuid", versionUuid); + schemaDoc.append("versionString", version.toString()); + schemaDoc.append("name", shapeDoc.getString("name")); + schemaDoc.append("description", ""); + schemaDoc.append("definition", shapeDoc.getString("definition")); + schemaDoc.append("targetClasses", MetadataSchemaShaclUtils.extractTargetClasses(shapeDoc.getString("definition"))); + schemaDoc.append("extendSchemas", extendSchemas); + schemaDoc.append("type", shapeDoc.get("type")); + schemaDoc.append("origin", origin); + schemaDoc.append("importedFrom", origin); + schemaDoc.append("latest", true); + schemaDoc.append("previousVersionUuid", null); + schemaDoc.append("published", shapeDoc.getBoolean("published", false)); + schemaDoc.append("abstractSchema", isAbstract); + schemaDoc.append("suggestedResourceName", suggestedResourceName); + schemaDoc.append("suggestedUrlPrefix", suggestedUrlPrefix); + schemaDoc.append("createdAt", now); + // Insert + schemaCol.insertOne(schemaDoc); + }); + db.dropCollection("shape"); + } + private void updateResourceDefinitionLinks() { + MongoCollection rdCol = db.getCollection("resourceDefinition"); + // Rename shape to metadata schema + rdCol.updateMany( + Filters.exists("shapeUuids"), + Updates.rename("shapeUuids", "metadataSchemaUuids") + ); + // Remove Resource link (it is abstract) + rdCol.find().forEach(rdDoc -> { + ArrayList metadataSchemaUuids = (ArrayList)rdDoc.get("metadataSchemaUuids"); + if (metadataSchemaUuids.contains(KnownUUIDs.SCHEMA_RESOURCE_UUID)) { + rdCol.updateOne( + Filters.eq("uuid", rdDoc.get("uuid")), + Updates.set("metadataSchemaUuids", metadataSchemaUuids.stream().filter(x -> !Objects.equals(x, KnownUUIDs.SCHEMA_RESOURCE_UUID)).toList()) + ); + } + }); + } + + private boolean docWithUuidExists(MongoCollection collection, String uuid) { + return collection.find(Filters.eq("uuid", uuid)).first() != null; + } + + @RollbackExecution + public void rollback() { + // updateInternalShapesType + MongoCollection shapeCol = db.getCollection("shape"); + MongoCollection schemaCol = db.getCollection("metadataSchema"); + schemaCol.find(Filters.eq("latest", true)).forEach(schemaDoc -> { + Document shapeDoc = new Document(); + shapeDoc.append("uuid", schemaDoc.getString("uuid")); + shapeDoc.append("name", schemaDoc.getString("name")); + shapeDoc.append("definition", schemaDoc.getString("definition")); + shapeDoc.append("targetClasses", schemaDoc.get("targetClasses")); + shapeDoc.append("type", schemaDoc.get("type") == "INTERNAL" ? "INTERNAL" : "CUSTOM"); + shapeDoc.append("published", schemaDoc.getBoolean("published", false)); + shapeCol.insertOne(shapeDoc); + }); + db.dropCollection("metadataSchema"); + db.dropCollection("metadataSchemaDraft"); + // updateResourceDefinitionLinks + MongoCollection rdCol = db.getCollection("resourceDefinition"); + // Add Resource link (it is abstract) + rdCol.find().forEach(rdDoc -> { + ArrayList metadataSchemaUuids = (ArrayList)rdDoc.get("metadataSchemaUuids"); + if (!metadataSchemaUuids.contains(KnownUUIDs.SCHEMA_RESOURCE_UUID)) { + metadataSchemaUuids.add(KnownUUIDs.SCHEMA_RESOURCE_UUID); + rdCol.updateOne( + Filters.eq("uuid", rdDoc.get("uuid")), + Updates.set("metadataSchemaUuids", metadataSchemaUuids) + ); + } + }); + // Rename metadata schema to shape + rdCol.updateMany( + Filters.exists("metadataSchemaUuids"), + Updates.rename("metadataSchemaUuids", "shapeUuids") + ); + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/ShapeRepository.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/MetadataSchemaDraftRepository.java similarity index 84% rename from src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/ShapeRepository.java rename to src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/MetadataSchemaDraftRepository.java index 3bf428f92..ad0fdf2ec 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/ShapeRepository.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/MetadataSchemaDraftRepository.java @@ -22,16 +22,13 @@ */ package nl.dtls.fairdatapoint.database.mongo.repository; -import nl.dtls.fairdatapoint.entity.shape.Shape; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; import org.springframework.data.mongodb.repository.MongoRepository; -import java.util.List; import java.util.Optional; -public interface ShapeRepository extends MongoRepository { +public interface MetadataSchemaDraftRepository extends MongoRepository { - Optional findByUuid(String uuid); - - List findAllByPublishedIsTrue(); + Optional findByUuid(String uuid); } diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/MetadataSchemaRepository.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/MetadataSchemaRepository.java new file mode 100644 index 000000000..4dcb5f646 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/MetadataSchemaRepository.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.database.mongo.repository; + +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; +import java.util.Optional; + +public interface MetadataSchemaRepository extends MongoRepository { + + List findByUuid(String uuid); + + Optional findByVersionUuid(String uuid); + + Optional findByUuidAndVersionString(String uuid, String versionString); + + Optional findByUuidAndLatestIsTrue(String uuid); + + List findAllByPublishedIsTrue(); + + List findAllByLatestIsTrue(); + + List findAllByExtendSchemasContains(String uuid); + + Optional findByPreviousVersionUuid(String uuid); + + List findAllByImportedFromIsNotNull(); +} diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/ResourceDefinitionRepository.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/ResourceDefinitionRepository.java index 3336cf1a3..6655a9409 100644 --- a/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/ResourceDefinitionRepository.java +++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/ResourceDefinitionRepository.java @@ -36,6 +36,6 @@ public interface ResourceDefinitionRepository extends MongoRepository findByUrlPrefix(String urlPrefix); - List findByShapeUuidsIsContaining(String shapeUuid); + List findByMetadataSchemaUuidsIsContaining(String metadataSchemaUuid); } diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/exception/ShapeImportException.java b/src/main/java/nl/dtls/fairdatapoint/entity/exception/MetadataSchemaImportException.java similarity index 95% rename from src/main/java/nl/dtls/fairdatapoint/entity/exception/ShapeImportException.java rename to src/main/java/nl/dtls/fairdatapoint/entity/exception/MetadataSchemaImportException.java index 2f70b560e..cccf7a8a2 100644 --- a/src/main/java/nl/dtls/fairdatapoint/entity/exception/ShapeImportException.java +++ b/src/main/java/nl/dtls/fairdatapoint/entity/exception/MetadataSchemaImportException.java @@ -30,7 +30,7 @@ @ResponseStatus(value = HttpStatus.BAD_REQUEST) @Getter @AllArgsConstructor -public class ShapeImportException extends RuntimeException { +public class MetadataSchemaImportException extends RuntimeException { private final String from; diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/resource/ResourceDefinition.java b/src/main/java/nl/dtls/fairdatapoint/entity/resource/ResourceDefinition.java index 6ed6946a3..a8c17ef9c 100644 --- a/src/main/java/nl/dtls/fairdatapoint/entity/resource/ResourceDefinition.java +++ b/src/main/java/nl/dtls/fairdatapoint/entity/resource/ResourceDefinition.java @@ -52,18 +52,18 @@ public class ResourceDefinition { protected String urlPrefix; - protected List shapeUuids = new ArrayList<>(); + protected List metadataSchemaUuids = new ArrayList<>(); protected List children = new ArrayList<>(); protected List externalLinks = new ArrayList<>(); - public ResourceDefinition(String uuid, String name, String urlPrefix, List shapeUuids, + public ResourceDefinition(String uuid, String name, String urlPrefix, List metadataSchemaUuids, List children, List externalLinks) { this.uuid = uuid; this.name = name; this.urlPrefix = urlPrefix; - this.shapeUuids = shapeUuids; + this.metadataSchemaUuids = metadataSchemaUuids; this.children = children; this.externalLinks = externalLinks; } @@ -73,6 +73,6 @@ public boolean isRoot() { } public boolean isCatalog() { - return name.equals(CATALOG_PREFIX); + return urlPrefix.equals(CATALOG_PREFIX); } } \ No newline at end of file diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchema.java b/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchema.java new file mode 100644 index 000000000..0fd5db2a6 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchema.java @@ -0,0 +1,140 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.entity.schema; + +import lombok.*; +import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.Transient; +import org.springframework.data.mongodb.core.index.Indexed; +import org.springframework.data.mongodb.core.mapping.Document; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Document +@Getter +@Setter +@AllArgsConstructor +@Builder(toBuilder = true) +public class MetadataSchema { + + @Id + protected ObjectId id; + + @Indexed + private String uuid; + + @Indexed + protected String versionUuid; + + @Indexed + protected String versionString; + + @NotNull + @Transient + protected SemVer version; + + @NotNull + @NotBlank + private String name; + + @NotNull + private String description; + + @NotNull + private String definition; + + @NotNull + private Set targetClasses = new HashSet<>(); + + @NotNull + private List extendSchemas = new ArrayList<>(); + + @NotNull + private MetadataSchemaType type; + + private String origin = null; + + private String importedFrom = null; + + private boolean latest; + + private boolean published; + + private boolean abstractSchema; + + private String suggestedResourceName; + + private String suggestedUrlPrefix; + + private Instant createdAt; + + private String previousVersionUuid = null; + + @PersistenceConstructor + public MetadataSchema(ObjectId id, String uuid, String versionUuid, String versionString, String name, String description, String definition, Set targetClasses, List extendSchemas, MetadataSchemaType type, String origin, String importedFrom, boolean latest, boolean published, boolean abstractSchema, String suggestedResourceName, String suggestedUrlPrefix, Instant createdAt, String previousVersionUuid) { + this.id = id; + this.uuid = uuid; + this.versionUuid = versionUuid; + this.versionString = versionString; + this.name = name; + this.description = description; + this.definition = definition; + this.targetClasses = targetClasses; + this.extendSchemas = extendSchemas; + this.type = type; + this.origin = origin; + this.importedFrom = importedFrom; + this.latest = latest; + this.published = published; + this.abstractSchema = abstractSchema; + this.suggestedResourceName = suggestedResourceName; + this.suggestedUrlPrefix = suggestedUrlPrefix; + this.createdAt = createdAt; + this.previousVersionUuid = previousVersionUuid; + } + + public void setVersionString(String versionString) { + this.versionString = versionString; + this.version = new SemVer(versionString); + } + + public void setVersion(SemVer version) { + this.version = version; + this.versionString = version.toString(); + } + + public SemVer getVersion() { + if (this.version == null) { + this.version = new SemVer(this.versionString); + } + return version; + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchemaDraft.java b/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchemaDraft.java new file mode 100644 index 000000000..df8b6266a --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchemaDraft.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.entity.schema; + +import lombok.*; +import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.Indexed; +import org.springframework.data.mongodb.core.mapping.Document; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.time.Instant; +import java.util.List; +import java.util.Set; + +@Document +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder(toBuilder = true) +public class MetadataSchemaDraft { + + @Id + protected ObjectId id; + + @Indexed + private String uuid; + + @NotNull + @NotBlank + private String name; + + @NotNull + private String description; + + private boolean abstractSchema; + + @NotNull + private String definition; + + @NotNull + private Set targetClasses; + + @NotNull + private List extendSchemas; + + private String suggestedResourceName; + + private String suggestedUrlPrefix; + + private Instant createdAt; + + private Instant updatedAt; + +} diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/shape/ShapeType.java b/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchemaType.java similarity index 91% rename from src/main/java/nl/dtls/fairdatapoint/entity/shape/ShapeType.java rename to src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchemaType.java index 4fc355159..ea0fc938c 100644 --- a/src/main/java/nl/dtls/fairdatapoint/entity/shape/ShapeType.java +++ b/src/main/java/nl/dtls/fairdatapoint/entity/schema/MetadataSchemaType.java @@ -20,10 +20,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.entity.shape; - -public enum ShapeType { - - INTERNAL, CUSTOM +package nl.dtls.fairdatapoint.entity.schema; +public enum MetadataSchemaType { + INTERNAL, CUSTOM, REFERENCE } diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/schema/SemVer.java b/src/main/java/nl/dtls/fairdatapoint/entity/schema/SemVer.java new file mode 100644 index 000000000..39d3c5023 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/entity/schema/SemVer.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.entity.schema; + +import lombok.*; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@EqualsAndHashCode +@Builder(toBuilder = true) +public class SemVer implements Comparable { + + private int major; + + private int minor; + + private int patch; + + public String toString() { + return format("%d.%d.%d", major, minor, patch); + } + + public SemVer(String semverString) { + var parts = Arrays.stream(semverString.split("\\.")).map(Integer::parseInt).collect(Collectors.toList()); + major = parts.get(0); + minor = parts.get(1); + patch = parts.get(2); + } + + @Override + public int compareTo(SemVer other) { + if (other == null) { + return 1; + } + return Comparator.comparing(SemVer::getMajor) + .thenComparing(SemVer::getMinor) + .thenComparing(SemVer::getPatch) + .compare(this, other); + } + + public boolean isSuccessor(SemVer version) { + return compareTo(version) > 0; + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java b/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java index 327d767f9..f38679896 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java @@ -27,7 +27,6 @@ import nl.dtls.fairdatapoint.api.dto.index.ping.PingDTO; import nl.dtls.fairdatapoint.database.mongo.repository.EventRepository; import nl.dtls.fairdatapoint.database.mongo.repository.IndexEntryRepository; -import nl.dtls.fairdatapoint.entity.exception.ResourceNotFoundException; import nl.dtls.fairdatapoint.entity.index.entry.IndexEntry; import nl.dtls.fairdatapoint.entity.index.entry.IndexEntryState; import nl.dtls.fairdatapoint.entity.index.event.Event; @@ -42,16 +41,12 @@ import nl.dtls.fairdatapoint.service.index.entry.IndexEntryService; import nl.dtls.fairdatapoint.service.index.settings.IndexSettingsService; import nl.dtls.fairdatapoint.service.index.webhook.WebhookService; -import nl.dtls.fairdatapoint.util.HttpUtil; -import org.eclipse.rdf4j.util.iterators.EmptyIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.security.core.Authentication; @@ -61,7 +56,7 @@ import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import java.time.Instant; -import java.util.Optional; +import java.util.Collections; @Service public class EventService { @@ -106,7 +101,7 @@ public Iterable getEvents(IndexEntry indexEntry) { @RequiredEnabledIndexFeature public Iterable getEvents(String indexEntryUuid) { - return indexEntryService.getEntry(indexEntryUuid).map(this::getEvents).orElse(EmptyIterator::new); + return indexEntryService.getEntry(indexEntryUuid).map(this::getEvents).orElse(Collections.emptyList()); } @RequiredEnabledIndexFeature diff --git a/src/main/java/nl/dtls/fairdatapoint/service/metadata/validator/MetadataValidator.java b/src/main/java/nl/dtls/fairdatapoint/service/metadata/validator/MetadataValidator.java index b54656609..d22e4d52f 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/metadata/validator/MetadataValidator.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/metadata/validator/MetadataValidator.java @@ -29,7 +29,7 @@ import nl.dtls.fairdatapoint.service.metadata.exception.MetadataServiceException; import nl.dtls.fairdatapoint.service.rdf.ShaclValidator; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionService; -import nl.dtls.fairdatapoint.service.shape.ShapeService; +import nl.dtls.fairdatapoint.service.schema.MetadataSchemaService; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Model; import org.eclipse.rdf4j.model.vocabulary.RDF; @@ -52,7 +52,7 @@ public class MetadataValidator { private ShaclValidator shaclValidator; @Autowired - private ShapeService shapeService; + private MetadataSchemaService metadataSchemaService; @Autowired private ResourceDefinitionService resourceDefinitionService; @@ -65,7 +65,7 @@ public void validate(Model metadata, IRI uri, ResourceDefinition rd) throws Meta } private void validateByShacl(Model metadata, IRI uri) { - Model shacl = shapeService.getShaclFromShapes(); + Model shacl = metadataSchemaService.getShaclFromSchemas(); shaclValidator.validate(shacl, metadata, uri.stringValue()); } diff --git a/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java b/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java index 47807e408..dad14f455 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java @@ -22,10 +22,9 @@ */ package nl.dtls.fairdatapoint.service.profile; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; import nl.dtls.fairdatapoint.entity.resource.ResourceDefinition; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionService; -import nl.dtls.fairdatapoint.service.shape.ShapeService; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Model; import org.eclipse.rdf4j.model.Resource; @@ -54,7 +53,7 @@ public class ProfileService { private String persistentUrl; @Autowired - private ShapeRepository shapeRepository; + private MetadataSchemaRepository metadataSchemaRepository; @Autowired private ResourceDefinitionService resourceDefinitionService; @@ -65,18 +64,18 @@ private Model getProfileForResourceDefinition(ResourceDefinition rd, IRI uri) { profile.add(uri, RDFS.LABEL, l(format("%s Profile", rd.getName()))); profile.add(uri, i(format("%sisProfileOf", PROFILE_PREFIX)), i(format("%s/profile/core", persistentUrl))); - rd.getShapeUuids().forEach(shapeUuid -> shapeRepository.findByUuid(shapeUuid).map(shape -> { + rd.getMetadataSchemaUuids().forEach(schemaUuid -> metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaUuid).map(schema -> { ModelBuilder modelBuilder = new ModelBuilder(); Resource resource = bn(); modelBuilder.subject(resource); modelBuilder.add(RDF.TYPE, i(format("%s#ResourceDescriptor", PROFILE_PREFIX))); - modelBuilder.add(RDFS.LABEL, l(shape.getName())); + modelBuilder.add(RDFS.LABEL, l(schema.getName())); modelBuilder.add(DCTERMS.FORMAT, i("https://w3id.org/mediatype/text/turtle")); modelBuilder.add(DCTERMS.CONFORMS_TO, i("https://www.w3.org/TR/shacl/")); modelBuilder.add(i(format("%shasRole", PROFILE_PREFIX)), i(format("%srole/Validation", PROFILE_PREFIX))); - modelBuilder.add(i(format("%shasArtifact", PROFILE_PREFIX)), i(format("%s/shapes/%s", - persistentUrl, shapeUuid))); + modelBuilder.add(i(format("%shasArtifact", PROFILE_PREFIX)), i(format("%s/metadata-schemas/%s", + persistentUrl, schemaUuid))); profile.add(uri, i(format("%shasResource", PROFILE_PREFIX)), resource); profile.addAll(new ArrayList<>(modelBuilder.build())); return null; diff --git a/src/main/java/nl/dtls/fairdatapoint/service/rdf/ShaclValidator.java b/src/main/java/nl/dtls/fairdatapoint/service/rdf/ShaclValidator.java index 043a779a3..80d98b130 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/rdf/ShaclValidator.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/rdf/ShaclValidator.java @@ -45,12 +45,11 @@ public void validate(Model shacl, Model data, String baseUri) { // 1. Prepare repository ShaclSail shaclSail = new ShaclSail(new MemoryStore()); shaclSail.setRdfsSubClassReasoning(true); - shaclSail.setUndefinedTargetValidatesAllSubjects(true); SailRepository sailRepository = new SailRepository(shaclSail); sailRepository.init(); try (SailRepositoryConnection connection = sailRepository.getConnection()) { - // 2. Save Shacl + // 2. Save SHACL connection.begin(); connection.add(shacl, RDF4J.SHACL_SHAPE_GRAPH); connection.commit(); @@ -66,7 +65,7 @@ public void validate(Model shacl, Model data, String baseUri) { Model validationReportModel = ((ShaclSailValidationException) cause).validationReportAsModel(); throw new RdfValidationException(validationReportModel); } - throw new ValidationException("Validation failed (unsupported exception"); + throw new ValidationException("Validation failed (unsupported exception)"); } } diff --git a/src/main/java/nl/dtls/fairdatapoint/service/reset/FactoryDefaults.java b/src/main/java/nl/dtls/fairdatapoint/service/reset/FactoryDefaults.java index 7a4bfc505..ac117a06f 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/reset/FactoryDefaults.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/reset/FactoryDefaults.java @@ -27,11 +27,11 @@ import nl.dtls.fairdatapoint.entity.metadata.Metadata; import nl.dtls.fairdatapoint.entity.metadata.MetadataState; import nl.dtls.fairdatapoint.entity.resource.*; -import nl.dtls.fairdatapoint.entity.shape.Shape; -import nl.dtls.fairdatapoint.entity.shape.ShapeType; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaType; import nl.dtls.fairdatapoint.entity.user.User; import nl.dtls.fairdatapoint.entity.user.UserRole; -import nl.dtls.fairdatapoint.service.shape.ShapeShaclUtils; +import nl.dtls.fairdatapoint.service.schema.MetadataSchemaShaclUtils; import nl.dtls.fairdatapoint.util.KnownUUIDs; import nl.dtls.fairdatapoint.vocabulary.DATACITE; import nl.dtls.fairdatapoint.vocabulary.FDP; @@ -107,11 +107,11 @@ public class FactoryDefaults { .uuid(KnownUUIDs.RD_FDP_UUID) .name("FAIR Data Point") .urlPrefix("") - .shapeUuids(List.of( - KnownUUIDs.SHAPE_RESOURCE_UUID, - KnownUUIDs.SHAPE_DATASERVICE_UUID, - KnownUUIDs.SHAPE_METADATASERVICE_UUID, - KnownUUIDs.SHAPE_FDP_UUID + .metadataSchemaUuids(List.of( + KnownUUIDs.SCHEMA_RESOURCE_UUID, + KnownUUIDs.SCHEMA_DATASERVICE_UUID, + KnownUUIDs.SCHEMA_METADATASERVICE_UUID, + KnownUUIDs.SCHEMA_FDP_UUID )) .children(List.of( ResourceDefinitionChild.builder() @@ -133,9 +133,9 @@ public class FactoryDefaults { .uuid(KnownUUIDs.RD_CATALOG_UUID) .name("Catalog") .urlPrefix("catalog") - .shapeUuids(List.of( - KnownUUIDs.SHAPE_RESOURCE_UUID, - KnownUUIDs.SHAPE_CATALOG_UUID + .metadataSchemaUuids(List.of( + KnownUUIDs.SCHEMA_RESOURCE_UUID, + KnownUUIDs.SCHEMA_CATALOG_UUID )) .children(List.of( ResourceDefinitionChild.builder() @@ -157,9 +157,9 @@ public class FactoryDefaults { .uuid(KnownUUIDs.RD_DATASET_UUID) .name("Dataset") .urlPrefix("dataset") - .shapeUuids(List.of( - KnownUUIDs.SHAPE_RESOURCE_UUID, - KnownUUIDs.SHAPE_DATASET_UUID + .metadataSchemaUuids(List.of( + KnownUUIDs.SCHEMA_RESOURCE_UUID, + KnownUUIDs.SCHEMA_DATASET_UUID )) .children(List.of( ResourceDefinitionChild.builder() @@ -186,9 +186,9 @@ public class FactoryDefaults { .uuid(KnownUUIDs.RD_DISTRIBUTION_UUID) .name("Distribution") .urlPrefix("distribution") - .shapeUuids(List.of( - KnownUUIDs.SHAPE_RESOURCE_UUID, - KnownUUIDs.SHAPE_DISTRIBUTION_UUID + .metadataSchemaUuids(List.of( + KnownUUIDs.SCHEMA_RESOURCE_UUID, + KnownUUIDs.SCHEMA_DISTRIBUTION_UUID )) .children(List.of()) .externalLinks(List.of( @@ -205,87 +205,106 @@ public class FactoryDefaults { //== SHAPES //== Changes: Migration_0003_ShapeDefinition, Migration_0005_UpdateShapeDefinition, Migration_0006_ShapesSharing, Migration_0010_ComplyFDPO - public static Shape shapeResource() throws Exception { + public static MetadataSchema schemaResource() throws Exception { String definition = loadClassResource("shape-resource.ttl", FactoryDefaults.class); - return Shape.builder() - .uuid(KnownUUIDs.SHAPE_RESOURCE_UUID) + return MetadataSchema.builder() + .uuid(KnownUUIDs.SCHEMA_RESOURCE_UUID) .name("Resource") - .type(ShapeType.INTERNAL) + .type(MetadataSchemaType.INTERNAL) .published(false) + .abstractSchema(true) .definition(definition) - .targetClasses(ShapeShaclUtils.extractTargetClasses(definition)) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(definition)) .build(); } - public static Shape shapeFDP() throws Exception { + public static MetadataSchema schemaFDP() throws Exception { String definition = loadClassResource("shape-fdp.ttl", FactoryDefaults.class); - return Shape.builder() - .uuid(KnownUUIDs.SHAPE_FDP_UUID) + return MetadataSchema.builder() + .uuid(KnownUUIDs.SCHEMA_FDP_UUID) .name("FAIR Data Point") - .type(ShapeType.INTERNAL) + .type(MetadataSchemaType.INTERNAL) .published(false) + .abstractSchema(false) .definition(definition) - .targetClasses(ShapeShaclUtils.extractTargetClasses(definition)) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(definition)) + .suggestedResourceName("FAIR Data Point") + .suggestedUrlPrefix("") .build(); } - public static Shape shapeDataService() throws Exception { + public static MetadataSchema schemaDataService() throws Exception { String definition = loadClassResource("shape-data-service.ttl", FactoryDefaults.class); - return Shape.builder() - .uuid(KnownUUIDs.SHAPE_DATASERVICE_UUID) + return MetadataSchema.builder() + .uuid(KnownUUIDs.SCHEMA_DATASERVICE_UUID) .name("Data Service") - .type(ShapeType.INTERNAL) + .type(MetadataSchemaType.INTERNAL) .published(false) + .abstractSchema(false) .definition(definition) - .targetClasses(ShapeShaclUtils.extractTargetClasses(definition)) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(definition)) + .suggestedResourceName("Data Service") + .suggestedUrlPrefix("data-service") .build(); } - public static Shape shapeMetadataService() throws Exception { + public static MetadataSchema schemaMetadataService() throws Exception { String definition = loadClassResource("shape-metadata-service.ttl", FactoryDefaults.class); - return Shape.builder() - .uuid(KnownUUIDs.SHAPE_METADATASERVICE_UUID) + return MetadataSchema.builder() + .uuid(KnownUUIDs.SCHEMA_METADATASERVICE_UUID) .name("Metadata Service") - .type(ShapeType.INTERNAL) + .type(MetadataSchemaType.INTERNAL) .published(false) + .abstractSchema(false) .definition(definition) - .targetClasses(ShapeShaclUtils.extractTargetClasses(definition)) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(definition)) + .suggestedResourceName("Metadata Service") + .suggestedUrlPrefix("metadata-service") .build(); } - public static Shape shapeCatalog() throws Exception { + public static MetadataSchema schemaCatalog() throws Exception { String definition = loadClassResource("shape-catalog.ttl", FactoryDefaults.class); - return Shape.builder() - .uuid(KnownUUIDs.SHAPE_CATALOG_UUID) + return MetadataSchema.builder() + .uuid(KnownUUIDs.SCHEMA_CATALOG_UUID) .name("Catalog") - .type(ShapeType.INTERNAL) + .type(MetadataSchemaType.INTERNAL) .published(false) + .abstractSchema(false) .definition(definition) - .targetClasses(ShapeShaclUtils.extractTargetClasses(definition)) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(definition)) + .suggestedResourceName("Catalog") + .suggestedUrlPrefix("catalog") .build(); } - public static Shape shapeDataset() throws Exception { + public static MetadataSchema schemaDataset() throws Exception { String definition = loadClassResource("shape-dataset.ttl", FactoryDefaults.class); - return Shape.builder() - .uuid(KnownUUIDs.SHAPE_DATASET_UUID) + return MetadataSchema.builder() + .uuid(KnownUUIDs.SCHEMA_DATASET_UUID) .name("Dataset") - .type(ShapeType.CUSTOM) + .type(MetadataSchemaType.CUSTOM) .published(false) + .abstractSchema(false) .definition(definition) - .targetClasses(ShapeShaclUtils.extractTargetClasses(definition)) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(definition)) + .suggestedResourceName("Dataset") + .suggestedUrlPrefix("dataset") .build(); } - public static Shape shapeDistribution() throws Exception { + public static MetadataSchema schemaDistribution() throws Exception { String definition = loadClassResource("shape-distribution.ttl", FactoryDefaults.class); - return Shape.builder() - .uuid(KnownUUIDs.SHAPE_DISTRIBUTION_UUID) + return MetadataSchema.builder() + .uuid(KnownUUIDs.SCHEMA_DISTRIBUTION_UUID) .name("Distribution") - .type(ShapeType.CUSTOM) + .type(MetadataSchemaType.CUSTOM) .published(false) + .abstractSchema(false) .definition(definition) - .targetClasses(ShapeShaclUtils.extractTargetClasses(definition)) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(definition)) + .suggestedResourceName("Distribution") + .suggestedUrlPrefix("distribution") .build(); } diff --git a/src/main/java/nl/dtls/fairdatapoint/service/reset/ResetService.java b/src/main/java/nl/dtls/fairdatapoint/service/reset/ResetService.java index 78e7959c3..6f85034bb 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/reset/ResetService.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/reset/ResetService.java @@ -27,24 +27,14 @@ import nl.dtls.fairdatapoint.api.dto.reset.ResetDTO; import nl.dtls.fairdatapoint.database.mongo.repository.*; import nl.dtls.fairdatapoint.entity.resource.ResourceDefinition; -import nl.dtls.fairdatapoint.entity.settings.Settings; import nl.dtls.fairdatapoint.service.metadata.exception.MetadataServiceException; import nl.dtls.fairdatapoint.service.metadata.generic.GenericMetadataService; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionCache; import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionTargetClassesCache; import nl.dtls.fairdatapoint.service.settings.SettingsService; -import nl.dtls.fairdatapoint.vocabulary.DATACITE; -import nl.dtls.fairdatapoint.vocabulary.FDP; -import nl.dtls.fairdatapoint.vocabulary.R3D; -import nl.dtls.fairdatapoint.vocabulary.Sio; -import org.apache.commons.codec.digest.DigestUtils; import org.bson.Document; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Statement; -import org.eclipse.rdf4j.model.vocabulary.DCTERMS; -import org.eclipse.rdf4j.model.vocabulary.FOAF; -import org.eclipse.rdf4j.model.vocabulary.RDF; -import org.eclipse.rdf4j.model.vocabulary.RDFS; import org.eclipse.rdf4j.repository.Repository; import org.eclipse.rdf4j.repository.RepositoryConnection; import org.eclipse.rdf4j.repository.RepositoryException; @@ -57,10 +47,7 @@ import org.springframework.security.acls.model.AclCache; import org.springframework.stereotype.Service; -import java.time.OffsetDateTime; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Optional; import static java.lang.String.format; @@ -103,7 +90,7 @@ public class ResetService { private UserRepository userRepository; @Autowired - private ShapeRepository shapeRepository; + private MetadataSchemaRepository metadataSchemaRepository; @Autowired private ResourceDefinitionRepository resourceDefinitionRepository; @@ -146,9 +133,9 @@ public void resetToFactoryDefaults(ResetDTO reqDto) throws Exception { restoreDefaultMetadata(); } if (reqDto.isResourceDefinitions()) { - clearShapes(); + clearMetadataSchemas(); clearResourceDefinitions(); - restoreDefaultShapes(); + restoreDefaultMetadataSchemas(); restoreDefaultResourceDefinitions(); } resourceDefinitionCache.computeCache(); @@ -173,9 +160,9 @@ private void clearUsers() { userRepository.deleteAll(); } - private void clearShapes() { - log.debug("Clearing SHACL shapes"); - shapeRepository.deleteAll(); + private void clearMetadataSchemas() { + log.debug("Clearing metadata schemas"); + metadataSchemaRepository.deleteAll(); } private void clearResourceDefinitions() { @@ -223,15 +210,15 @@ private void restoreDefaultMetadata() { } } - private void restoreDefaultShapes() throws Exception { - log.debug("Creating default shapes"); - shapeRepository.save(FactoryDefaults.shapeResource()); - shapeRepository.save(FactoryDefaults.shapeDataService()); - shapeRepository.save(FactoryDefaults.shapeMetadataService()); - shapeRepository.save(FactoryDefaults.shapeFDP()); - shapeRepository.save(FactoryDefaults.shapeCatalog()); - shapeRepository.save(FactoryDefaults.shapeDataset()); - shapeRepository.save(FactoryDefaults.shapeDistribution()); + private void restoreDefaultMetadataSchemas() throws Exception { + log.debug("Creating default metadata schemas"); + metadataSchemaRepository.save(FactoryDefaults.schemaResource()); + metadataSchemaRepository.save(FactoryDefaults.schemaDataService()); + metadataSchemaRepository.save(FactoryDefaults.schemaMetadataService()); + metadataSchemaRepository.save(FactoryDefaults.schemaFDP()); + metadataSchemaRepository.save(FactoryDefaults.schemaCatalog()); + metadataSchemaRepository.save(FactoryDefaults.schemaDataset()); + metadataSchemaRepository.save(FactoryDefaults.schemaDistribution()); } private void restoreDefaultResourceDefinitions() { diff --git a/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionMapper.java b/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionMapper.java index 04d464840..c8e59b5c1 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionMapper.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionMapper.java @@ -37,7 +37,7 @@ public ResourceDefinition fromChangeDTO(ResourceDefinitionChangeDTO dto, String uuid, dto.getName(), dto.getUrlPrefix(), - dto.getShapeUuids(), + dto.getMetadataSchemaUuids(), dto.getChildren(), dto.getExternalLinks()); } @@ -46,7 +46,7 @@ public ResourceDefinitionChangeDTO toChangeDTO(ResourceDefinition rd) { return new ResourceDefinitionChangeDTO( rd.getName(), rd.getUrlPrefix(), - rd.getShapeUuids(), + rd.getMetadataSchemaUuids(), rd.getChildren(), rd.getExternalLinks()); } @@ -56,7 +56,7 @@ public ResourceDefinitionDTO toDTO(ResourceDefinition rd, List targetCla rd.getUuid(), rd.getName(), rd.getUrlPrefix(), - rd.getShapeUuids(), + rd.getMetadataSchemaUuids(), targetClassUris, rd.getChildren(), rd.getExternalLinks() diff --git a/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionService.java b/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionService.java index ac3444824..287662f37 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionService.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionService.java @@ -114,7 +114,7 @@ public ResourceDefinitionDTO create(ResourceDefinitionChangeDTO reqDto) throws B String uuid = UUID.randomUUID().toString(); ResourceDefinition rd = resourceDefinitionMapper.fromChangeDTO(reqDto, uuid); - // TODO: check if shapes exist + // TODO: check if schemas exist resourceDefinitionValidator.validate(rd); resourceDefinitionRepository.save(rd); @@ -136,7 +136,7 @@ public Optional update(String uuid, ResourceDefinitionCha ResourceDefinition updatedRd = resourceDefinitionMapper.fromChangeDTO(reqDto, rd.getUuid()); updatedRd.setId(rd.getId()); - // TODO: check if shapes exist + // TODO: check if schemas exist resourceDefinitionValidator.validate(updatedRd); resourceDefinitionRepository.save(updatedRd); diff --git a/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionTargetClassesCache.java b/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionTargetClassesCache.java index 6b22f4c23..39075d19a 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionTargetClassesCache.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/resource/ResourceDefinitionTargetClassesCache.java @@ -23,9 +23,9 @@ package nl.dtls.fairdatapoint.service.resource; import nl.dtls.fairdatapoint.database.mongo.repository.ResourceDefinitionRepository; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; import nl.dtls.fairdatapoint.entity.resource.ResourceDefinition; -import nl.dtls.fairdatapoint.entity.shape.Shape; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; @@ -48,7 +48,7 @@ public class ResourceDefinitionTargetClassesCache { private ResourceDefinitionRepository resourceDefinitionRepository; @Autowired - private ShapeRepository shapeRepository; + private MetadataSchemaRepository metadataSchemaRepository; @PostConstruct public void computeCache() { @@ -60,12 +60,28 @@ public void computeCache() { // Add to cache List rds = resourceDefinitionRepository.findAll(); - Map shapes = shapeRepository.findAll().stream().collect(Collectors.toMap(Shape::getUuid, Function.identity())); + Map metadataSchemaMap = new HashMap<>(); + metadataSchemaRepository.findAllByLatestIsTrue().forEach(schema -> { + if (!metadataSchemaMap.containsKey(schema.getUuid()) || metadataSchemaMap.get(schema.getUuid()).getVersion().compareTo(schema.getVersion()) < 0) { + metadataSchemaMap.put(schema.getUuid(), schema); + } + }); rds.forEach(rd -> { Set targetClassUris = new HashSet<>(); - rd.getShapeUuids().forEach(shapeUuid -> { - if (shapes.containsKey(shapeUuid)) { - targetClassUris.addAll(shapes.get(shapeUuid).getTargetClasses()); + rd.getMetadataSchemaUuids().forEach(schemaUuid -> { + if (metadataSchemaMap.containsKey(schemaUuid)) { + targetClassUris.addAll(metadataSchemaMap.get(schemaUuid).getTargetClasses()); + Queue parentUuids = new LinkedList<>(metadataSchemaMap.get(schemaUuid).getExtendSchemas()); + Set visitedParents = new HashSet<>(); + String parentUuid = null; + while (!parentUuids.isEmpty()) { + parentUuid = parentUuids.poll(); + if (!visitedParents.contains(parentUuid)) { + visitedParents.add(parentUuid); + targetClassUris.addAll(metadataSchemaMap.get(parentUuid).getTargetClasses()); + parentUuids.addAll(metadataSchemaMap.get(parentUuid).getExtendSchemas()); + } + } } }); cache.put(rd.getUuid(), targetClassUris.stream().toList()); diff --git a/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaMapper.java b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaMapper.java new file mode 100644 index 000000000..509993dd1 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaMapper.java @@ -0,0 +1,265 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.service.schema; + +import nl.dtls.fairdatapoint.api.dto.schema.*; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaType; +import nl.dtls.fairdatapoint.entity.schema.SemVer; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class MetadataSchemaMapper { + + public MetadataSchemaDraft fromChangeDTO(MetadataSchemaChangeDTO dto, String uuid) { + Instant now = Instant.now(); + return MetadataSchemaDraft.builder() + .uuid(uuid) + .name(dto.getName()) + .description(dto.getDescription()) + .abstractSchema(dto.isAbstractSchema()) + .definition(dto.getDefinition()) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(dto.getDefinition())) + .extendSchemas(dto.getExtendsSchemaUuids()) + .suggestedResourceName(dto.getSuggestedResourceName()) + .suggestedUrlPrefix(dto.getSuggestedUrlPrefix()) + .createdAt(now) + .updatedAt(now) + .build(); + } + + public MetadataSchemaDraft fromChangeDTO(MetadataSchemaChangeDTO dto, MetadataSchemaDraft draft) { + return + draft + .toBuilder() + .name(dto.getName()) + .abstractSchema(dto.isAbstractSchema()) + .description(dto.getDescription()) + .definition(dto.getDefinition()) + .extendSchemas(dto.getExtendsSchemaUuids()) + .targetClasses(MetadataSchemaShaclUtils.extractTargetClasses(dto.getDefinition())) + .suggestedResourceName(dto.getSuggestedResourceName()) + .suggestedUrlPrefix(dto.getSuggestedUrlPrefix()) + .build(); + } + + public MetadataSchemaDraftDTO toDraftDTO(MetadataSchemaDraft draft, MetadataSchema lastVersion) { + return MetadataSchemaDraftDTO.builder() + .uuid(draft.getUuid()) + .name(draft.getName()) + .description(draft.getDescription()) + .abstractSchema(draft.isAbstractSchema()) + .definition(draft.getDefinition()) + .extendsSchemaUuids(draft.getExtendSchemas()) + .suggestedResourceName(draft.getSuggestedResourceName()) + .suggestedUrlPrefix(draft.getSuggestedUrlPrefix()) + .lastVersion(lastVersion == null ? null : lastVersion.getVersionString()) + .build(); + } + + public MetadataSchemaDraftDTO toDraftDTO(MetadataSchema schema) { + return MetadataSchemaDraftDTO.builder() + .uuid(schema.getUuid()) + .name(schema.getName()) + .description(schema.getDescription()) + .abstractSchema(schema.isAbstractSchema()) + .definition(schema.getDefinition()) + .extendsSchemaUuids(schema.getExtendSchemas()) + .suggestedResourceName(schema.getSuggestedResourceName()) + .suggestedUrlPrefix(schema.getSuggestedUrlPrefix()) + .lastVersion(schema.getVersionString()) + .build(); + } + + public MetadataSchemaVersionDTO toVersionDTO(MetadataSchema schema) { + return MetadataSchemaVersionDTO.builder() + .uuid(schema.getUuid()) + .versionUuid(schema.getVersionUuid()) + .version(schema.getVersion().toString()) + .name(schema.getName()) + .description(schema.getDescription()) + .published(schema.isPublished()) + .abstractSchema(schema.isAbstractSchema()) + .latest(schema.isLatest()) + .type(schema.getType()) + .origin(schema.getOrigin()) + .importedFrom(schema.getImportedFrom()) + .definition(schema.getDefinition()) + .targetClasses(schema.getTargetClasses()) + .extendsSchemaUuids(schema.getExtendSchemas()) + .suggestedResourceName(schema.getSuggestedResourceName()) + .suggestedUrlPrefix(schema.getSuggestedUrlPrefix()) + .previousVersionUuid(schema.getPreviousVersionUuid()) + .build(); + } + + public MetadataSchema fromReleaseDTO(MetadataSchemaReleaseDTO reqDto, MetadataSchemaDraft draft, String versionUuid) { + return MetadataSchema.builder() + .uuid(draft.getUuid()) + .versionUuid(versionUuid) + .version(new SemVer(reqDto.getVersion())) + .versionString(reqDto.getVersion()) + .type(MetadataSchemaType.CUSTOM) + .origin(null) + .name(draft.getName()) + .description(reqDto.getDescription()) + .definition(draft.getDefinition()) + .targetClasses(draft.getTargetClasses()) + .extendSchemas(draft.getExtendSchemas()) + .abstractSchema(draft.isAbstractSchema()) + .published(reqDto.isPublished()) + .latest(true) + .previousVersionUuid(null) + .suggestedResourceName(draft.getSuggestedResourceName()) + .suggestedUrlPrefix(draft.getSuggestedUrlPrefix()) + .createdAt(Instant.now()) + .build(); + } + + public MetadataSchemaDTO toDTO( + MetadataSchema latest, + MetadataSchemaDraft draft, + List schemaVersions, + List childSchemas + ) { + if (latest != null) { + return MetadataSchemaDTO.builder() + .uuid(latest.getUuid()) + .name(latest.getName()) + .latest(toVersionDTO(latest)) + .draft(draft == null ? null : toDraftDTO(draft, latest)) + .versions(schemaVersions + .stream() + .map(MetadataSchema::getVersion) + .sorted() + .map(SemVer::toString) + .toList() + ) + .extendSchemaUuids(latest.getExtendSchemas()) + .childSchemaUuids(childSchemas + .stream() + .map(MetadataSchema::getUuid) + .toList() + ) + .build(); + } + if (draft != null) { + return MetadataSchemaDTO.builder() + .uuid(draft.getUuid()) + .name(draft.getName()) + .latest(null) + .draft(toDraftDTO(draft, latest)) + .versions(schemaVersions + .stream() + .map(MetadataSchema::getVersion) + .sorted() + .map(SemVer::toString) + .toList() + ) + .extendSchemaUuids(Collections.emptyList()) + .childSchemaUuids(Collections.emptyList()) + .build(); + } + return null; + } + + public MetadataSchema fromUpdateDTO(MetadataSchema schema, MetadataSchemaUpdateDTO reqDto) { + return + schema + .toBuilder() + .name(reqDto.getName()) + .description(reqDto.getDescription()) + .published(reqDto.isPublished()) + .build(); + } + + public MetadataSchemaDraft toDraft(MetadataSchema schema) { + return MetadataSchemaDraft.builder() + .uuid(schema.getUuid()) + .name(schema.getName()) + .name(schema.getName()) + .description(schema.getDescription()) + .abstractSchema(schema.isAbstractSchema()) + .definition(schema.getDefinition()) + .extendSchemas(schema.getExtendSchemas()) + .suggestedResourceName(schema.getSuggestedResourceName()) + .suggestedUrlPrefix(schema.getSuggestedUrlPrefix()) + .build(); + } + + public MetadataSchemaVersionDTO toPublishedVersionDTO(MetadataSchema schema, String persistentUrl) { + MetadataSchemaVersionDTO dto = toVersionDTO(schema); + if (dto.getOrigin() == null) { + dto.setOrigin(persistentUrl); + } + dto.setImportedFrom(persistentUrl); + return dto; + } + + public MetadataSchema fromRemoteVersion(MetadataSchemaVersionDTO remoteVersion) { + return MetadataSchema.builder() + .uuid(remoteVersion.getUuid()) + .versionUuid(remoteVersion.getVersionUuid()) + .name(remoteVersion.getName()) + .description(remoteVersion.getDescription()) + .definition(remoteVersion.getDefinition()) + .versionString(remoteVersion.getVersion()) + .origin(remoteVersion.getOrigin()) + .importedFrom(remoteVersion.getImportedFrom()) + .extendSchemas(remoteVersion.getExtendsSchemaUuids()) + .targetClasses(remoteVersion.getTargetClasses()) + .abstractSchema(remoteVersion.isAbstractSchema()) + .published(false) + .suggestedResourceName(remoteVersion.getSuggestedResourceName()) + .suggestedUrlPrefix(remoteVersion.getSuggestedUrlPrefix()) + .type(MetadataSchemaType.REFERENCE) + .previousVersionUuid(remoteVersion.getPreviousVersionUuid()) + .createdAt(Instant.now()) + .build(); + } + + public MetadataSchema fromRemoteVersion(MetadataSchemaVersionDTO remoteVersion, MetadataSchema schema) { + return schema.toBuilder() + .name(remoteVersion.getName()) + .description(remoteVersion.getDescription()) + .definition(remoteVersion.getDefinition()) + .extendSchemas(remoteVersion.getExtendsSchemaUuids()) + .type(MetadataSchemaType.REFERENCE) + .targetClasses(remoteVersion.getTargetClasses()) + .abstractSchema(remoteVersion.isAbstractSchema()) + .published(schema.isPublished()) + .origin(remoteVersion.getOrigin()) + .importedFrom(remoteVersion.getImportedFrom()) + .suggestedUrlPrefix(remoteVersion.getSuggestedUrlPrefix()) + .suggestedResourceName(remoteVersion.getSuggestedResourceName()) + .versionString(remoteVersion.getVersion()) + .createdAt(Instant.now()) + .build(); + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeRetrievalUtils.java b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaRetrievalUtils.java similarity index 65% rename from src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeRetrievalUtils.java rename to src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaRetrievalUtils.java index fffe2a27b..faf3601b7 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeRetrievalUtils.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaRetrievalUtils.java @@ -20,13 +20,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.service.shape; +package nl.dtls.fairdatapoint.service.schema; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.entity.exception.ShapeImportException; +import lombok.extern.slf4j.Slf4j; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaVersionDTO; +import nl.dtls.fairdatapoint.entity.exception.MetadataSchemaImportException; import org.springframework.http.HttpHeaders; import java.io.IOException; @@ -36,9 +37,11 @@ import java.net.http.HttpResponse; import java.time.Duration; import java.util.List; -import java.util.Optional; -public class ShapeRetrievalUtils { +import static java.lang.String.format; + +@Slf4j +public class MetadataSchemaRetrievalUtils { private static final HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -48,23 +51,27 @@ public class ShapeRetrievalUtils { private static final ObjectMapper objectMapper = new ObjectMapper(); - private static final TypeReference> responseType = new TypeReference<>() { + private static final TypeReference> responseType = new TypeReference<>() { }; - public static List retrievePublishedShapes(String fdpUrl) { + public static List retrievePublishedMetadataSchemas(String fdpUrl) { try { + log.info(format("Retrieving published metadata schemas from %s", fdpUrl)); HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(fdpUrl.replaceAll("/$", "") + "/shapes/public")) + .uri(URI.create(fdpUrl.replaceAll("/$", "") + "/metadata-schemas/public")) .header(HttpHeaders.ACCEPT, "application/json") .GET().build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); return objectMapper.readValue(response.body(), responseType); } catch (JsonProcessingException e) { - throw new ShapeImportException(fdpUrl, "Cannot process response: " + e.getMessage()); + log.warn(format("Could not parse published metadata schemas from %s: %s", fdpUrl, e.getMessage())); + throw new MetadataSchemaImportException(fdpUrl, "Cannot process response: " + e.getMessage()); } catch (IOException e) { - throw new ShapeImportException(fdpUrl, "Cannot get response: " + e.getMessage()); + log.warn(format("Could not retrieve published metadata schemas from %s: %s", fdpUrl, e.getMessage())); + throw new MetadataSchemaImportException(fdpUrl, "Cannot get response: " + e.getMessage()); } catch (Exception e) { - throw new ShapeImportException(fdpUrl, e.getMessage()); + log.warn(format("Could not retrieve published metadata schemas from %s: %s", fdpUrl, e.getMessage())); + throw new MetadataSchemaImportException(fdpUrl, e.getMessage()); } } } diff --git a/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaService.java b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaService.java new file mode 100644 index 000000000..df47e20a6 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaService.java @@ -0,0 +1,526 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.service.schema; + +import lombok.extern.slf4j.Slf4j; +import nl.dtls.fairdatapoint.api.dto.schema.*; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaDraftRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.exception.ResourceNotFoundException; +import nl.dtls.fairdatapoint.entity.exception.ValidationException; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaType; +import nl.dtls.fairdatapoint.entity.schema.SemVer; +import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionTargetClassesCache; +import nl.dtls.fairdatapoint.util.RdfIOUtil; +import org.eclipse.rdf4j.model.Model; +import org.eclipse.rdf4j.model.impl.LinkedHashModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; + +import javax.validation.Valid; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.lang.String.format; +import static java.util.Optional.empty; +import static java.util.Optional.of; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toMap; + +@Service +@Slf4j +public class MetadataSchemaService { + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Autowired + private MetadataSchemaDraftRepository metadataSchemaDraftRepository; + + @Autowired + private MetadataSchemaMapper metadataSchemaMapper; + + @Autowired + private MetadataSchemaValidator metadataSchemaValidator; + + @Autowired + private ResourceDefinitionTargetClassesCache targetClassesCache; + + @Autowired + private String persistentUrl; + + // =============================================================================================== + // Schema drafts + + @PreAuthorize("hasRole('ADMIN')") + public MetadataSchemaDraftDTO createSchemaDraft(MetadataSchemaChangeDTO reqDto) { + String uuid = UUID.randomUUID().toString(); + + // Validate + metadataSchemaValidator.validateAllExist(reqDto.getExtendsSchemaUuids()); + + MetadataSchemaDraft draft = metadataSchemaMapper.fromChangeDTO(reqDto, uuid); + metadataSchemaDraftRepository.save(draft); + return metadataSchemaMapper.toDraftDTO(draft, null); + } + + @PreAuthorize("hasRole('ADMIN')") + public Optional getSchemaDraft(String uuid) { + Optional oDraft = metadataSchemaDraftRepository.findByUuid(uuid); + Optional oLatest = metadataSchemaRepository.findByUuidAndLatestIsTrue(uuid); + if (oDraft.isPresent()) { + return oDraft.map(draft -> metadataSchemaMapper.toDraftDTO(draft, oLatest.orElse(null))); + } + return oLatest.map(latest -> metadataSchemaMapper.toDraftDTO(latest)); + } + + @PreAuthorize("hasRole('ADMIN')") + public Optional updateSchemaDraft(String uuid, MetadataSchemaChangeDTO reqDto) { + Optional oDraft = metadataSchemaDraftRepository.findByUuid(uuid); + Optional oSchema = metadataSchemaRepository.findByUuidAndLatestIsTrue(uuid); + MetadataSchemaDraft baseDraft; + // Check if present + if (oDraft.isPresent()) { + baseDraft = oDraft.get(); + } else { + if (oSchema.isEmpty()) { + return empty(); + } + MetadataSchema schema = oSchema.get(); + baseDraft = metadataSchemaMapper.toDraft(schema); + } + // Validate + metadataSchemaValidator.validateAllExist(reqDto.getExtendsSchemaUuids()); + metadataSchemaValidator.validateNoExtendsCycle(uuid, reqDto.getExtendsSchemaUuids()); + // Save + MetadataSchemaDraft updatedDraft = metadataSchemaMapper.fromChangeDTO(reqDto, baseDraft); + metadataSchemaDraftRepository.save(updatedDraft); + return of(metadataSchemaMapper.toDraftDTO(updatedDraft, oSchema.orElse(null))); + } + + @PreAuthorize("hasRole('ADMIN')") + public boolean deleteSchemaDraft(String uuid) { + Optional oDraft = metadataSchemaDraftRepository.findByUuid(uuid); + if (oDraft.isEmpty()) { + return false; + } + MetadataSchemaDraft draft = oDraft.get(); + metadataSchemaDraftRepository.delete(draft); + return true; + } + + @PreAuthorize("hasRole('ADMIN')") + public Optional releaseDraft(String uuid, MetadataSchemaReleaseDTO reqDto) { + Optional oDraft = metadataSchemaDraftRepository.findByUuid(uuid); + // Check if present + if (oDraft.isEmpty()) { + return empty(); + } + // Update + MetadataSchemaDraft draft = oDraft.get(); + String versionUuid = UUID.randomUUID().toString(); + MetadataSchema newLatest = metadataSchemaMapper.fromReleaseDTO(reqDto, draft, versionUuid); + Optional oLatest = metadataSchemaRepository.findByUuidAndLatestIsTrue(uuid); + oLatest.map(MetadataSchema::getVersionUuid).ifPresent(newLatest::setPreviousVersionUuid); + // Validate & Save + metadataSchemaValidator.validateAllExist(newLatest.getExtendSchemas()); + // validate all parents are published if publishing + if (reqDto.isPublished()) { + List parents = resolveExtends(draft); + if (!parents.stream().allMatch(MetadataSchema::isPublished)) { + throw new ValidationException("Cannot publish as not all parents (via extends) are published"); + } + } + if (oLatest.isPresent()) { + MetadataSchema oldLatest = oLatest.get(); + oldLatest.setLatest(false); // transactions would be nice + metadataSchemaValidator.validate(newLatest, oldLatest); + metadataSchemaRepository.save(oldLatest); + } else { + metadataSchemaValidator.validate(newLatest); + } + metadataSchemaRepository.save(newLatest); + metadataSchemaDraftRepository.delete(draft); + // Update cache + targetClassesCache.computeCache(); + List versions = metadataSchemaRepository.findByUuid(uuid); + List childs = metadataSchemaRepository.findAllByExtendSchemasContains(uuid); + return of(metadataSchemaMapper.toDTO(newLatest, draft, versions, childs)); + } + + // =============================================================================================== + // Schema versions + + private Optional getByUuidAndVersion(String uuid, String version) { + if (Objects.equals(version, "latest")) { + return metadataSchemaRepository.findByUuidAndLatestIsTrue(uuid); + } + return metadataSchemaRepository.findByUuidAndVersionString(uuid, version); + } + + public Optional getVersion(String uuid, String version) { + return getByUuidAndVersion(uuid, version).map(metadataSchemaMapper::toVersionDTO); + } + + @PreAuthorize("hasRole('ADMIN')") + public Optional updateVersion(String uuid, String version, MetadataSchemaUpdateDTO reqDto) { + Optional oSchema = getByUuidAndVersion(uuid, version); + if (oSchema.isEmpty()) { + return empty(); + } + MetadataSchema schema = oSchema.get(); + // validate all parents are published if publishing + if (!schema.isPublished() && reqDto.isPublished()) { + List parents = resolveExtends(schema); + if (!parents.stream().allMatch(MetadataSchema::isPublished)) { + throw new ValidationException("Cannot publish as not all parents (via extends) are published"); + } + } + // result + MetadataSchema updatedSchema = metadataSchemaRepository.save(metadataSchemaMapper.fromUpdateDTO(schema, reqDto)); + return of(metadataSchemaMapper.toVersionDTO(updatedSchema)); + } + + @PreAuthorize("hasRole('ADMIN')") + public boolean deleteVersion(String uuid, String version) { + Optional oSchema = getByUuidAndVersion(uuid, version); + // Check if present + if (oSchema.isEmpty()) { + return false; + } + // Validate and fix links + MetadataSchema schema = oSchema.get(); + MetadataSchema previous = null; + if (schema.getPreviousVersionUuid() != null) { + previous = metadataSchemaRepository.findByVersionUuid(schema.getPreviousVersionUuid()).orElse(null); + } + Optional oNewer = metadataSchemaRepository.findByPreviousVersionUuid(schema.getVersionUuid()); + if (schema.isLatest()) { + if (previous == null) { + metadataSchemaValidator.validateNotUsed(uuid); + } else { + previous.setLatest(true); + metadataSchemaValidator.validateNoExtendsCycle(uuid, previous.getExtendSchemas()); + metadataSchemaRepository.save(previous); + } + } else if (oNewer.isPresent()) { + MetadataSchema newer = oNewer.get(); + newer.setPreviousVersionUuid(previous == null ? null : previous.getVersionUuid()); + metadataSchemaRepository.save(newer); + } + metadataSchemaRepository.delete(schema); + return true; + } + + @PreAuthorize("hasRole('ADMIN')") + public boolean deleteSchemaFull(String uuid) { + List schemas = metadataSchemaRepository.findByUuid(uuid); + Optional oDraft = metadataSchemaDraftRepository.findByUuid(uuid); + // Check if present + if (schemas.isEmpty() && oDraft.isEmpty()) { + return false; + } + // Validate + metadataSchemaValidator.validateNotUsed(uuid); + if (schemas.stream().anyMatch(schema -> schema.getType() == MetadataSchemaType.INTERNAL)) { + throw new ValidationException("You can't delete INTERNAL Shape"); + } + // Delete + if (!schemas.isEmpty()) { + metadataSchemaRepository.deleteAll(schemas); + } + oDraft.ifPresent(draft -> metadataSchemaDraftRepository.delete(draft)); + // Update cache + targetClassesCache.computeCache(); + return true; + } + + // =============================================================================================== + // Reading schemas + + public List getSchemasWithoutDrafts(boolean includeAbstract) { + return metadataSchemaRepository + .findAllByLatestIsTrue() + .stream() + .filter(s -> includeAbstract || !s.isAbstractSchema()) + .map(schema -> { + List versions = metadataSchemaRepository.findByUuid(schema.getUuid()); + List children = metadataSchemaRepository.findAllByExtendSchemasContains(schema.getUuid()); + return metadataSchemaMapper.toDTO(schema, null, versions, children); + }) + .toList(); + } + + public List getSchemasWithDrafts(boolean includeAbstract) { + Set listedUuids = new HashSet<>(); + Stream schemas = metadataSchemaRepository + .findAllByLatestIsTrue() + .stream() + .filter(s -> includeAbstract || !s.isAbstractSchema()) + .map(schema -> { + List versions = metadataSchemaRepository.findByUuid(schema.getUuid()); + List children = metadataSchemaRepository.findAllByExtendSchemasContains(schema.getUuid()); + Optional oDraft = metadataSchemaDraftRepository.findByUuid(schema.getUuid()); + listedUuids.add(schema.getUuid()); + return metadataSchemaMapper.toDTO(schema, oDraft.orElse(null), versions, children); + }); + + Stream drafts = metadataSchemaDraftRepository + .findAll() + .stream() + .filter(d -> !listedUuids.contains(d.getUuid()) && (includeAbstract || !d.isAbstractSchema())) + .map(draft -> metadataSchemaMapper.toDTO(null, draft, Collections.emptyList(), Collections.emptyList())); + return Stream.concat(schemas, drafts).toList(); + } + + public Optional getSchemaByUuid(String uuid) { + Optional oSchema = metadataSchemaRepository.findByUuidAndLatestIsTrue(uuid); + Optional oDraft = metadataSchemaDraftRepository.findByUuid(uuid); + return oSchema.map(schema -> { + List versions = metadataSchemaRepository.findByUuid(schema.getUuid()); + List children = metadataSchemaRepository.findAllByExtendSchemasContains(schema.getUuid()); + return metadataSchemaMapper.toDTO(schema, oDraft.orElse(null), versions, children); + }); + } + + public Optional getSchemaContentByUuid(String uuid) { + // TODO: cache (?) + Optional oSchema = metadataSchemaRepository.findByUuidAndLatestIsTrue(uuid); + if (oSchema.isEmpty()) { + return empty(); + } + MetadataSchema schema = oSchema.get(); + List schemas = resolveExtends(schema); + return of(mergeSchemaDefinitions(schemas)); + } + + public Model getShaclFromSchemas() { + return mergeSchemaDefinitions(metadataSchemaRepository.findAllByLatestIsTrue()); + } + + public Model getShaclFromSchemas(List metadataSchemaUuids) { + HashSet schemaUuids = new HashSet<>(metadataSchemaUuids); + List schemas = schemaUuids + .stream() + .map(schemaUuid -> metadataSchemaRepository + .findByUuidAndLatestIsTrue(schemaUuid) + .orElseThrow(() -> new ResourceNotFoundException(format("Metadata schema '%s' not found", schemaUuid))) + ) + .toList(); + return mergeSchemaDefinitions(resolveExtends(schemas)); + } + + // =============================================================================================== + // Extends and SHACL manipulation + private List resolveExtends(MetadataSchema schema) { + return resolveExtends(List.of(schema)); + } + + private List resolveExtends(MetadataSchemaDraft draft) { + return resolveExtends( + draft + .getExtendSchemas() + .stream() + .map(schemaUuid -> metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaUuid).orElse(null)) + .toList() + ); + } + + private List resolveExtends(List schemas) { + Map allSchemas = metadataSchemaRepository + .findAllByLatestIsTrue() + .stream() + .collect(Collectors.toMap(MetadataSchema::getUuid, Function.identity())); + Set addedSchemaUuids = new HashSet<>(); + List result = new ArrayList<>(); + schemas.forEach(schema -> { + addedSchemaUuids.add(schema.getUuid()); + result.add(schema); + }); + int index = 0; + while (index < result.size()) { + result.get(index).getExtendSchemas().forEach(extendUuid -> { + if (!addedSchemaUuids.contains(extendUuid) && allSchemas.containsKey(extendUuid)) { + result.add(allSchemas.get(extendUuid)); + } + }); + index++; + } + return result; + } + + private Model mergeSchemaDefinitions(List schemas) { + Model model = new LinkedHashModel(); + schemas.stream() + .map(s -> RdfIOUtil.read(s.getDefinition(), "")) + .forEach(m -> model.addAll(new ArrayList<>(m))); + return model; + } + + // =============================================================================================== + // Importing and sharing + + public List getPublishedSchemas() { + return metadataSchemaRepository + .findAllByPublishedIsTrue() + .stream() + .map(schema -> metadataSchemaMapper.toPublishedVersionDTO(schema, persistentUrl)) + .toList(); + } + + private MetadataSchemaRemoteDTO toRemoteSchema(MetadataSchemaVersionDTO remoteDto) { + Optional localVersion = metadataSchemaRepository.findByVersionUuid(remoteDto.getVersionUuid()); + List localSchemas = metadataSchemaRepository.findByUuid(remoteDto.getUuid()); + boolean isDirty = false; + MetadataSchemaRemoteState status = MetadataSchemaRemoteState.NOT_IMPORTED; + if (localVersion.isPresent()) { + isDirty = !Objects.equals(localVersion.get().getDefinition(), remoteDto.getDefinition()); + // TODO: compare more + } + boolean hasConflict = localSchemas.stream().anyMatch(schema -> schema.getType() == MetadataSchemaType.CUSTOM); + if (localVersion.isEmpty() && hasConflict) { + status = MetadataSchemaRemoteState.CONFLICT; + } else if (isDirty) { + status = MetadataSchemaRemoteState.DIRTY; + } else if (localVersion.isPresent()) { + status = MetadataSchemaRemoteState.ALREADY_IMPORTED; + } + return MetadataSchemaRemoteDTO + .builder() + .schema(remoteDto) + .status(status) + .canImport(!hasConflict && localVersion.isEmpty()) + .build(); + } + + public List getRemoteSchemas(String fdpUrl) { + return MetadataSchemaRetrievalUtils + .retrievePublishedMetadataSchemas(fdpUrl) + .parallelStream() + .map(this::toRemoteSchema) + .toList(); + } + + public List importSchemas(String schemaUuid, List remoteVersions) { + // prepare and check local versions + Map versions = metadataSchemaRepository + .findByUuid(schemaUuid) + .stream() + .collect(toMap(MetadataSchema::getVersionUuid, Function.identity())); + if (versions.values().stream().anyMatch(version -> version.getType() == MetadataSchemaType.CUSTOM)) { + throw new ValidationException(format("Schema has CUSTOM version(s): %s", schemaUuid)); + } + // update from remote + remoteVersions.forEach(remoteVersion -> { + if (versions.containsKey(remoteVersion.getVersionUuid())) { + versions.put(remoteVersion.getVersionUuid(), metadataSchemaMapper.fromRemoteVersion(remoteVersion, versions.get(remoteVersion.getVersionUuid()))); + } else { + versions.put(remoteVersion.getVersionUuid(), metadataSchemaMapper.fromRemoteVersion(remoteVersion)); + } + }); + // fix versions chain + Map versionMap = versions.values().stream().collect(toMap(MetadataSchema::getVersionString, Function.identity())); + List versionsSorted = versionMap.keySet().stream().map(SemVer::new).sorted().map(SemVer::toString).toList(); + String previousVersionUuid = null; + for (String version : versionsSorted) { + versionMap.get(version).setPreviousVersionUuid(previousVersionUuid); + versionMap.get(version).setLatest(false); + previousVersionUuid = versionMap.get(version).getVersionUuid(); + } + versionMap.get(versionsSorted.get(versionsSorted.size()-1)).setLatest(true); + return versionMap.values().stream().toList(); + } + + public List importSchemas(@Valid List reqDtos) { + // Validate + reqDtos.forEach(dto -> metadataSchemaValidator.validate(dto)); + List localLatestSchemas = metadataSchemaRepository.findAllByLatestIsTrue(); + Map> schemas = reqDtos.stream().collect(groupingBy(MetadataSchemaVersionDTO::getUuid)); + Set toBePresentUuids = localLatestSchemas.stream().map(MetadataSchema::getUuid).collect(Collectors.toSet()); + toBePresentUuids.addAll(schemas.keySet()); + reqDtos.forEach(dto -> { + if (dto.getExtendsSchemaUuids().stream().anyMatch(uuid -> !toBePresentUuids.contains(uuid))) { + throw new ValidationException("Missing schema for extends relation"); + } + }); + List toSave = new ArrayList<>(); + schemas.forEach((schemaUuid, versions) -> { + toSave.addAll(importSchemas(schemaUuid, versions)); + }); + metadataSchemaRepository.saveAll(toSave); + return reqDtos + .parallelStream() + .map(v -> metadataSchemaRepository.findByVersionUuid(v.getVersionUuid()).orElse(null)) + .filter(Objects::nonNull) + .map(metadataSchemaMapper::toVersionDTO) + .toList(); + } + + private List checkForUpdates(String fdpUrl) { + try { + Map> remoteSchemas = MetadataSchemaRetrievalUtils + .retrievePublishedMetadataSchemas(fdpUrl) + .stream() + .collect(groupingBy(MetadataSchemaVersionDTO::getUuid)); + List updates = new ArrayList<>(); + remoteSchemas.forEach((schemaUuid, remoteVersions) -> { + List localVersions = metadataSchemaRepository.findByUuid(schemaUuid); + boolean hasCustom = localVersions.stream().anyMatch(schema -> schema.getType() == MetadataSchemaType.CUSTOM); + boolean allImportedFromThis = localVersions.stream().allMatch(schema -> schema.getImportedFrom().equals(fdpUrl)); + if (!hasCustom && allImportedFromThis && !localVersions.isEmpty()) { + Set localVersionUuids = localVersions.stream().map(MetadataSchema::getVersionUuid).collect(Collectors.toSet()); + updates.addAll(remoteVersions.stream().filter(v -> !localVersionUuids.contains(v.getVersionUuid())).toList()); + } + }); + return updates + .stream() + .map(schemaVersion -> MetadataSchemaRemoteDTO.builder() + .canImport(true) + .schema(schemaVersion) + .status(MetadataSchemaRemoteState.NOT_IMPORTED) + .build() + ).toList(); + } catch (Exception e) { + log.warn(format("Failed to check for updates from %s: %s", fdpUrl, e.getMessage())); + return Collections.emptyList(); + } + } + + public List checkForUpdates() { + Set importSources = metadataSchemaRepository + .findAllByImportedFromIsNotNull() + .stream() + .map(MetadataSchema::getImportedFrom) + .collect(Collectors.toSet()); + return importSources + .stream() + .map(this::checkForUpdates) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeShaclUtils.java b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaShaclUtils.java similarity index 95% rename from src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeShaclUtils.java rename to src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaShaclUtils.java index 6a6f1da22..fc6ef84ec 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeShaclUtils.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaShaclUtils.java @@ -20,10 +20,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.service.shape; +package nl.dtls.fairdatapoint.service.schema; import nl.dtls.fairdatapoint.util.RdfIOUtil; -import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Model; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Value; @@ -34,7 +33,7 @@ import static nl.dtls.fairdatapoint.util.ValueFactoryHelper.i; -public class ShapeShaclUtils { +public class MetadataSchemaShaclUtils { public static Set extractTargetClasses(String definition) { var model = RdfIOUtil.read(definition, ""); diff --git a/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaValidator.java b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaValidator.java new file mode 100644 index 000000000..883141a58 --- /dev/null +++ b/src/main/java/nl/dtls/fairdatapoint/service/schema/MetadataSchemaValidator.java @@ -0,0 +1,129 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.service.schema; + +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaChangeDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaVersionDTO; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaDraftRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.ResourceDefinitionRepository; +import nl.dtls.fairdatapoint.entity.exception.ValidationException; +import nl.dtls.fairdatapoint.entity.resource.ResourceDefinition; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.util.RdfIOUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +@Component +public class MetadataSchemaValidator { + + @Autowired + private MetadataSchemaDraftRepository metadataSchemaDraftRepository; + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Autowired + private ResourceDefinitionRepository resourceDefinitionRepository; + + + private void validateShacl(String shaclDefinition) { + try { + RdfIOUtil.read(shaclDefinition, ""); + } catch (ValidationException e) { + throw new ValidationException("Unable to read SHACL definition"); + } + } + + public void validate(MetadataSchema metadataSchema) { + validate(metadataSchema, null); + } + + public void validate(MetadataSchema newVersion, MetadataSchema previousVersion) { + // Check previous + if (previousVersion != null) { + if (previousVersion.getVersion().compareTo(newVersion.getVersion()) >= 0) { + throw new ValidationException("Version is not higher than previous"); + } + if (previousVersion.isLatest()) { + throw new ValidationException("Older version is still marked as latest"); + } + } + // Check SHACL definition + validateShacl(newVersion.getDefinition()); + } + + public void validateNotUsed(String uuid) { + List resourceDefinitions = resourceDefinitionRepository.findByMetadataSchemaUuidsIsContaining(uuid); + if (!resourceDefinitions.isEmpty()) { + throw new ValidationException(format("Schema is used in %d resource definitions", resourceDefinitions.size())); + } + List children = metadataSchemaRepository.findAllByExtendSchemasContains(uuid); + if (!children.isEmpty()) { + throw new ValidationException(format("Schema is used in %d other schemas", children.size())); + } + } + + public void validateNoExtendsCycle(String uuid, List extendSchemaUuids) { + if (extendSchemaUuids.contains(uuid)) { + throw new ValidationException("Extends-cycle detected for the metadata schema"); + } + extendSchemaUuids.forEach(schemaUuid -> { + Optional oSchema = metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaUuid); + oSchema.ifPresent(schema -> validateNoExtendsCycle(uuid, schema.getExtendSchemas())); + }); + } + + public void validate(MetadataSchemaVersionDTO reqDto) { + // Check SHACL definition + validateShacl(reqDto.getDefinition()); + } + + public void validate(MetadataSchemaChangeDTO reqDto) { + // Check SHACL definition + validateShacl(reqDto.getDefinition()); + } + + private List getMissingSchemaUuids(List schemasUuids) { + Set existingUuids = metadataSchemaRepository + .findAllByLatestIsTrue() + .stream() + .map(MetadataSchema::getUuid) + .collect(Collectors.toSet()); + return schemasUuids.stream().filter(schemaUuid -> !existingUuids.contains(schemaUuid)).toList(); + } + + public void validateAllExist(List schemasUuids) { + List missing = getMissingSchemaUuids(schemasUuids); + if (!missing.isEmpty()) { + throw new ValidationException(format("Metadata schemas not found: %s", missing)); + } + } +} diff --git a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeMapper.java b/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeMapper.java deleted file mode 100644 index 7e068de5e..000000000 --- a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeMapper.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * The MIT License - * Copyright © 2017 DTL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package nl.dtls.fairdatapoint.service.shape; - -import nl.dtls.fairdatapoint.api.dto.shape.ShapeChangeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeRemoteDTO; -import nl.dtls.fairdatapoint.entity.shape.Shape; -import nl.dtls.fairdatapoint.entity.shape.ShapeType; -import org.springframework.stereotype.Service; - -@Service -public class ShapeMapper { - - public ShapeDTO toDTO(Shape shape) { - return - new ShapeDTO( - shape.getUuid(), - shape.getName(), - shape.isPublished(), - shape.getType(), - shape.getDefinition(), - shape.getTargetClasses().stream().sorted().toList() - ); - } - - public Shape fromChangeDTO(ShapeChangeDTO dto, String uuid) { - return - new Shape( - null, - uuid, - dto.getName(), - dto.isPublished(), - ShapeType.CUSTOM, - dto.getDefinition(), - ShapeShaclUtils.extractTargetClasses(dto.getDefinition()) - ); - - } - - public Shape fromChangeDTO(ShapeChangeDTO dto, Shape shape) { - return - shape - .toBuilder() - .name(dto.getName()) - .published(dto.isPublished()) - .definition(dto.getDefinition()) - .targetClasses(ShapeShaclUtils.extractTargetClasses(dto.getDefinition())) - .build(); - } - - public ShapeRemoteDTO toRemoteDTO(String fdpUrl, ShapeDTO shape) { - return - new ShapeRemoteDTO( - fdpUrl, - shape.getUuid(), - shape.getName(), - shape.getDefinition() - ); - } - - public ShapeChangeDTO fromRemoteDTO(ShapeRemoteDTO shape) { - return - new ShapeChangeDTO( - shape.getName(), - false, - shape.getDefinition() - ); - } -} diff --git a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeService.java b/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeService.java deleted file mode 100644 index 2d0d23534..000000000 --- a/src/main/java/nl/dtls/fairdatapoint/service/shape/ShapeService.java +++ /dev/null @@ -1,184 +0,0 @@ -/** - * The MIT License - * Copyright © 2017 DTL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package nl.dtls.fairdatapoint.service.shape; - -import nl.dtls.fairdatapoint.api.dto.shape.ShapeChangeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeRemoteDTO; -import nl.dtls.fairdatapoint.database.mongo.repository.ResourceDefinitionRepository; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; -import nl.dtls.fairdatapoint.entity.exception.ShapeImportException; -import nl.dtls.fairdatapoint.entity.exception.ValidationException; -import nl.dtls.fairdatapoint.entity.resource.ResourceDefinition; -import nl.dtls.fairdatapoint.entity.shape.Shape; -import nl.dtls.fairdatapoint.entity.shape.ShapeType; -import nl.dtls.fairdatapoint.service.resource.ResourceDefinitionTargetClassesCache; -import nl.dtls.fairdatapoint.util.RdfIOUtil; -import org.eclipse.rdf4j.model.Model; -import org.eclipse.rdf4j.model.impl.LinkedHashModel; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; - -import static java.lang.String.format; -import static java.util.Optional.empty; -import static java.util.Optional.of; -import static java.util.stream.Collectors.toList; - -@Service -public class ShapeService { - - @Autowired - private ShapeRepository shapeRepository; - - @Autowired - private ResourceDefinitionRepository resourceDefinitionRepository; - - @Autowired - private ShapeMapper shapeMapper; - - @Autowired - private ShapeValidator shapeValidator; - - @Autowired - private ResourceDefinitionTargetClassesCache targetClassesCache; - - public List getShapes() { - List shapes = shapeRepository.findAll(); - return - shapes - .stream() - .map(shapeMapper::toDTO) - .collect(toList()); - } - - public List getPublishedShapes() { - List shapes = shapeRepository.findAllByPublishedIsTrue(); - return - shapes - .stream() - .map(shapeMapper::toDTO) - .collect(toList()); - } - - public Optional getShapeByUuid(String uuid) { - return - shapeRepository - .findByUuid(uuid) - .map(shapeMapper::toDTO); - } - - public Optional getShapeContentByUuid(String uuid) { - return - shapeRepository - .findByUuid(uuid) - .map(shape -> RdfIOUtil.read(shape.getDefinition(), "")); - } - - @PreAuthorize("hasRole('ADMIN')") - public ShapeDTO createShape(ShapeChangeDTO reqDto) { - shapeValidator.validate(reqDto); - String uuid = UUID.randomUUID().toString(); - Shape shape = shapeMapper.fromChangeDTO(reqDto, uuid); - shapeRepository.save(shape); - targetClassesCache.computeCache(); - return shapeMapper.toDTO(shape); - } - - @PreAuthorize("hasRole('ADMIN')") - public Optional updateShape(String uuid, ShapeChangeDTO reqDto) { - shapeValidator.validate(reqDto); - Optional oShape = shapeRepository.findByUuid(uuid); - if (oShape.isEmpty()) { - return empty(); - } - Shape shape = oShape.get(); - Shape updatedShape = shapeMapper.fromChangeDTO(reqDto, shape); - shapeRepository.save(updatedShape); - targetClassesCache.computeCache(); - return of(shapeMapper.toDTO(updatedShape)); - } - - @PreAuthorize("hasRole('ADMIN')") - public boolean deleteShape(String uuid) { - Optional oShape = shapeRepository.findByUuid(uuid); - if (oShape.isEmpty()) { - return false; - } - Shape shape = oShape.get(); - - List resourceDefinitions = resourceDefinitionRepository.findByShapeUuidsIsContaining(shape.getUuid()); - if (!resourceDefinitions.isEmpty()) { - throw new ValidationException(format("Shape is used in %d resource definitions", resourceDefinitions.size())); - } - - if (shape.getType() == ShapeType.INTERNAL) { - throw new ValidationException("You can't delete INTERNAL Shape"); - } - shapeRepository.delete(shape); - targetClassesCache.computeCache(); - return true; - } - - public Model getShaclFromShapes() { - Model shacl = new LinkedHashModel(); - List shapes = shapeRepository.findAll(); - shapes.stream() - .map(s -> RdfIOUtil.read(s.getDefinition(), "")) - .forEach(m -> shacl.addAll(new ArrayList<>(m))); - return shacl; - } - - public List getRemoteShapes(String fdpUrl) { - List shapes = ShapeRetrievalUtils.retrievePublishedShapes(fdpUrl); - return shapes - .stream() - .map(s -> shapeMapper.toRemoteDTO(fdpUrl, s)) - .collect(Collectors.toList()); - } - - private ShapeDTO importShape(ShapeChangeDTO reqDto) { - shapeValidator.validate(reqDto); - String uuid = UUID.randomUUID().toString(); - Shape shape = shapeMapper.fromChangeDTO(reqDto, uuid); - shapeRepository.save(shape); - return shapeMapper.toDTO(shape); - } - - public List importShapes(List reqDtos) { - List result = - reqDtos - .stream() - .map(s -> shapeMapper.fromRemoteDTO(s)) - .map(this::importShape) - .collect(Collectors.toList()); - targetClassesCache.computeCache(); - return result; - } -} diff --git a/src/main/java/nl/dtls/fairdatapoint/service/user/CurrentUserService.java b/src/main/java/nl/dtls/fairdatapoint/service/user/CurrentUserService.java index 207b09cf7..4089b1248 100644 --- a/src/main/java/nl/dtls/fairdatapoint/service/user/CurrentUserService.java +++ b/src/main/java/nl/dtls/fairdatapoint/service/user/CurrentUserService.java @@ -51,6 +51,11 @@ public Optional getCurrentUserUuid() { return empty(); } + public boolean isAdmin() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + return auth != null && auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); + } + public Optional getCurrentUser() { return getCurrentUserUuid().flatMap(userRepository::findByUuid); } diff --git a/src/main/java/nl/dtls/fairdatapoint/util/KnownUUIDs.java b/src/main/java/nl/dtls/fairdatapoint/util/KnownUUIDs.java index 6d23c1845..aa83a78aa 100644 --- a/src/main/java/nl/dtls/fairdatapoint/util/KnownUUIDs.java +++ b/src/main/java/nl/dtls/fairdatapoint/util/KnownUUIDs.java @@ -36,21 +36,35 @@ public class KnownUUIDs { public static final String MEMBERSHIP_DATAPROVIDER_UUID = "87a2d984-7db2-43f6-805c-6b0040afead5"; - public static final String SHAPE_RESOURCE_UUID = "6a668323-3936-4b53-8380-a4fd2ed082ee"; + public static final String SCHEMA_RESOURCE_UUID = "6a668323-3936-4b53-8380-a4fd2ed082ee"; - public static final String SHAPE_REPOSITORY_UUID = "a92958ab-a414-47e6-8e17-68ba96ba3a2b"; + public static final String SCHEMA_REPOSITORY_UUID = "a92958ab-a414-47e6-8e17-68ba96ba3a2b"; - public static final String SHAPE_FDP_UUID = "a92958ab-a414-47e6-8e17-68ba96ba3a2b"; + public static final String SCHEMA_FDP_UUID = "a92958ab-a414-47e6-8e17-68ba96ba3a2b"; - public static final String SHAPE_DATASERVICE_UUID = "89d94c1b-f6ff-4545-ba9b-120b2d1921d0"; + public static final String SCHEMA_DATASERVICE_UUID = "89d94c1b-f6ff-4545-ba9b-120b2d1921d0"; - public static final String SHAPE_METADATASERVICE_UUID = "6f7a5a76-6185-4bd0-9fe9-62ecc90c9bad"; + public static final String SCHEMA_METADATASERVICE_UUID = "6f7a5a76-6185-4bd0-9fe9-62ecc90c9bad"; - public static final String SHAPE_CATALOG_UUID = "2aa7ba63-d27a-4c0e-bfa6-3a4e250f4660"; + public static final String SCHEMA_CATALOG_UUID = "2aa7ba63-d27a-4c0e-bfa6-3a4e250f4660"; - public static final String SHAPE_DATASET_UUID = "866d7fb8-5982-4215-9c7c-18d0ed1bd5f3"; + public static final String SCHEMA_DATASET_UUID = "866d7fb8-5982-4215-9c7c-18d0ed1bd5f3"; - public static final String SHAPE_DISTRIBUTION_UUID = "ebacbf83-cd4f-4113-8738-d73c0735b0ab"; + public static final String SCHEMA_DISTRIBUTION_UUID = "ebacbf83-cd4f-4113-8738-d73c0735b0ab"; + + public static final String SCHEMA_V1_RESOURCE_UUID = "71d77460-f919-4f72-b265-ed26567fe361"; + + public static final String SCHEMA_V1_FDP_UUID = "4e64208d-f102-45a0-96e3-17b002e6213e"; + + public static final String SCHEMA_V1_DATASERVICE_UUID = "9111d436-fe58-4bd5-97ae-e6f86bc2997a"; + + public static final String SCHEMA_V1_METADATASERVICE_UUID = "36b22b70-6203-4dd2-9fb6-b39a776bf467"; + + public static final String SCHEMA_V1_CATALOG_UUID = "c9640671-945d-4114-88fb-e81314cb7ab2"; + + public static final String SCHEMA_V1_DATASET_UUID = "9cc3c89a-76cf-4639-a71f-652627af51db"; + + public static final String SCHEMA_V1_DISTRIBUTION_UUID = "3cda8cd3-b08b-4797-822d-d3f3e83c466a"; public static final String RD_REPOSITORY_UUID = "77aaad6a-0136-4c6e-88b9-07ffccd0ee4c"; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7964fa1e7..349c32d22 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -59,7 +59,7 @@ metadataProperties: openapi: title: FAIR Data Point API - version: 1.13.2 + version: 1.14.0 description: "The reference implementation of the metadata registration service: A service implementing the API specification. It contains an authentication system to allow maintainers to define and update metadata. Read-only access to the data is public." contact: name: Luiz Bonino diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-catalog.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-catalog.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-catalog.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-catalog.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-custom-edited.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-custom-edited.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-custom-edited.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-custom-edited.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-custom.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-custom.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-custom.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-custom.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-data-service.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-data-service.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-data-service.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-data-service.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-dataset.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-dataset.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-dataset.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-dataset.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-distribution.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-distribution.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-distribution.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-distribution.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-fdp.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-fdp.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-fdp.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-fdp.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-metadata-service.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-metadata-service.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-metadata-service.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-metadata-service.ttl diff --git a/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-resource.ttl b/src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-resource.ttl similarity index 100% rename from src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/shape/data/shape-resource.ttl rename to src/main/resources/nl/dtls/fairdatapoint/database/mongo/migration/development/schema/data/shape-resource.ttl diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/common/NotFoundTest.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/common/NotFoundTest.java index 837ca955d..1e27345f4 100644 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/common/NotFoundTest.java +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/common/NotFoundTest.java @@ -51,6 +51,13 @@ public static void createNotFoundTest(TestRestTemplate client, RequestEntity assertThat(result.getStatusCode(), is(equalTo(HttpStatus.NOT_FOUND))); } + public static void createAdminNotFoundTestGet(TestRestTemplate client, URI url) { + createNotFoundTest( + client, + RequestEntity.get(url).header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN).build() + ); + } + public static void createUserNotFoundTestGet(TestRestTemplate client, URI url) { createNotFoundTest( client, diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Common.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Common.java new file mode 100644 index 000000000..48964c948 --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Common.java @@ -0,0 +1,71 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaChangeDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDraftDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaVersionDTO; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +public class Common { + + public static void compare(MetadataSchemaChangeDTO entity, MetadataSchemaDTO dto) { + assertThat(dto.getName(), is(equalTo(entity.getName()))); + assertThat(dto.getLatest().getDefinition(), is(equalTo(entity.getDefinition()))); + } + + public static void compare(MetadataSchemaChangeDTO entity, MetadataSchemaDraftDTO dto) { + assertThat(dto.getName(), is(equalTo(entity.getName()))); + assertThat(dto.getDefinition(), is(equalTo(entity.getDefinition()))); + } + + public static void compare(MetadataSchema entity, MetadataSchemaDTO dto) { + assertThat(dto.getUuid(), is(equalTo(entity.getUuid()))); + assertThat(dto.getName(), is(equalTo(entity.getName()))); + assertThat(dto.getLatest().getDefinition(), is(equalTo(entity.getDefinition()))); + } + + public static void compare(MetadataSchemaDraft entity, MetadataSchemaDTO dto) { + assertThat(dto.getUuid(), is(equalTo(entity.getUuid()))); + assertThat(dto.getName(), is(equalTo(entity.getName()))); + assertThat(dto.getDraft().getDefinition(), is(equalTo(entity.getDefinition()))); + } + + public static void compare(MetadataSchema entity, MetadataSchemaVersionDTO dto) { + assertThat(dto.getUuid(), is(equalTo(entity.getUuid()))); + assertThat(dto.getName(), is(equalTo(entity.getName()))); + assertThat(dto.getDefinition(), is(equalTo(entity.getDefinition()))); + } + + public static void compare(MetadataSchemaDraft entity, MetadataSchemaDraftDTO dto) { + assertThat(dto.getUuid(), is(equalTo(entity.getUuid()))); + assertThat(dto.getName(), is(equalTo(entity.getName()))); + assertThat(dto.getDefinition(), is(equalTo(entity.getDefinition()))); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/List_GET.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Content_GET.java similarity index 55% rename from src/test/java/nl/dtls/fairdatapoint/acceptance/shape/List_GET.java rename to src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Content_GET.java index 81df2a91e..04fcb972f 100644 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/List_GET.java +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Content_GET.java @@ -20,11 +20,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.acceptance.shape; +package nl.dtls.fairdatapoint.acceptance.schema; import nl.dtls.fairdatapoint.WebIntegrationTest; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -34,46 +35,23 @@ import org.springframework.http.ResponseEntity; import java.net.URI; -import java.util.List; +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createUserNotFoundTestGet; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; -@DisplayName("GET /shapes") -public class List_GET extends WebIntegrationTest { +@DisplayName("GET /metadata-schemas/:schemaUuid") +public class Content_GET extends WebIntegrationTest { - private URI url() { - return URI.create("/shapes"); + private URI url(String uuid) { + return URI.create(format("/metadata-schemas/%s", uuid)); } @Autowired - private ShapeFixtures shapeFixtures; + private MetadataSchemaFixtures metadataSchemaFixtures; - @Test - @DisplayName("HTTP 200") - public void res200() { - // GIVEN: - RequestEntity request = RequestEntity - .get(url()) - .build(); - ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { - }; - - // WHEN: - ResponseEntity> result = client.exchange(request, responseType); - - // THEN: - assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); - List body = result.getBody(); - assertThat(body.size(), is(equalTo(7))); - Common.compare(shapeFixtures.resourceShape(), body.get(0)); - Common.compare(shapeFixtures.fdpShape(), body.get(1)); - Common.compare(shapeFixtures.dataServiceShape(), body.get(2)); - Common.compare(shapeFixtures.metadataServiceShape(), body.get(3)); - Common.compare(shapeFixtures.catalogShape(), body.get(4)); - Common.compare(shapeFixtures.datasetShape(), body.get(5)); - Common.compare(shapeFixtures.distributionShape(), body.get(6)); - } + // TODO } diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_DELETE.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Detail_DELETE.java similarity index 75% rename from src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_DELETE.java rename to src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Detail_DELETE.java index 3fdf37754..3d25302c7 100644 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_DELETE.java +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Detail_DELETE.java @@ -20,13 +20,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.acceptance.shape; +package nl.dtls.fairdatapoint.acceptance.schema; import nl.dtls.fairdatapoint.WebIntegrationTest; import nl.dtls.fairdatapoint.api.dto.error.ErrorDTO; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; -import nl.dtls.fairdatapoint.entity.shape.Shape; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -45,27 +45,27 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; -@DisplayName("DELETE /shapes/:shapeUuid") +@DisplayName("DELETE /metadata-schemas/:schemaUuid") public class Detail_DELETE extends WebIntegrationTest { private URI url(String uuid) { - return URI.create(format("/shapes/%s", uuid)); + return URI.create(format("/metadata-schemas/%s", uuid)); } @Autowired - private ShapeFixtures shapeFixtures; + private MetadataSchemaFixtures metadataSchemaFixtures; @Autowired - private ShapeRepository shapeRepository; + private MetadataSchemaRepository metadataSchemaRepository; @Test @DisplayName("HTTP 204") public void res204() { // GIVEN: - Shape shape = shapeFixtures.customShape(); - shapeRepository.save(shape); + MetadataSchema metadataSchema = metadataSchemaFixtures.customSchema(); + metadataSchemaRepository.save(metadataSchema); RequestEntity request = RequestEntity - .delete(url(shape.getUuid())) + .delete(url(metadataSchema.getUuid())) .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) .build(); ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { @@ -82,9 +82,9 @@ public void res204() { @DisplayName("HTTP 400") public void res400_used() { // GIVEN: - Shape shape = shapeFixtures.datasetShape(); + MetadataSchema metadataSchema = metadataSchemaFixtures.datasetSchema(); RequestEntity request = RequestEntity - .delete(url(shape.getUuid())) + .delete(url(metadataSchema.getUuid())) .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) .build(); ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { @@ -98,12 +98,12 @@ public void res400_used() { } @Test - @DisplayName("HTTP 400: Delete INTERNAL shape") + @DisplayName("HTTP 400: Delete INTERNAL schema") public void res400_internal() { // GIVEN: - Shape shape = shapeFixtures.fdpShape(); + MetadataSchema metadataSchema = metadataSchemaFixtures.fdpSchema(); RequestEntity request = RequestEntity - .delete(url(shape.getUuid())) + .delete(url(metadataSchema.getUuid())) .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) .build(); ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { @@ -119,15 +119,15 @@ public void res400_internal() { @Test @DisplayName("HTTP 403: User is not authenticated") public void res403_notAuthenticated() { - Shape shape = shapeFixtures.datasetShape(); - createUserForbiddenTestDelete(client, url(shape.getUuid())); + MetadataSchema metadataSchema = metadataSchemaFixtures.datasetSchema(); + createUserForbiddenTestDelete(client, url(metadataSchema.getUuid())); } @Test @DisplayName("HTTP 403: User is not an admin") - public void res403_shape() { - Shape shape = shapeFixtures.datasetShape(); - createUserForbiddenTestDelete(client, url(shape.getUuid())); + public void res403_notAdmin() { + MetadataSchema metadataSchema = metadataSchemaFixtures.datasetSchema(); + createUserForbiddenTestDelete(client, url(metadataSchema.getUuid())); } @Test diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_GET.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Detail_GET.java similarity index 75% rename from src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_GET.java rename to src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Detail_GET.java index 83a243bfc..d6ec40da7 100644 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_GET.java +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Detail_GET.java @@ -20,12 +20,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.acceptance.shape; +package nl.dtls.fairdatapoint.acceptance.schema; import nl.dtls.fairdatapoint.WebIntegrationTest; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; -import nl.dtls.fairdatapoint.entity.shape.Shape; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -42,33 +42,33 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; -@DisplayName("GET /shapes/:shapeUuid") +@DisplayName("GET /metadata-schemas/:schemaUuid") public class Detail_GET extends WebIntegrationTest { private URI url(String uuid) { - return URI.create(format("/shapes/%s", uuid)); + return URI.create(format("/metadata-schemas/%s", uuid)); } @Autowired - private ShapeFixtures shapeFixtures; + private MetadataSchemaFixtures metadataSchemaFixtures; @Test @DisplayName("HTTP 200") public void res200() { // GIVEN: - Shape shape = shapeFixtures.fdpShape(); + MetadataSchema metadataSchema = metadataSchemaFixtures.fdpSchema(); RequestEntity request = RequestEntity - .get(url(shape.getUuid())) + .get(url(metadataSchema.getUuid())) .build(); - ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { }; // WHEN: - ResponseEntity result = client.exchange(request, responseType); + ResponseEntity result = client.exchange(request, responseType); // THEN: assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); - Common.compare(shape, result.getBody()); + Common.compare(metadataSchema, result.getBody()); } @Test diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_DELETE.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_DELETE.java new file mode 100644 index 000000000..d270faba5 --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_DELETE.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaDraftRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; + +import java.net.URI; +import java.util.UUID; + +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.*; +import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createAdminNotFoundTestDelete; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +@DisplayName("DELETE /metadata-schemas/:schemaUuid/draft") +public class Draft_DELETE extends WebIntegrationTest { + + private URI url(String uuid) { + return URI.create(format("/metadata-schemas/%s/draft", uuid)); + } + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Autowired + private MetadataSchemaDraftRepository metadataSchemaDraftRepository; + + @Test + @DisplayName("HTTP 200") + public void res200() { + // GIVEN: Prepare data + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaDraftRepository.save(draft); + + // AND: Prepare request + RequestEntity request = RequestEntity + .delete(url(draft.getUuid())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT))); + assertThat(metadataSchemaDraftRepository.findAll().isEmpty(), is(true)); + } + + @Test + @DisplayName("HTTP 404") + public void res404() { + createAdminNotFoundTestDelete(client, url("nonExisting")); + } + + @Test + @DisplayName("HTTP 403: User is not authenticated") + public void res403_notAuthenticated() { + createNoUserForbiddenTestDelete(client, url(UUID.randomUUID().toString())); + } + + @Test + @DisplayName("HTTP 403: User is not an admin") + public void res403_notAdmin() { + createUserForbiddenTestDelete(client, url(UUID.randomUUID().toString())); + } + +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_GET.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_GET.java new file mode 100644 index 000000000..1cc759c47 --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_GET.java @@ -0,0 +1,101 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDraftDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaDraftRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; + +import java.net.URI; +import java.util.UUID; + +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.*; +import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createAdminNotFoundTestGet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +@DisplayName("GET /metadata-schemas/:schemaUuid/draft") +public class Draft_GET extends WebIntegrationTest { + + private URI url(String uuid) { + return URI.create(format("/metadata-schemas/%s/draft", uuid)); + } + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Autowired + private MetadataSchemaDraftRepository metadataSchemaDraftRepository; + + @Test + @DisplayName("HTTP 200") + public void res200() { + // GIVEN: Prepare data + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaDraftRepository.save(draft); + + // AND: Prepare request + RequestEntity request = RequestEntity + .get(url(draft.getUuid())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + Common.compare(draft, result.getBody()); + } + + @Test + @DisplayName("HTTP 404") + public void res404() { + createAdminNotFoundTestGet(client, url("nonExisting")); + } + + @Test + @DisplayName("HTTP 403: User is not authenticated") + public void res403_notAuthenticated() { + createNoUserForbiddenTestGet(client, url(UUID.randomUUID().toString())); + } + + @Test + @DisplayName("HTTP 403: User is not an admin") + public void res403_notAdmin() { + createUserForbiddenTestGet(client, url(UUID.randomUUID().toString())); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_PUT.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_PUT.java new file mode 100644 index 000000000..499820239 --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Draft_PUT.java @@ -0,0 +1,246 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaChangeDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDraftDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaDraftRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; + +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createNoUserForbiddenTestPut; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createUserForbiddenTestPut; +import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createAdminNotFoundTestPut; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +@DisplayName("PUT /metadata-schemas/:schemaUuid/draft") +public class Draft_PUT extends WebIntegrationTest { + + private URI url(String uuid) { + return URI.create(format("/metadata-schemas/%s/draft", uuid)); + } + + private MetadataSchemaChangeDTO reqDto() { + return MetadataSchemaChangeDTO.builder() + .name("Updated schema draft") + .description("Description of changes") + .abstractSchema(false) + .definition("# no SHACL") + .extendsSchemaUuids(Collections.emptyList()) + .build(); + } + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Autowired + private MetadataSchemaDraftRepository metadataSchemaDraftRepository; + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Test + @DisplayName("HTTP 200") + public void res200() { + // GIVEN: Prepare data + metadataSchemaDraftRepository.deleteAll(); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.save(draft); + MetadataSchemaChangeDTO reqDto = reqDto(); + + // AND: Prepare request + RequestEntity request = RequestEntity + .put(url(draft.getUuid())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .body(reqDto); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat(result.getBody(), is(notNullValue())); + assertThat(result.getBody().getName(), is(equalTo(reqDto.getName()))); + assertThat(result.getBody().getDescription(), is(equalTo(reqDto.getDescription()))); + assertThat(result.getBody().getDefinition(), is(equalTo(reqDto.getDefinition()))); + assertThat(result.getBody().isAbstractSchema(), is(equalTo(reqDto.isAbstractSchema()))); + assertThat(result.getBody().getExtendsSchemaUuids(), is(equalTo(reqDto.getExtendsSchemaUuids()))); + } + + @Test + @DisplayName("HTTP 200: with extends") + public void res200_extends() { + // GIVEN: Prepare data + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.deleteAll(); + MetadataSchema parentSchema = metadataSchemaFixtures.resourceSchema(); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.save(draft); + metadataSchemaRepository.save(parentSchema); + MetadataSchemaChangeDTO reqDto = reqDto(); + reqDto.setExtendsSchemaUuids(List.of(parentSchema.getUuid())); + + // AND: Prepare request + RequestEntity request = RequestEntity + .put(url(draft.getUuid())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .body(reqDto); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat(result.getBody(), is(notNullValue())); + assertThat(result.getBody().getExtendsSchemaUuids(), is(equalTo(reqDto.getExtendsSchemaUuids()))); + } + + @Test + @DisplayName("HTTP 400: non-existing schema") + public void res400_nonExistingSchema() { + // GIVEN: Prepare data + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.deleteAll(); + MetadataSchema parentSchema = metadataSchemaFixtures.resourceSchema(); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.save(draft); + MetadataSchemaChangeDTO reqDto = reqDto(); + reqDto.setExtendsSchemaUuids(List.of(parentSchema.getUuid())); + + // AND: Prepare request + RequestEntity request = RequestEntity + .put(url(draft.getUuid())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .body(reqDto); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST))); + } + + @Test + @DisplayName("HTTP 400: simple schema loop") + public void res400_schemaLoopSimple() { + // GIVEN: Prepare data + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.deleteAll(); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.save(draft); + MetadataSchemaChangeDTO reqDto = reqDto(); + reqDto.setExtendsSchemaUuids(List.of(draft.getUuid())); + + // AND: Prepare request + RequestEntity request = RequestEntity + .put(url(draft.getUuid())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .body(reqDto); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST))); + } + + @Test + @DisplayName("HTTP 400: complex schema loop") + public void res400_schemaLoopComplex() { + // GIVEN: Prepare data + MetadataSchema resourceSchema = metadataSchemaFixtures.resourceSchema(); + MetadataSchema catalogSchema = metadataSchemaFixtures.catalogSchema(); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + catalogSchema.setExtendSchemas(List.of(resourceSchema.getUuid())); + resourceSchema.setExtendSchemas(List.of(draft.getUuid())); + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaDraftRepository.save(draft); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.save(resourceSchema); + metadataSchemaRepository.save(catalogSchema); + MetadataSchemaChangeDTO reqDto = reqDto(); + reqDto.setExtendsSchemaUuids(List.of(catalogSchema.getUuid())); + + // AND: Prepare request + RequestEntity request = RequestEntity + .put(url(draft.getUuid())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .body(reqDto); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST))); + } + + @Test + @DisplayName("HTTP 404") + public void res404() { + createAdminNotFoundTestPut(client, url("nonExisting"), reqDto()); + } + + @Test + @DisplayName("HTTP 403: User is not authenticated") + public void res403_notAuthenticated() { + createNoUserForbiddenTestPut(client, url(UUID.randomUUID().toString()), reqDto()); + } + + @Test + @DisplayName("HTTP 403: User is not an admin") + public void res403_notAdmin() { + createUserForbiddenTestPut(client, url(UUID.randomUUID().toString()), reqDto()); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Import_POST.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Import_POST.java new file mode 100644 index 000000000..23af7b311 --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Import_POST.java @@ -0,0 +1,297 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaRemoteDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaVersionDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; + +import java.net.URI; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@DisplayName("GET /metadata-schemas/import") +public class Import_POST extends WebIntegrationTest { + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + private final ParameterizedTypeReference> responseType = + new ParameterizedTypeReference<>() { + }; + + private URI url() { + return URI.create("/metadata-schemas/import"); + } + + private MetadataSchemaVersionDTO schemaPublicDTO1() { + String schemaUuid = UUID.randomUUID().toString(); + String versionUuid = UUID.randomUUID().toString(); + return MetadataSchemaVersionDTO.builder() + .uuid(schemaUuid) + .versionUuid(versionUuid) + .previousVersionUuid(null) + .importedFrom("http://example.com/remote-fdp") + .origin("http://example.com/remote-fdp") + .version("1.0.0") + .name(metadataSchemaFixtures.customSchema().getName()) + .description(metadataSchemaFixtures.customSchema().getDescription()) + .definition(metadataSchemaFixtures.customSchema().getDefinition()) + .abstractSchema(metadataSchemaFixtures.customSchema().isAbstractSchema()) + .extendsSchemaUuids(Collections.emptyList()) + .build(); + } + + private MetadataSchemaVersionDTO schemaPublicDTO2() { + String schemaUuid = UUID.randomUUID().toString(); + String versionUuid = UUID.randomUUID().toString(); + return MetadataSchemaVersionDTO.builder() + .uuid(schemaUuid) + .versionUuid(versionUuid) + .previousVersionUuid(null) + .importedFrom("http://example.com/remote-fdp") + .origin("http://example.com/other-remote-fdp") + .version("1.2.3") + .name(metadataSchemaFixtures.customSchema().getName()) + .description(metadataSchemaFixtures.customSchema().getDescription()) + .definition(metadataSchemaFixtures.customSchema().getDefinition()) + .abstractSchema(metadataSchemaFixtures.customSchema().isAbstractSchema()) + .extendsSchemaUuids(Collections.emptyList()) + .build(); + } + + @Test + @DisplayName("HTTP 200: empty import") + public void res200_emptyImport() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + List reqDTOs = Collections.emptyList(); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDTOs); + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat("Response body is not null", result.getBody(), is(notNullValue())); + assertThat("Result is an empty list", result.getBody().size(), is(equalTo(0))); + assertThat("Metadata schema repository is empty", metadataSchemaRepository.count(), is(equalTo(0L))); + } + + @Test + @DisplayName("HTTP 200: single import") + public void res200_singleImport() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchemaVersionDTO schema = schemaPublicDTO1(); + List reqDTOs = Collections.singletonList(schema); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDTOs); + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat("Response body is not null", result.getBody(), is(notNullValue())); + assertThat("Result contains one schema", result.getBody().size(), is(equalTo(1))); + assertThat("Schema is in the metadata schema repository", metadataSchemaRepository.count(), is(equalTo(1L))); + } + + @Test + @DisplayName("HTTP 200: multiple import") + public void res200_multipleImport() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchemaVersionDTO schema1 = schemaPublicDTO1(); + MetadataSchemaVersionDTO schema2 = schemaPublicDTO2(); + List reqDTOs = Arrays.asList(schema1, schema2); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDTOs); + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat("Response body is not null", result.getBody(), is(notNullValue())); + assertThat("Result contains one schema", result.getBody().size(), is(equalTo(2))); + assertThat("Schema is in the metadata schema repository", metadataSchemaRepository.count(), is(equalTo(2L))); + } + + @Test + @DisplayName("HTTP 200: import versions of the same schema") + public void res200_multipleVersions() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchemaVersionDTO schema1 = schemaPublicDTO1(); + MetadataSchemaVersionDTO schema2 = schemaPublicDTO2(); + schema2.setPreviousVersionUuid(schema1.getVersionUuid()); + schema2.setUuid(schema1.getUuid()); + List reqDTOs = Arrays.asList(schema1, schema2); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDTOs); + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat("Response body is not null", result.getBody(), is(notNullValue())); + assertThat("Result contains one schema", result.getBody().size(), is(equalTo(2))); + assertThat("Schema is in the metadata schema repository", metadataSchemaRepository.count(), is(equalTo(2L))); + } + + @Test + @DisplayName("HTTP 200: import with extends") + public void res200_withExtends() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchemaVersionDTO schema1 = schemaPublicDTO1(); + MetadataSchemaVersionDTO schema2 = schemaPublicDTO2(); + schema2.setExtendsSchemaUuids(Collections.singletonList(schema1.getUuid())); + List reqDTOs = Arrays.asList(schema1, schema2); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDTOs); + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat("Response body is not null", result.getBody(), is(notNullValue())); + assertThat("Result contains one schema", result.getBody().size(), is(equalTo(2))); + assertThat("Schema is in the metadata schema repository", metadataSchemaRepository.count(), is(equalTo(2L))); + } + + @Test + @DisplayName("HTTP 400: missing extends") + public void res400_missingExtends() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchemaVersionDTO schema1 = schemaPublicDTO1(); + schema1.setExtendsSchemaUuids(Collections.singletonList(UUID.randomUUID().toString())); + List reqDTOs = Collections.singletonList(schema1); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDTOs); + + // WHEN: + ResponseEntity result = client.exchange(request, new ParameterizedTypeReference<>() {}); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST))); + assertThat("Nothings changes in metadata schema repository", metadataSchemaRepository.count(), is(equalTo(0L))); + } + + @Test + @DisplayName("HTTP 403: no token") + public void res403_noToken() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchemaVersionDTO schema = schemaPublicDTO1(); + List reqDTOs = Collections.singletonList(schema); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .body(reqDTOs); + + // WHEN + ResponseEntity result = client.exchange(request, new ParameterizedTypeReference<>() {}); + + // THEN: + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN))); + assertThat("Metadata schema repository stays empty", metadataSchemaRepository.count(), is(equalTo(0L))); + } + + @Test + @DisplayName("HTTP 403: non-admin token") + public void res403_nonAdminToken() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchemaVersionDTO schema1 = schemaPublicDTO1(); + MetadataSchemaVersionDTO schema2 = schemaPublicDTO2(); + List reqDTOs = Arrays.asList(schema1, schema2); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url()) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ALBERT_TOKEN) + .body(reqDTOs); + + // WHEN + ResponseEntity result = client.exchange(request, new ParameterizedTypeReference<>() {}); + + // THEN: + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN))); + assertThat("Metadata schema repository stays empty", metadataSchemaRepository.count(), is(equalTo(0L))); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/List_GET.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/List_GET.java new file mode 100644 index 000000000..d51fd0d2d --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/List_GET.java @@ -0,0 +1,206 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaDraftRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; + +import java.net.URI; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +@DisplayName("GET /metadata-schemas") +public class List_GET extends WebIntegrationTest { + + private URI url(boolean includeDrafts, boolean includeAbstract) { + if (includeDrafts && !includeAbstract) { + return URI.create("/metadata-schemas?drafts=true&abstract=false"); + } + if (includeDrafts) { + return URI.create("/metadata-schemas?drafts=true"); + } + if (!includeAbstract) { + return URI.create("/metadata-schemas?abstract=false"); + } + return URI.create("/metadata-schemas"); + } + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Autowired + private MetadataSchemaDraftRepository metadataSchemaDraftRepository; + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Test + @DisplayName("HTTP 200: regular user") + public void res200_regularUser() { + // GIVEN: prepare data + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.insert(metadataSchemaFixtures.resourceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.fdpSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.dataServiceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.metadataServiceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.catalogSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.datasetSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.distributionSchema()); + + // AND: prepare request + RequestEntity request = RequestEntity + .get(url(false, true)) + .build(); + ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + List body = result.getBody(); + assertThat(body.size(), is(equalTo(7))); + Common.compare(metadataSchemaFixtures.resourceSchema(), body.get(0)); + Common.compare(metadataSchemaFixtures.fdpSchema(), body.get(1)); + Common.compare(metadataSchemaFixtures.dataServiceSchema(), body.get(2)); + Common.compare(metadataSchemaFixtures.metadataServiceSchema(), body.get(3)); + Common.compare(metadataSchemaFixtures.catalogSchema(), body.get(4)); + Common.compare(metadataSchemaFixtures.datasetSchema(), body.get(5)); + Common.compare(metadataSchemaFixtures.distributionSchema(), body.get(6)); + } + + @Test + @DisplayName("HTTP 200: regular user, no abstract") + public void res200_regularUserNoAbstract() { + // GIVEN: prepare data + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.insert(metadataSchemaFixtures.resourceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.fdpSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.dataServiceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.metadataServiceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.catalogSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.datasetSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.distributionSchema()); + + // AND: prepare request + RequestEntity request = RequestEntity + .get(url(false, false)) + .build(); + ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + List body = result.getBody(); + assertThat(body.size(), is(equalTo(6))); + Common.compare(metadataSchemaFixtures.fdpSchema(), body.get(0)); + Common.compare(metadataSchemaFixtures.dataServiceSchema(), body.get(1)); + Common.compare(metadataSchemaFixtures.metadataServiceSchema(), body.get(2)); + Common.compare(metadataSchemaFixtures.catalogSchema(), body.get(3)); + Common.compare(metadataSchemaFixtures.datasetSchema(), body.get(4)); + Common.compare(metadataSchemaFixtures.distributionSchema(), body.get(5)); + } + + @Test + @DisplayName("HTTP 200: admin with drafts") + public void res200_adminDrafts() { + // GIVEN: prepare data + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.insert(metadataSchemaFixtures.resourceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.fdpSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.dataServiceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.metadataServiceSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.catalogSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.datasetSchema()); + metadataSchemaRepository.insert(metadataSchemaFixtures.distributionSchema()); + metadataSchemaDraftRepository.insert(metadataSchemaFixtures.customSchemaDraft1()); + + // AND: prepare request + RequestEntity request = RequestEntity + .get(url(true, true)) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .build(); + ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity> result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + List body = result.getBody(); + assertThat(body.size(), is(equalTo(8))); + Common.compare(metadataSchemaFixtures.resourceSchema(), body.get(0)); + Common.compare(metadataSchemaFixtures.fdpSchema(), body.get(1)); + Common.compare(metadataSchemaFixtures.dataServiceSchema(), body.get(2)); + Common.compare(metadataSchemaFixtures.metadataServiceSchema(), body.get(3)); + Common.compare(metadataSchemaFixtures.catalogSchema(), body.get(4)); + Common.compare(metadataSchemaFixtures.datasetSchema(), body.get(5)); + Common.compare(metadataSchemaFixtures.distributionSchema(), body.get(6)); + Common.compare(metadataSchemaFixtures.customSchemaDraft1(), body.get(7)); + } + + + @Test + @DisplayName("HTTP 403: unauthorized for drafts") + public void res403_unauthorized() { + // GIVEN: prepare data + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.deleteAll(); + + // AND: prepare request + RequestEntity request = RequestEntity + .get(url(true, true)) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.UNAUTHORIZED))); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/List_POST.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/List_POST.java similarity index 55% rename from src/test/java/nl/dtls/fairdatapoint/acceptance/shape/List_POST.java rename to src/test/java/nl/dtls/fairdatapoint/acceptance/schema/List_POST.java index 4da914265..be3336f18 100644 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/List_POST.java +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/List_POST.java @@ -20,14 +20,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.acceptance.shape; +package nl.dtls.fairdatapoint.acceptance.schema; import nl.dtls.fairdatapoint.WebIntegrationTest; -import nl.dtls.fairdatapoint.api.dto.error.ErrorDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeChangeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; -import nl.dtls.fairdatapoint.entity.shape.Shape; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaChangeDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDraftDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -35,6 +34,7 @@ import org.springframework.http.*; import java.net.URI; +import java.util.Collections; import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createNoUserForbiddenTestPost; import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createUserForbiddenTestPost; @@ -42,79 +42,60 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; -@DisplayName("POST /shapes") +@DisplayName("POST /metadata-schemas") public class List_POST extends WebIntegrationTest { @Autowired - private ShapeFixtures shapeFixtures; + private MetadataSchemaFixtures metadataSchemaFixtures; private URI url() { - return URI.create("/shapes"); + return URI.create("/metadata-schemas"); } - private ShapeChangeDTO reqDto(Shape shape) { - return new ShapeChangeDTO(shape.getName(), false, shape.getDefinition()); + private MetadataSchemaChangeDTO reqDto(MetadataSchema metadataSchema) { + return MetadataSchemaChangeDTO.builder() + .name(metadataSchema.getName()) + .description("") + .abstractSchema(false) + .definition(metadataSchema.getDefinition()) + .extendsSchemaUuids(Collections.emptyList()) + .build(); } @Test @DisplayName("HTTP 200") public void res200() { // GIVEN: Prepare data - Shape shape = shapeFixtures.customShape(); - ShapeChangeDTO reqDto = reqDto(shape); + MetadataSchema metadataSchema = metadataSchemaFixtures.customSchema(); + MetadataSchemaChangeDTO reqDto = reqDto(metadataSchema); // AND: Prepare request - RequestEntity request = RequestEntity + RequestEntity request = RequestEntity .post(url()) .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) .accept(MediaType.APPLICATION_JSON) .body(reqDto); - ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { }; // WHEN: - ResponseEntity result = client.exchange(request, responseType); + ResponseEntity result = client.exchange(request, responseType); // THEN: assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); Common.compare(reqDto, result.getBody()); } - @Test - @DisplayName("HTTP 400: Invalid SHACL Definition") - public void res400_invalidShacl() { - // GIVEN: Prepare data - Shape shape = shapeFixtures.customShape(); - shape.setDefinition(shape.getDefinition() + "Some random text that will break the validity of SHACL"); - ShapeChangeDTO reqDto = reqDto(shape); - - // AND: Prepare request - RequestEntity request = RequestEntity - .post(url()) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .accept(MediaType.APPLICATION_JSON) - .body(reqDto); - ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { - }; - - // WHEN: - ResponseEntity result = client.exchange(request, responseType); - - // THEN: - assertThat(result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST))); - assertThat(result.getBody().getMessage(), is("Unable to read SHACL definition")); - } - @Test @DisplayName("HTTP 403: User is not authenticated") public void res403_notAuthenticated() { - createNoUserForbiddenTestPost(client, url(), reqDto(shapeFixtures.customShape())); + createNoUserForbiddenTestPost(client, url(), reqDto(metadataSchemaFixtures.customSchema())); } @Test @DisplayName("HTTP 403: User is not an admin") - public void res403_shape() { - createUserForbiddenTestPost(client, url(), reqDto(shapeFixtures.customShape())); + public void res403_notAdmin() { + createUserForbiddenTestPost(client, url(), reqDto(metadataSchemaFixtures.customSchema())); } } diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Public_GET.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Public_GET.java similarity index 60% rename from src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Public_GET.java rename to src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Public_GET.java index e61bc63e2..71f4c3710 100644 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Public_GET.java +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Public_GET.java @@ -20,13 +20,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package nl.dtls.fairdatapoint.acceptance.shape; +package nl.dtls.fairdatapoint.acceptance.schema; import nl.dtls.fairdatapoint.WebIntegrationTest; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; -import nl.dtls.fairdatapoint.entity.shape.Shape; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaRemoteDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaVersionDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.SemVer; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -46,29 +49,34 @@ import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNull.notNullValue; -@DisplayName("GET /shapes/public") +@DisplayName("GET /metadata-schemas/public") public class Public_GET extends WebIntegrationTest { @Autowired - private ShapeRepository shapeRepository; + private MetadataSchemaRepository metadataSchemaRepository; @Autowired - private ShapeFixtures shapeFixtures; + private MetadataSchemaFixtures metadataSchemaFixtures; - private final ParameterizedTypeReference> responseType = + private final ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { }; private URI url() { - return URI.create("/shapes/public"); + return URI.create("/metadata-schemas/public"); } - private Shape makeShape(Boolean published) { - return new Shape().toBuilder() + private MetadataSchema makeSchema(Boolean published) { + SemVer version = new SemVer("1.0.0"); + return MetadataSchema.builder() .uuid(UUID.randomUUID().toString()) - .name(shapeFixtures.customShape().getName()) - .definition(shapeFixtures.customShape().getDefinition()) + .version(version) + .versionString(version.toString()) + .versionUuid(UUID.randomUUID().toString()) + .name(metadataSchemaFixtures.customSchema().getName()) + .definition(metadataSchemaFixtures.customSchema().getDefinition()) .published(published) + .previousVersionUuid(null) .targetClasses(Set.of()) .build(); } @@ -77,9 +85,9 @@ private Shape makeShape(Boolean published) { @DisplayName("HTTP 200: no published") public void res200_noPublished() { // GIVEN: prepare data - shapeRepository.deleteAll(); - Shape shape = makeShape(false); - shapeRepository.insert(shape); + metadataSchemaRepository.deleteAll(); + MetadataSchema metadataSchema = makeSchema(false); + metadataSchemaRepository.insert(metadataSchema); // AND: prepare request RequestEntity request = RequestEntity @@ -88,7 +96,7 @@ public void res200_noPublished() { .build(); // WHEN: - ResponseEntity> result = client.exchange(request, responseType); + ResponseEntity> result = client.exchange(request, responseType); // THEN assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); @@ -100,11 +108,11 @@ public void res200_noPublished() { @DisplayName("HTTP 200: published") public void res200_published() { // GIVEN: prepare data - shapeRepository.deleteAll(); - Shape shapeNotPublished = makeShape(false); - Shape shapePublished = makeShape(true); - shapeRepository.insert(shapeNotPublished); - shapeRepository.insert(shapePublished); + metadataSchemaRepository.deleteAll(); + MetadataSchema metadataSchemaNotPublished = makeSchema(false); + MetadataSchema metadataSchemaPublished = makeSchema(true); + metadataSchemaRepository.insert(metadataSchemaNotPublished); + metadataSchemaRepository.insert(metadataSchemaPublished); // AND: prepare request RequestEntity request = RequestEntity @@ -113,12 +121,13 @@ public void res200_published() { .build(); // WHEN: - ResponseEntity> result = client.exchange(request, responseType); + ResponseEntity> result = client.exchange(request, responseType); // THEN assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); assertThat("Response body is not null", result.getBody(), is(notNullValue())); assertThat("Result is an empty list", result.getBody().size(), is(equalTo(1))); - assertThat("UUID matches the published shape", result.getBody().get(0).getUuid(), is(equalTo(shapePublished.getUuid()))); + assertThat("UUID matches the published schema", result.getBody().get(0).getUuid(), is(equalTo(metadataSchemaPublished.getUuid()))); + assertThat("UUID matches the published schema", result.getBody().get(0).getVersionUuid(), is(equalTo(metadataSchemaPublished.getVersionUuid()))); } } diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_DELETE.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_DELETE.java new file mode 100644 index 000000000..7c393e2fe --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_DELETE.java @@ -0,0 +1,161 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; + +import java.net.URI; +import java.util.UUID; + +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createNoUserForbiddenTestDelete; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createUserForbiddenTestDelete; +import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createAdminNotFoundTestDelete; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +@DisplayName("DELETE /metadata-schemas/:schemaUuid/versions/:version") +public class Version_DELETE extends WebIntegrationTest { + + private URI url(String uuid, String version) { + return URI.create(format("/metadata-schemas/%s/versions/%s", uuid, version)); + } + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Test + @DisplayName("HTTP 200: delete single") + public void res200_deleteSingle() { + // GIVEN: Prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchema schema = metadataSchemaFixtures.customSchema(); + metadataSchemaRepository.save(schema); + + // AND: Prepare request + RequestEntity request = RequestEntity + .delete(url(schema.getUuid(), schema.getVersionString())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT))); + assertThat(metadataSchemaRepository.findAll().isEmpty(), is(true)); + } + + @Test + @DisplayName("HTTP 200: delete latest") + public void res200_deleteLatest() { + // GIVEN: Prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchema schemaV1 = metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v1(false)); + MetadataSchema schemaV2 = metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v2(schemaV1, false)); + MetadataSchema schemaV3 = metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v3(schemaV2, true)); + + // AND: Prepare request + RequestEntity request = RequestEntity + .delete(url(schemaV3.getUuid(), schemaV3.getVersionString())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT))); + assertThat(metadataSchemaRepository.findAll().isEmpty(), is(false)); + assertThat(metadataSchemaRepository.findAll().size(), is(equalTo(2))); + assertThat(metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaV3.getUuid()).isPresent(), is(true)); + assertThat(metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaV3.getUuid()).get().getVersion(), is(equalTo(schemaV2.getVersion()))); + assertThat(metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaV3.getUuid()).get().isLatest(), is(true)); + } + + @Test + @DisplayName("HTTP 200: delete non-latest") + public void res200_deleteNonLatest() { + // GIVEN: Prepare data + metadataSchemaRepository.deleteAll(); + MetadataSchema schemaV1 = metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v1(false)); + MetadataSchema schemaV2 = metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v2(schemaV1, false)); + MetadataSchema schemaV3 = metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v3(schemaV2, true)); + + // AND: Prepare request + RequestEntity request = RequestEntity + .delete(url(schemaV2.getUuid(), schemaV2.getVersionString())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT))); + assertThat(metadataSchemaRepository.findAll().isEmpty(), is(false)); + assertThat(metadataSchemaRepository.findAll().size(), is(equalTo(2))); + assertThat(metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaV3.getUuid()).isPresent(), is(true)); + assertThat(metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaV3.getUuid()).get().getVersion(), is(equalTo(schemaV3.getVersion()))); + assertThat(metadataSchemaRepository.findByUuidAndLatestIsTrue(schemaV3.getUuid()).get().isLatest(), is(true)); + } + + @Test + @DisplayName("HTTP 404") + public void res404() { + createAdminNotFoundTestDelete(client, url("nonExisting", "1.0.0")); + } + + @Test + @DisplayName("HTTP 403: User is not authenticated") + public void res403_notAuthenticated() { + createNoUserForbiddenTestDelete(client, url(UUID.randomUUID().toString(), "1.0.0")); + } + + @Test + @DisplayName("HTTP 403: User is not an admin") + public void res403_notAdmin() { + createUserForbiddenTestDelete(client, url(UUID.randomUUID().toString(), "1.0.0")); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_GET.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_GET.java new file mode 100644 index 000000000..d5dd693b8 --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_GET.java @@ -0,0 +1,131 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaVersionDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; + +import java.net.URI; +import java.util.UUID; + +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createNoUserForbiddenTestGet; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createUserForbiddenTestGet; +import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createAdminNotFoundTestGet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +@DisplayName("GET /metadata-schemas/:schemaUuid/versions/:version") +public class Version_GET extends WebIntegrationTest { + + private URI url(String uuid, String version) { + return URI.create(format("/metadata-schemas/%s/versions/%s", uuid, version)); + } + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Test + @DisplayName("HTTP 200") + public void res200() { + // GIVEN: Prepare data + MetadataSchema schema = metadataSchemaFixtures.resourceSchema(); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.save(schema); + + // AND: Prepare request + RequestEntity request = RequestEntity + .get(url(schema.getUuid(), schema.getVersionString())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + Common.compare(schema, result.getBody()); + } + + @Test + @DisplayName("HTTP 200") + public void res200_latest() { + // GIVEN: Prepare data + MetadataSchema schema = metadataSchemaFixtures.resourceSchema(); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.save(schema); + + // AND: Prepare request + RequestEntity request = RequestEntity + .get(url(schema.getUuid(), "latest")) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .build(); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + Common.compare(schema, result.getBody()); + } + + @Test + @DisplayName("HTTP 404") + public void res404() { + MetadataSchema schema = metadataSchemaFixtures.resourceSchema(); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.save(schema); + createAdminNotFoundTestGet(client, url("nonExisting", "1.0.0")); + createAdminNotFoundTestGet(client, url(schema.getUuid(), "1.0.1")); + } + + @Test + @DisplayName("HTTP 403: User is not authenticated") + public void res403_notAuthenticated() { + createNoUserForbiddenTestGet(client, url(UUID.randomUUID().toString(), "1.0.0")); + } + + @Test + @DisplayName("HTTP 403: User is not an admin") + public void res403_notAdmin() { + createUserForbiddenTestGet(client, url(UUID.randomUUID().toString(), "1.0.0")); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_PUT.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_PUT.java new file mode 100644 index 000000000..1b065ee4c --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Version_PUT.java @@ -0,0 +1,117 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaUpdateDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaVersionDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; + +import java.net.URI; +import java.util.UUID; + +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createNoUserForbiddenTestPut; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createUserForbiddenTestPut; +import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createAdminNotFoundTestPut; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +@DisplayName("PUT /metadata-schemas/:schemaUuid/versions/:version") +public class Version_PUT extends WebIntegrationTest { + + private URI url(String uuid, String version) { + return URI.create(format("/metadata-schemas/%s/versions/%s", uuid, version)); + } + + private MetadataSchemaUpdateDTO reqDto() { + return new MetadataSchemaUpdateDTO( + "Fixed name", + "Fixed description", + true + ); + } + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Test + @DisplayName("HTTP 200") + public void res200() { + // GIVEN: Prepare data + MetadataSchema schema = metadataSchemaFixtures.customSchema(); + metadataSchemaRepository.deleteAll(); + metadataSchemaRepository.save(schema); + MetadataSchemaUpdateDTO reqDto = reqDto(); + + // AND: Prepare request + RequestEntity request = RequestEntity + .put(url(schema.getUuid(), schema.getVersionString())) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .accept(MediaType.APPLICATION_JSON) + .body(reqDto); + ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { + }; + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN: + assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat(result.getBody().getName(), is(equalTo(reqDto.getName()))); + assertThat(result.getBody().getDescription(), is(equalTo(reqDto.getDescription()))); + assertThat(result.getBody().isPublished(), is(equalTo(reqDto.isPublished()))); + assertThat(result.getBody().getDefinition(), is(equalTo(schema.getDefinition()))); + assertThat(result.getBody().isAbstractSchema(), is(equalTo(schema.isAbstractSchema()))); + } + + + @Test + @DisplayName("HTTP 404") + public void res404() { + createAdminNotFoundTestPut(client, url("nonExisting", "1.0.0"), reqDto()); + } + + @Test + @DisplayName("HTTP 403: User is not authenticated") + public void res403_notAuthenticated() { + createNoUserForbiddenTestPut(client, url(UUID.randomUUID().toString(), "1.0.0"), reqDto()); + } + + @Test + @DisplayName("HTTP 403: User is not an admin") + public void res403_notAdmin() { + createUserForbiddenTestPut(client, url(UUID.randomUUID().toString(), "1.0.0"), reqDto()); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Versions_POST.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Versions_POST.java new file mode 100644 index 000000000..ecebdbfb6 --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/schema/Versions_POST.java @@ -0,0 +1,183 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.acceptance.schema; + +import nl.dtls.fairdatapoint.WebIntegrationTest; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaDTO; +import nl.dtls.fairdatapoint.api.dto.schema.MetadataSchemaReleaseDTO; +import nl.dtls.fairdatapoint.database.mongo.migration.development.schema.data.MetadataSchemaFixtures; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaDraftRepository; +import nl.dtls.fairdatapoint.database.mongo.repository.MetadataSchemaRepository; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchema; +import nl.dtls.fairdatapoint.entity.schema.MetadataSchemaDraft; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; + +import java.net.URI; +import java.util.UUID; + +import static java.lang.String.format; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createNoUserForbiddenTestPost; +import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createUserForbiddenTestPost; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsNull.notNullValue; + +@DisplayName("POST /metadata-schemas/:schemaUuid/versions") +public class Versions_POST extends WebIntegrationTest { + + private URI url(String uuid) { + return URI.create(format("/metadata-schemas/%s/versions", uuid)); + } + + private MetadataSchemaReleaseDTO reqDto(String version, boolean published) { + return new MetadataSchemaReleaseDTO( + version, + "Some description", + published + ); + } + + @Autowired + private MetadataSchemaFixtures metadataSchemaFixtures; + + @Autowired + private MetadataSchemaRepository metadataSchemaRepository; + + @Autowired + private MetadataSchemaDraftRepository metadataSchemaDraftRepository; + + private final ParameterizedTypeReference responseType = + new ParameterizedTypeReference<>() { + }; + + @Test + @DisplayName("HTTP 200: publish new") + public void res200_publishNew() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + metadataSchemaDraftRepository.deleteAll(); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.save(draft); + MetadataSchemaReleaseDTO reqDto = reqDto("0.1.0", true); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url(draft.getUuid())) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDto); + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat("Response body is not null", result.getBody(), is(notNullValue())); + assertThat("Result is latest", result.getBody().getLatest().isLatest(), is(equalTo(true))); + assertThat("Result is published", result.getBody().getLatest().isPublished(), is(equalTo(true))); + assertThat("Result has correct name", result.getBody().getName(), is(equalTo(draft.getName()))); + assertThat("Result has correct version", result.getBody().getLatest().getVersion(), is(equalTo(reqDto.getVersion()))); + assertThat("Metadata schema repository has one schema", metadataSchemaRepository.count(), is(equalTo(1L))); + } + + @Test + @DisplayName("HTTP 200: publish newer version") + public void res200_publishNewerVersion() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + metadataSchemaDraftRepository.deleteAll(); + MetadataSchema schemaV1 = metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v1(true)); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.save(draft); + MetadataSchemaReleaseDTO reqDto = reqDto("2.0.0", true); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url(draft.getUuid())) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDto); + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat("Response body is not null", result.getBody(), is(notNullValue())); + assertThat("Result is latest", result.getBody().getLatest().isLatest(), is(equalTo(true))); + assertThat("Result is published", result.getBody().getLatest().isPublished(), is(equalTo(true))); + assertThat("Result has correct name", result.getBody().getName(), is(equalTo(draft.getName()))); + assertThat("Result has correct version", result.getBody().getLatest().getVersion(), is(equalTo(reqDto.getVersion()))); + assertThat("Metadata schema repository has 2 schemas", metadataSchemaRepository.count(), is(equalTo(2L))); + assertThat("Older version is still stored", metadataSchemaRepository.findByUuidAndVersionString(schemaV1.getUuid(), schemaV1.getVersionString()).isPresent(), is(true)); + assertThat("Older version is no longer the latest", metadataSchemaRepository.findByUuidAndVersionString(schemaV1.getUuid(), schemaV1.getVersionString()).get().isLatest(), is(false)); + } + + @Test + @DisplayName("HTTP 400: not newer version") + public void res400_notNewerVersion() { + // GIVEN: prepare data + metadataSchemaRepository.deleteAll(); + metadataSchemaDraftRepository.deleteAll(); + metadataSchemaRepository.save(metadataSchemaFixtures.customSchema_v1(true)); + MetadataSchemaDraft draft = metadataSchemaFixtures.customSchemaDraft1(); + metadataSchemaDraftRepository.save(draft); + MetadataSchemaReleaseDTO reqDto = reqDto("0.1.0", true); + + // AND: prepare request + RequestEntity request = RequestEntity + .post(url(draft.getUuid())) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) + .body(reqDto); + + // WHEN: + ResponseEntity result = client.exchange(request, responseType); + + // THEN + assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST))); + } + + @Test + @DisplayName("HTTP 404") + public void res404() { + createNoUserForbiddenTestPost(client, url("nonExisting"), reqDto("1.0.0", true)); + } + + @Test + @DisplayName("HTTP 403: User is not authenticated") + public void res403_notAuthenticated() { + createNoUserForbiddenTestPost(client, url(UUID.randomUUID().toString()), reqDto("1.0.0", true)); + } + + @Test + @DisplayName("HTTP 403: User is not an admin") + public void res403_notAdmin() { + createUserForbiddenTestPost(client, url(UUID.randomUUID().toString()), reqDto("1.0.0", true)); + } +} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Common.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Common.java deleted file mode 100644 index 2564c7b5e..000000000 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Common.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * The MIT License - * Copyright © 2017 DTL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package nl.dtls.fairdatapoint.acceptance.shape; - -import nl.dtls.fairdatapoint.api.dto.shape.ShapeChangeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.entity.shape.Shape; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsEqual.equalTo; - -public class Common { - - public static void compare(ShapeChangeDTO entity, ShapeDTO dto) { - assertThat(dto.getName(), is(equalTo(entity.getName()))); - assertThat(dto.getDefinition(), is(equalTo(entity.getDefinition()))); - } - - public static void compare(Shape entity, ShapeDTO dto) { - assertThat(dto.getUuid(), is(equalTo(entity.getUuid()))); - assertThat(dto.getName(), is(equalTo(entity.getName()))); - assertThat(dto.getDefinition(), is(equalTo(entity.getDefinition()))); - } -} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_PUT.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_PUT.java deleted file mode 100644 index c6676b592..000000000 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Detail_PUT.java +++ /dev/null @@ -1,190 +0,0 @@ -/** - * The MIT License - * Copyright © 2017 DTL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package nl.dtls.fairdatapoint.acceptance.shape; - -import nl.dtls.fairdatapoint.WebIntegrationTest; -import nl.dtls.fairdatapoint.api.dto.error.ErrorDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeChangeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; -import nl.dtls.fairdatapoint.entity.shape.Shape; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; - -import java.net.URI; - -import static java.lang.String.format; -import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createNoUserForbiddenTestPut; -import static nl.dtls.fairdatapoint.acceptance.common.ForbiddenTest.createUserForbiddenTestPut; -import static nl.dtls.fairdatapoint.acceptance.common.NotFoundTest.createAdminNotFoundTestPut; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsEqual.equalTo; - -@DisplayName("PUT /shapes/:shapeUuid") -public class Detail_PUT extends WebIntegrationTest { - - private URI url(String uuid) { - return URI.create(format("/shapes/%s", uuid)); - } - - private ShapeChangeDTO reqDto(Shape shape) { - return new ShapeChangeDTO(format("EDITED: %s", shape.getName()), false, shape.getDefinition()); - } - - @Autowired - private ShapeFixtures shapeFixtures; - - @Autowired - private ShapeRepository shapeRepository; - - @Test - @DisplayName("HTTP 200") - public void res200() { - // GIVEN: Prepare data - Shape shape = shapeFixtures.customShapeEdited(); - ShapeChangeDTO reqDto = reqDto(shape); - - // AND: Prepare request - RequestEntity request = RequestEntity - .put(url(shape.getUuid())) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .accept(MediaType.APPLICATION_JSON) - .body(reqDto); - ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { - }; - - // AND: Prepare DB - shapeRepository.save(shape); - - // WHEN: - ResponseEntity result = client.exchange(request, responseType); - - // THEN: - assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); - Common.compare(reqDto, result.getBody()); - } - - @Test - @DisplayName("HTTP 200: Published") - public void res200_published() { - // GIVEN: Prepare data - Shape shape = shapeFixtures.customShapeEdited(); - ShapeChangeDTO reqDto = reqDto(shape); - reqDto.setPublished(true); - - // AND: Prepare request - RequestEntity request = RequestEntity - .put(url(shape.getUuid())) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .accept(MediaType.APPLICATION_JSON) - .body(reqDto); - ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { - }; - - // AND: Prepare DB - shapeRepository.save(shape); - - // WHEN: - ResponseEntity result = client.exchange(request, responseType); - - // THEN: - assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); - Common.compare(reqDto, result.getBody()); - } - - @Test - @DisplayName("HTTP 400: Invalid SHACL Definition") - public void res400_invalidShacl() { - // GIVEN: Prepare data - Shape shape = shapeFixtures.customShapeEdited(); - shape.setDefinition(shape.getDefinition() + "Some random text that will break the validity of SHACL"); - ShapeChangeDTO reqDto = reqDto(shape); - - // AND: Prepare request - RequestEntity request = RequestEntity - .put(url(shape.getUuid())) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .accept(MediaType.APPLICATION_JSON) - .body(reqDto); - ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { - }; - - // AND: Prepare DB - shapeRepository.save(shapeFixtures.customShape()); - - // WHEN: - ResponseEntity result = client.exchange(request, responseType); - - // THEN: - assertThat(result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST))); - assertThat(result.getBody().getMessage(), is("Unable to read SHACL definition")); - } - - @Test - @DisplayName("HTTP 200: Edit INTERNAL shape") - public void res200_internal() { - // GIVEN: - Shape shape = shapeFixtures.fdpShape(); - ShapeChangeDTO reqDto = reqDto(shape); - RequestEntity request = RequestEntity - .put(url(shape.getUuid())) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .accept(MediaType.APPLICATION_JSON) - .body(reqDto); - ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() { - }; - - // WHEN: - ResponseEntity result = client.exchange(request, responseType); - - // THEN: - assertThat(result.getStatusCode(), is(equalTo(HttpStatus.OK))); - Common.compare(reqDto, result.getBody()); - } - - @Test - @DisplayName("HTTP 403: User is not authenticated") - public void res403_notAuthenticated() { - Shape shape = shapeFixtures.fdpShape(); - createNoUserForbiddenTestPut(client, url(shape.getUuid()), reqDto(shape)); - } - - @Test - @DisplayName("HTTP 403: User is not an admin") - public void res403_shape() { - Shape shape = shapeFixtures.fdpShape(); - createUserForbiddenTestPut(client, url(shape.getUuid()), reqDto(shape)); - } - - @Test - @DisplayName("HTTP 404") - public void res404() { - createAdminNotFoundTestPut(client, url("nonExisting"), reqDto(shapeFixtures.customShape())); - } - -} diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Import_POST.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Import_POST.java deleted file mode 100644 index b5998516e..000000000 --- a/src/test/java/nl/dtls/fairdatapoint/acceptance/shape/Import_POST.java +++ /dev/null @@ -1,202 +0,0 @@ -/** - * The MIT License - * Copyright © 2017 DTL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package nl.dtls.fairdatapoint.acceptance.shape; - -import nl.dtls.fairdatapoint.WebIntegrationTest; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeDTO; -import nl.dtls.fairdatapoint.api.dto.shape.ShapeRemoteDTO; -import nl.dtls.fairdatapoint.database.mongo.migration.development.shape.data.ShapeFixtures; -import nl.dtls.fairdatapoint.database.mongo.repository.ShapeRepository; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; - -import java.net.URI; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.hamcrest.core.IsNull.notNullValue; - -@DisplayName("GET /shapes/import") -public class Import_POST extends WebIntegrationTest { - - @Autowired - private ShapeRepository shapeRepository; - - @Autowired - private ShapeFixtures shapeFixtures; - - private final ParameterizedTypeReference> responseType = - new ParameterizedTypeReference<>() { - }; - - private URI url() { - return URI.create("/shapes/import"); - } - - private ShapeRemoteDTO shapeRemoteDTO1() { - return new ShapeRemoteDTO( - "http://example.com", - UUID.randomUUID().toString(), - shapeFixtures.customShape().getName(), - shapeFixtures.customShape().getDefinition() - ); - } - - private ShapeRemoteDTO shapeRemoteDTO2() { - return new ShapeRemoteDTO( - "http://example.com", - UUID.randomUUID().toString(), - shapeFixtures.customShapeEdited().getName(), - shapeFixtures.customShapeEdited().getDefinition() - ); - } - - @Test - @DisplayName("HTTP 200: empty import") - public void res200_emptyImport() { - // GIVEN: prepare data - shapeRepository.deleteAll(); - List reqDTOs = Collections.emptyList(); - - // AND: prepare request - RequestEntity request = RequestEntity - .post(url()) - .accept(MediaType.APPLICATION_JSON) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .body(reqDTOs); - - // WHEN: - ResponseEntity> result = client.exchange(request, responseType); - - // THEN - assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); - assertThat("Response body is not null", result.getBody(), is(notNullValue())); - assertThat("Result is an empty list", result.getBody().size(), is(equalTo(0))); - assertThat("Shape repository is empty", shapeRepository.count(), is(equalTo(0L))); - } - - @Test - @DisplayName("HTTP 200: single import") - public void res200_singleImport() { - // GIVEN: prepare data - shapeRepository.deleteAll(); - ShapeRemoteDTO shape = shapeRemoteDTO1(); - List reqDTOs = Collections.singletonList(shape); - - // AND: prepare request - RequestEntity request = RequestEntity - .post(url()) - .accept(MediaType.APPLICATION_JSON) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .body(reqDTOs); - - // WHEN: - ResponseEntity> result = client.exchange(request, responseType); - - // THEN - assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); - assertThat("Response body is not null", result.getBody(), is(notNullValue())); - assertThat("Result contains one shape", result.getBody().size(), is(equalTo(1))); - assertThat("Shape is in the shape repository", shapeRepository.count(), is(equalTo(1L))); - } - - @Test - @DisplayName("HTTP 200: multiple import") - public void res200_multipleImport() { - // GIVEN: prepare data - shapeRepository.deleteAll(); - ShapeRemoteDTO shape1 = shapeRemoteDTO1(); - ShapeRemoteDTO shape2 = shapeRemoteDTO2(); - List reqDTOs = Arrays.asList(shape1, shape2); - - // AND: prepare request - RequestEntity request = RequestEntity - .post(url()) - .accept(MediaType.APPLICATION_JSON) - .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN) - .body(reqDTOs); - - // WHEN: - ResponseEntity> result = client.exchange(request, responseType); - - // THEN - assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK))); - assertThat("Response body is not null", result.getBody(), is(notNullValue())); - assertThat("Result contains one shape", result.getBody().size(), is(equalTo(2))); - assertThat("Shape is in the shape repository", shapeRepository.count(), is(equalTo(2L))); - } - - @Test - @DisplayName("HTTP 403: no token") - public void res403_noToken() { - // GIVEN: prepare data - shapeRepository.deleteAll(); - ShapeRemoteDTO shape = shapeRemoteDTO1(); - List reqDTOs = Collections.singletonList(shape); - - // AND: prepare request - RequestEntity request = RequestEntity - .post(url()) - .accept(MediaType.APPLICATION_JSON) - .body(reqDTOs); - - // WHEN - ResponseEntity result = client.exchange(request, new ParameterizedTypeReference<>() {}); - - // THEN: - assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN))); - assertThat("Shape repository stays empty", shapeRepository.count(), is(equalTo(0L))); - } - - @Test - @DisplayName("HTTP 403: non-admin token") - public void res403_nonAdminToken() { - // GIVEN: prepare data - shapeRepository.deleteAll(); - ShapeRemoteDTO shape1 = shapeRemoteDTO1(); - ShapeRemoteDTO shape2 = shapeRemoteDTO2(); - List reqDTOs = Arrays.asList(shape1, shape2); - - // AND: prepare request - RequestEntity request = RequestEntity - .post(url()) - .accept(MediaType.APPLICATION_JSON) - .header(HttpHeaders.AUTHORIZATION, ALBERT_TOKEN) - .body(reqDTOs); - - // WHEN - ResponseEntity result = client.exchange(request, new ParameterizedTypeReference<>() {}); - - // THEN: - assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN))); - assertThat("Shape repository stays empty", shapeRepository.count(), is(equalTo(0L))); - } -} diff --git a/src/test/java/nl/dtls/fairdatapoint/database/rdf/repository/common/MetadataRepositoryTest.java b/src/test/java/nl/dtls/fairdatapoint/database/rdf/repository/common/MetadataRepositoryTest.java index e7a4c938a..751910bd4 100644 --- a/src/test/java/nl/dtls/fairdatapoint/database/rdf/repository/common/MetadataRepositoryTest.java +++ b/src/test/java/nl/dtls/fairdatapoint/database/rdf/repository/common/MetadataRepositoryTest.java @@ -58,13 +58,14 @@ public class MetadataRepositoryTest extends WebIntegrationTest { @Test public void findWorks() throws Exception { // GIVEN: + Model metadata = testMetadataFixtures.catalog1(); IRI context = getUri(testMetadataFixtures.catalog1()); // WHEN: List result = metadataRepository.find(context); // THEN: - assertThat(result.size(), is(equalTo(30))); + assertThat(result.size(), is(equalTo(metadata.size()))); } @Test @@ -112,7 +113,7 @@ public void removeWorks() throws Exception { IRI context = getUri(metadata); // AND: Check existence before delete - assertThat(metadataRepository.find(context).size(), is(equalTo(30))); + assertThat(metadataRepository.find(context).size(), is(equalTo(metadata.size()))); // WHEN: metadataRepository.remove(context); @@ -128,13 +129,13 @@ public void removeStatementWorks() throws Exception { IRI context = getUri(metadata); // AND: Check existence before delete - assertThat(metadataRepository.find(context).size(), is(equalTo(30))); + assertThat(metadataRepository.find(context).size(), is(equalTo(metadata.size()))); // WHEN: metadataRepository.removeStatement(getUri(metadata), DCTERMS.LANGUAGE, getLanguage(metadata), context); // THEN: - assertThat(metadataRepository.find(context).size(), is(equalTo(29))); + assertThat(metadataRepository.find(context).size(), is(equalTo(metadata.size() - 1))); } } diff --git a/src/test/java/nl/dtls/fairdatapoint/entity/schema/SemVerTest.java b/src/test/java/nl/dtls/fairdatapoint/entity/schema/SemVerTest.java new file mode 100644 index 000000000..81fd9856d --- /dev/null +++ b/src/test/java/nl/dtls/fairdatapoint/entity/schema/SemVerTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright © 2017 DTL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package nl.dtls.fairdatapoint.entity.schema; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +public class SemVerTest { + + @Test + public void testSemVerString() { + assertThat(new SemVer("1.2.3").toString(), is(equalTo("1.2.3"))); + assertThat(new SemVer("1.20.0").toString(), is(equalTo("1.20.0"))); + assertThat(new SemVer("0.1.0").toString(), is(equalTo("0.1.0"))); + } + + @Test + public void testCompareBasic() { + assertThat((new SemVer("1.2.3")).compareTo(new SemVer("1.2.3")), is(equalTo(0))); + assertThat((new SemVer("1.0.0")).compareTo(new SemVer("1.0.5")), is(equalTo(-1))); + assertThat((new SemVer("1.0.10")).compareTo(new SemVer("1.0.0")), is(equalTo(1))); + assertThat((new SemVer("1.0.0")).compareTo(new SemVer("1.5.0")), is(equalTo(-1))); + assertThat((new SemVer("1.10.574")).compareTo(new SemVer("1.0.999")), is(equalTo(1))); + assertThat((new SemVer("1.18.0")).compareTo(new SemVer("2.5.0")), is(equalTo(-1))); + assertThat((new SemVer("2.0.0")).compareTo(new SemVer("1.755.999")), is(equalTo(1))); + assertThat((new SemVer("1.2.50")).compareTo(new SemVer("1.20.0")), is(equalTo(-1))); + assertThat((new SemVer("1.20.2")).compareTo(new SemVer("1.2.50")), is(equalTo(1))); + } + + @Test + public void testCompareNull() { + assertThat((new SemVer("1.2.3")).compareTo(null), is(equalTo(1))); + assertThat((new SemVer("5.7.10")).compareTo(null), is(equalTo(1))); + } + + @Test + public void testSorting() { + SemVer a = new SemVer("0.8.1"); + SemVer b = new SemVer("1.20.1"); + SemVer c = new SemVer("5.3.0"); + SemVer d = new SemVer("5.3.1"); + List lst = Arrays.asList(b, a, d, c); + lst.sort(SemVer::compareTo); + assertThat(lst.get(0), is(equalTo(a))); + assertThat(lst.get(1), is(equalTo(b))); + assertThat(lst.get(2), is(equalTo(c))); + assertThat(lst.get(3), is(equalTo(d))); + } +}