diff --git a/dbrepo-authentication-service/dbrepo-realm.json b/dbrepo-authentication-service/dbrepo-realm.json index 0dbcd6bb725f9867788bfc442b033082d16bd672..99be5195d410b5b78f99d119b781df093d43425d 100644 --- a/dbrepo-authentication-service/dbrepo-realm.json +++ b/dbrepo-authentication-service/dbrepo-realm.json @@ -206,6 +206,14 @@ "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", "attributes" : { } + }, { + "id" : "e4cfdc4d-2373-477b-a8df-161db99aba00", + "name" : "create-foreign-identifier", + "description" : "${create-foreign-identifier}", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } }, { "id" : "09147c48-273b-450b-8b11-7ef9b9245244", "name" : "export-table-data", @@ -280,10 +288,7 @@ "id" : "abd2d9ee-ebc4-4d0a-839e-6b588a6d442a", "name" : "default-roles-dbrepo", "description" : "${role_default-roles}", - "composite" : true, - "composites" : { - "realm" : [ "default-researcher-roles" ] - }, + "composite" : false, "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", "attributes" : { } @@ -341,7 +346,7 @@ "description" : "${escalated-identifier-handling}", "composite" : true, "composites" : { - "realm" : [ "delete-identifier", "modify-identifier-metadata" ] + "realm" : [ "delete-identifier", "create-foreign-identifier", "modify-identifier-metadata" ] }, "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", @@ -844,54 +849,39 @@ } }, "groups" : [ { - "id" : "16c0fda1-864b-4c27-8755-0fdffa577000", - "name" : "External", - "path" : "/External", + "id" : "f2ce17fe-7b15-47a4-bbf8-86f415298fa9", + "name" : "data-stewards", + "path" : "/data-stewards", "attributes" : { }, - "realmRoles" : [ ], + "realmRoles" : [ "default-data-steward-roles" ], "clientRoles" : { }, "subGroups" : [ ] }, { - "id" : "1d8e6a45-1c77-453b-a5a8-9096e81e8b9b", - "name" : "Internal", - "path" : "/Internal", + "id" : "124d9888-0b6e-46aa-8225-077dcedaf16e", + "name" : "developers", + "path" : "/developers", "attributes" : { }, - "realmRoles" : [ ], + "realmRoles" : [ "default-developer-roles" ], "clientRoles" : { }, - "subGroups" : [ { - "id" : "7fe5a587-d2bc-4d3d-980b-324c3336862c", - "name" : "Developers", - "path" : "/Internal/Developers", - "attributes" : { }, - "realmRoles" : [ ], - "clientRoles" : { }, - "subGroups" : [ ] - }, { - "id" : "cc357d61-bfbf-4ed7-93d3-122113f438e3", - "name" : "Researchers", - "path" : "/Internal/Researchers", - "attributes" : { }, - "realmRoles" : [ ], - "clientRoles" : { }, - "subGroups" : [ ] - }, { - "id" : "c33f23e6-f7d0-4dee-9af4-f68773bad280", - "name" : "Data Stewards", - "path" : "/Internal/Data Stewards", - "attributes" : { }, - "realmRoles" : [ ], - "clientRoles" : { }, - "subGroups" : [ ] - } ] + "subGroups" : [ ] + }, { + "id" : "f467c38e-9041-4faa-ae0b-39cec65ff4db", + "name" : "researchers", + "path" : "/researchers", + "attributes" : { }, + "realmRoles" : [ "default-researcher-roles" ], + "clientRoles" : { }, + "subGroups" : [ ] } ], "defaultRole" : { "id" : "abd2d9ee-ebc4-4d0a-839e-6b588a6d442a", "name" : "default-roles-dbrepo", "description" : "${role_default-roles}", - "composite" : true, + "composite" : false, "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0" }, + "defaultGroups" : [ "/researchers" ], "requiredCredentials" : [ "password" ], "otpPolicyType" : "totp", "otpPolicyAlgorithm" : "HmacSHA1", @@ -900,7 +890,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName", "totpAppFreeOTPName" ], + "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -1890,7 +1880,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper" ] } }, { "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979", @@ -1899,7 +1889,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-full-name-mapper" ] } } ], "org.keycloak.keys.KeyProvider" : [ { @@ -1951,7 +1941,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "2659228d-907e-4832-9478-93c1537361ad", + "id" : "e29d33f1-c44c-4f6b-b8ca-54eb4b468f1d", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -1973,7 +1963,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6f2ebe37-d1c9-4359-8516-05f7c435a09c", + "id" : "bc4badef-d637-4448-b345-3e79f24290b0", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -2002,7 +1992,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e8ecd6b9-5991-4a84-b52d-bc9961d05f9a", + "id" : "06ddc635-bb0d-443a-ab6b-7f56be7fd59a", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2024,7 +2014,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "91815128-e40c-4800-946d-09d2f33a1f39", + "id" : "a8230646-a4a8-40c2-a37a-e7ac4daf9a67", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2046,7 +2036,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "90383773-dec6-4dc3-a6ff-5dfdde0ecda5", + "id" : "e84a6624-232f-401e-9937-6c5865b2a341", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2068,7 +2058,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ae5a2e94-c9b0-4851-b88e-4b0acacb645f", + "id" : "00094f7f-cdcb-4670-87b0-d75ed3fa986b", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -2090,7 +2080,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "a754b5ae-bc9d-4a98-ad59-6cc8ff27d016", + "id" : "e8cea56d-d07c-4696-b389-0f70449ba26d", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -2112,7 +2102,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "bdf46041-56db-445a-b4f3-3d000b239b8b", + "id" : "505a65be-a4f8-4e6e-a1a0-21d20d3f07a5", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -2135,7 +2125,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "1915f7a6-19a4-4e54-8010-73c939f18afe", + "id" : "9c31dad3-3367-4f7e-9fd0-3c855e1da0b8", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -2157,7 +2147,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "b3d8705b-5235-43da-89c6-ce55fb304ae5", + "id" : "e6fdd307-ca87-43f0-9f74-2bc567cc02db", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -2193,7 +2183,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "33efb232-46c9-4482-ba8e-7e452668bafa", + "id" : "b42d375b-1cb9-4cc7-be7d-fae32791d0a8", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -2229,7 +2219,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "f94e82fa-882d-4952-88c9-3aa37d9be1b6", + "id" : "3c534feb-1736-4ff4-8187-192e49f21fd9", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -2258,7 +2248,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "15948c95-df50-4304-8695-f586e5351979", + "id" : "5f3c06ca-8691-4c6c-8892-8d29563da9d0", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -2273,7 +2263,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "1983ebae-21b2-4a31-8517-c7a4746fedeb", + "id" : "76abf3cd-3635-494e-b75d-a3132a7933a5", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -2296,7 +2286,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "f9893ead-c7a1-404b-aca2-d24e03eb5c16", + "id" : "d44f3e97-d11a-49ba-a3bf-7c8489de290d", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -2318,7 +2308,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "d830402e-475d-46b5-a9d7-1105436d9092", + "id" : "7e60e213-6272-484c-b620-d7cf4f98f950", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -2340,7 +2330,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "0de6040b-5167-4ab1-8e40-f633419b5890", + "id" : "cce9e0e9-82f4-499d-a6f7-fc556248fc25", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -2356,7 +2346,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "84a658d1-d9ca-4090-9a86-9d45844324e2", + "id" : "61f08b26-e87b-4126-ac7a-c704d01e54dc", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -2392,7 +2382,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9cc594f8-9bb0-4557-bffb-4a66b2eb0f34", + "id" : "d9ce077f-16c2-4c96-aba5-dd60bb3aa536", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -2428,7 +2418,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "d2b1442c-c5db-4e3f-94f6-48196fccd207", + "id" : "8d4383f0-ebd2-4e0b-8d3f-2c6e1793c05b", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -2444,13 +2434,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "aebc30c4-79b7-47c5-8d13-f4cfe66aba10", + "id" : "1206bd7a-0525-4e21-9bc7-d9ec62c1a4bc", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "5c00357d-8701-4848-a920-0ae1db86fdd0", + "id" : "f5ff277d-b059-4dff-aaf8-8c5feeca1d4c", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java index eb7f335e0e95a5ca8f149a17ebe4371b621d044f..43cc6674b9d106548b72b73c8b4aaccd815cdadb 100644 --- a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java +++ b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java @@ -91,7 +91,7 @@ public class IdentifierEndpoint { @PostMapping @Transactional @Timed(value = "identifier.create", description = "Time needed to create an identifier") - @PreAuthorize("hasAuthority('create-identifier')") + @PreAuthorize("hasAuthority('create-identifier') or hasAuthority('create-foreign-identifier')") @Operation(summary = "Create identifier", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -104,8 +104,8 @@ public class IdentifierEndpoint { content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), - @ApiResponse(responseCode = "404", - description = "Query, database or user could not be found", + @ApiResponse(responseCode = "403", + description = "Insufficient access rights or authorities", content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), @@ -134,7 +134,7 @@ public class IdentifierEndpoint { @NotNull @RequestHeader(name = "Authorization") String authorization, @NotNull Principal principal) throws IdentifierAlreadyExistsException, QueryNotFoundException, IdentifierPublishingNotAllowedException, - RemoteUnavailableException, UserNotFoundException, DatabaseNotFoundException, IdentifierRequestException, AccessDeniedException { + RemoteUnavailableException, UserNotFoundException, DatabaseNotFoundException, IdentifierRequestException, NotAllowedException { log.debug("endpoint create identifier, data={}, authorization=(hidden), principal={}", data, principal); if (data.getType().equals(IdentifierTypeDto.SUBSET) && data.getQid() == null) { log.error("Identifier of type subset need to have a qid present"); @@ -144,63 +144,16 @@ public class IdentifierEndpoint { throw new IdentifierRequestException("Identifier of type database must not have a qid present"); } final User user = userService.findByUsername(principal.getName()); - final DatabaseAccess access = accessService.find(data.getDbid(), user.getId()); + try { + accessService.find(data.getDbid(), user.getId()); + } catch (AccessDeniedException e) { + if (!User.hasRole(principal, "create-foreign-identifier")) { + log.error("Failed to create identifier: insufficient access"); + throw new NotAllowedException("Failed to create identifier: insufficient access"); + } + } final Identifier identifier = identifierService.create(data, principal, authorization); return ResponseEntity.status(HttpStatus.CREATED) .body(identifierMapper.identifierToIdentifierDto(identifier)); } - - @PutMapping("/{id}") - @Transactional - @Timed(value = "identifier.update", description = "Time needed to update an identifier") - @PreAuthorize("hasAuthority('update-identifier')") - @Operation(summary = "Update some identifier", security = @SecurityRequirement(name = "bearerAuth")) - @ApiResponses(value = { - @ApiResponse(responseCode = "202", - description = "Updated identifier", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = IdentifierDto.class))}), - @ApiResponse(responseCode = "404", - description = "Identifier could not be found", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), - @ApiResponse(responseCode = "406", - description = "Updating identifier not allowed", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), - }) - public ResponseEntity<IdentifierDto> update(@NotNull @PathVariable("id") Long id, - @NotNull @Valid @RequestBody IdentifierDto data) - throws IdentifierNotFoundException, IdentifierRequestException { - log.debug("endpoint update identifier, id={}, data={}", id, data); - final Identifier identifier = identifierService.update(id, data); - return ResponseEntity.accepted() - .body(identifierMapper.identifierToIdentifierDto(identifier)); - } - - @DeleteMapping("/{id}") - @Transactional - @Timed(value = "identifier.delete", description = "Time needed to delete an identifier") - @PreAuthorize("hasAuthority('delete-identifier')") - @Operation(summary = "Delete some identifier", security = @SecurityRequirement(name = "bearerAuth")) - @ApiResponses(value = { - @ApiResponse(responseCode = "202", - description = "Deleted identifier", - content = {@Content}), - @ApiResponse(responseCode = "404", - description = "Identifier could not be found", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ApiErrorDto.class))}), - }) - public ResponseEntity<?> delete(@NotNull @PathVariable("id") Long id) - throws IdentifierNotFoundException, NotAllowedException { - log.debug("endpoint delete identifier, id={}", id); - identifierService.delete(id); - return ResponseEntity.accepted() - .build(); - } } diff --git a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java index 602ae69e091e35317d125a58a77da72deaa0b27f..6f30f79be88c9ece0f87d7c9ff1378ba7b0dd311 100644 --- a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java +++ b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java @@ -3,20 +3,22 @@ package at.tuwien.endpoints; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.api.identifier.BibliographyTypeDto; import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.api.identifier.IdentifierUpdateDto; import at.tuwien.config.EndpointConfig; import at.tuwien.entities.identifier.Identifier; -import at.tuwien.exception.IdentifierNotFoundException; -import at.tuwien.exception.IdentifierRequestException; -import at.tuwien.exception.QueryNotFoundException; -import at.tuwien.exception.RemoteUnavailableException; +import at.tuwien.entities.user.User; +import at.tuwien.exception.*; import at.tuwien.mapper.IdentifierMapper; +import at.tuwien.service.AccessService; import at.tuwien.service.IdentifierService; +import at.tuwien.service.UserService; import io.micrometer.core.annotation.Timed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; @@ -28,6 +30,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.security.Principal; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -37,13 +41,17 @@ import java.util.regex.Pattern; @RequestMapping("/api/pid") public class PersistenceEndpoint { + private final UserService userService; + private final AccessService accessService; private final EndpointConfig endpointConfig; private final IdentifierMapper identifierMapper; private final IdentifierService identifierService; @Autowired - public PersistenceEndpoint(EndpointConfig endpointConfig, IdentifierMapper identifierMapper, - IdentifierService identifierService) { + public PersistenceEndpoint(UserService userService, AccessService accessService, EndpointConfig endpointConfig, + IdentifierMapper identifierMapper, IdentifierService identifierService) { + this.userService = userService; + this.accessService = accessService; this.endpointConfig = endpointConfig; this.identifierMapper = identifierMapper; this.identifierService = identifierService; @@ -141,4 +149,95 @@ public class PersistenceEndpoint { .build(); } + @PutMapping("/{id}") + @Transactional + @Timed(value = "identifier.update", description = "Time needed to update an identifier") + @PreAuthorize("hasAuthority('modify-identifier-metadata')") + @Operation(summary = "Update some identifier", security = @SecurityRequirement(name = "bearerAuth")) + @ApiResponses(value = { + @ApiResponse(responseCode = "202", + description = "Updated identifier", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = IdentifierDto.class))}), + @ApiResponse(responseCode = "400", + description = "Identifier data is not valid to the form", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), + @ApiResponse(responseCode = "404", + description = "Identifier or user could not be found", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), + @ApiResponse(responseCode = "405", + description = "Updating identifier not permitted", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), + @ApiResponse(responseCode = "406", + description = "Updating identifier not allowed", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), + }) + public ResponseEntity<IdentifierDto> update(@NotNull @PathVariable("id") Long id, + @NotNull @Valid @RequestBody IdentifierUpdateDto data, + @NotNull Principal principal) + throws IdentifierNotFoundException, IdentifierRequestException, UserNotFoundException, NotAllowedException { + log.debug("endpoint update identifier, id={}, data={}", id, data); + final Identifier identifier = identifierService.find(id); + final User user = userService.findByUsername(principal.getName()); + try { + accessService.find(identifier.getDatabaseId(), user.getId()); + } catch (AccessDeniedException e) { + if (!User.hasRole(principal, "modify-identifier-metadata")) { + log.error("Failed to update identifier: insufficient access"); + throw new NotAllowedException("Failed to update identifier: insufficient access"); + } + } + /* check */ + if (identifier.getDoi() != null && !identifier.getDoi().equals(data.getDoi())) { + log.error("Failed to update identifier: once attached the DOI cannot be changed"); + throw new IdentifierRequestException("Failed to update identifier: once attached the DOI cannot be changed"); + } + final IdentifierDto dto = identifierMapper.identifierToIdentifierDto(identifierService.update(id, data)); + log.debug("update identifier resulted in dto={}", dto); + return ResponseEntity.accepted() + .body(dto); + } + + @DeleteMapping("/{id}") + @Transactional + @Timed(value = "identifier.delete", description = "Time needed to delete an identifier") + @PreAuthorize("hasAuthority('delete-identifier')") + @Operation(summary = "Delete some identifier", security = @SecurityRequirement(name = "bearerAuth")) + @ApiResponses(value = { + @ApiResponse(responseCode = "202", + description = "Deleted identifier", + content = {@Content}), + @ApiResponse(responseCode = "404", + description = "Identifier could not be found", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}), + @ApiResponse(responseCode = "405", + description = "Deleting identifier not permitted", + content = {@Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiErrorDto.class))}) + }) + public ResponseEntity<?> delete(@NotNull @PathVariable("id") Long id) + throws IdentifierNotFoundException, NotAllowedException { + log.debug("endpoint delete identifier, id={}", id); + final Identifier identifier = identifierService.find(id); + if (identifier.getDoi() != null) { + log.error("Failed to delete identifier: a DOI is already attached"); + throw new NotAllowedException("Failed to delete identifier: a DOI is already attached"); + } + identifierService.delete(id); + return ResponseEntity.accepted() + .build(); + } + } diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointIntegrationTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointIntegrationTest.java index 831bf171d56a2512a056a6f14fcc6fd0db43e56f..a3a0191d1afebd99dee10509164f63f6ffc2b1d1 100644 --- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointIntegrationTest.java +++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointIntegrationTest.java @@ -6,6 +6,7 @@ import at.tuwien.api.identifier.IdentifierTypeDto; import at.tuwien.config.IndexInitializer; import at.tuwien.config.ReadyConfig; import at.tuwien.endpoints.IdentifierEndpoint; +import at.tuwien.exception.NotAllowedException; import at.tuwien.repository.jpa.*; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; @@ -87,7 +88,7 @@ public class IdentifierEndpointIntegrationTest extends BaseUnitTest { public void list_hasRole_succeeds() { /* mock */ - identifierRepository.save(IDENTIFIER_1_SIMPLE); + identifierRepository.save(IDENTIFIER_1); /* test */ final List<IdentifierDto> response = this.generic_list(null, null, null); @@ -103,7 +104,7 @@ public class IdentifierEndpointIntegrationTest extends BaseUnitTest { public void list_noRole_succeeds() { /* mock */ - identifierRepository.save(IDENTIFIER_1_SIMPLE); + identifierRepository.save(IDENTIFIER_1); /* test */ final List<IdentifierDto> response = this.generic_list(null, null, null); @@ -119,7 +120,7 @@ public class IdentifierEndpointIntegrationTest extends BaseUnitTest { public void list_databaseId_succeeds() { /* mock */ - identifierRepository.save(IDENTIFIER_1_SIMPLE); + identifierRepository.save(IDENTIFIER_1); /* test */ final List<IdentifierDto> response = this.generic_list(DATABASE_1_ID, null, null); @@ -139,10 +140,10 @@ public class IdentifierEndpointIntegrationTest extends BaseUnitTest { containerRepository.save(CONTAINER_4_SIMPLE); databaseRepository.save(DATABASE_3_SIMPLE); databaseRepository.save(DATABASE_4_SIMPLE); - identifierRepository.save(IDENTIFIER_1_SIMPLE); - identifierRepository.save(IDENTIFIER_2_SIMPLE); - identifierRepository.save(IDENTIFIER_3_SIMPLE); - identifierRepository.save(IDENTIFIER_4_SIMPLE); + identifierRepository.save(IDENTIFIER_1); + identifierRepository.save(IDENTIFIER_2); + identifierRepository.save(IDENTIFIER_3); + identifierRepository.save(IDENTIFIER_4); /* test */ final List<IdentifierDto> response = this.generic_list(DATABASE_4_ID, null, IdentifierTypeDto.DATABASE); @@ -158,7 +159,7 @@ public class IdentifierEndpointIntegrationTest extends BaseUnitTest { public void list_subsetIdAndType_succeeds() { /* mock */ - identifierRepository.save(IDENTIFIER_1_SIMPLE); + identifierRepository.save(IDENTIFIER_1); /* test */ final List<IdentifierDto> response = this.generic_list(DATABASE_1_ID, QUERY_1_ID, IdentifierTypeDto.SUBSET); @@ -188,7 +189,7 @@ public class IdentifierEndpointIntegrationTest extends BaseUnitTest { databaseRepository.save(DATABASE_3_SIMPLE); /* test */ - assertThrows(at.tuwien.exception.AccessDeniedException.class, () -> { + assertThrows(NotAllowedException.class, () -> { identifierEndpoint.create(IDENTIFIER_3_DTO_REQUEST, "ABC", USER_1_PRINCIPAL); }); } diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java index 0d1f7ce1cc92381d4f3de24101a43c44f851fd51..cab5bb1b8973ff40ded5eec41aa3699f46952a78 100644 --- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java +++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java @@ -10,12 +10,14 @@ import at.tuwien.config.ReadyConfig; import at.tuwien.endpoints.IdentifierEndpoint; import at.tuwien.endpoints.PersistenceEndpoint; import at.tuwien.entities.database.Database; +import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.identifier.RelatedIdentifier; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.QueryServiceGateway; import at.tuwien.repository.jpa.*; +import at.tuwien.service.AccessService; import at.tuwien.service.IdentifierService; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.Test; @@ -37,10 +39,11 @@ import java.io.IOException; import java.security.Principal; import java.util.List; import java.util.Optional; +import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @@ -62,6 +65,9 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { @MockBean private IdentifierRepository identifierRepository; + @MockBean + private AccessService accessService; + @MockBean private UserRepository userRepository; @@ -148,7 +154,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, IDENTIFIER_1_DTO_REQUEST, null, null, null, null); + generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, null, IDENTIFIER_1_DTO_REQUEST, null, null, null, null, null); }); } @@ -156,23 +162,23 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"}) public void create_hasRoleDatabase_succeeds() throws IdentifierAlreadyExistsException, UserNotFoundException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, - IdentifierPublishingNotAllowedException, IdentifierRequestException, at.tuwien.exception.AccessDeniedException { + IdentifierPublishingNotAllowedException, IdentifierRequestException, NotAllowedException, at.tuwien.exception.AccessDeniedException { /* mock */ when(accessRepository.findByHdbidAndHuserid(DATABASE_1_ID, USER_1_ID)) .thenReturn(Optional.of(DATABASE_1_RESEARCHER_READ_ACCESS)); /* test */ - generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, IDENTIFIER_1_DTO_REQUEST, IDENTIFIER_1, USER_1_PRINCIPAL, USER_1_USERNAME, USER_1); + generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, DATABASE_1_RESEARCHER_READ_ACCESS, IDENTIFIER_1_DTO_REQUEST, IDENTIFIER_1, USER_1_PRINCIPAL, USER_1_ID, USER_1_USERNAME, USER_1); } @Test - @WithMockUser(username = USER_2_USERNAME, authorities = {"create-identifier"}) + @WithMockUser(username = USER_3_USERNAME, authorities = {"create-identifier"}) public void create_hasRoleDatabaseNoAccess_fails() { /* test */ - assertThrows(at.tuwien.exception.AccessDeniedException.class, () -> { - generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, IDENTIFIER_1_DTO_REQUEST, IDENTIFIER_1, USER_2_PRINCIPAL, USER_2_USERNAME, USER_2); + assertThrows(NotAllowedException.class, () -> { + generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, null, IDENTIFIER_1_DTO_REQUEST, IDENTIFIER_1, USER_3_PRINCIPAL, USER_3_ID, USER_3_USERNAME, USER_3); }); } @@ -182,7 +188,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(AccessDeniedException.class, () -> { - generic_create(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, IDENTIFIER_2_DTO_REQUEST, IDENTIFIER_2, null, null, null); + generic_create(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, null, IDENTIFIER_2_DTO_REQUEST, IDENTIFIER_2, null, null, null, null); }); } @@ -190,14 +196,11 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_2_USERNAME, authorities = {"create-identifier"}) public void create_hasRoleReadAccessQuery_succeeds() throws IdentifierAlreadyExistsException, UserNotFoundException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, - IdentifierPublishingNotAllowedException, IdentifierRequestException, at.tuwien.exception.AccessDeniedException { - - /* mock */ - when(accessRepository.findByHdbidAndHuserid(DATABASE_2_ID, USER_2_ID)) - .thenReturn(Optional.of(DATABASE_2_RESEARCHER_READ_ACCESS)); + IdentifierPublishingNotAllowedException, IdentifierRequestException, NotAllowedException, + at.tuwien.exception.AccessDeniedException { /* test */ - generic_create(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, IDENTIFIER_2_DTO_REQUEST, IDENTIFIER_2, USER_2_PRINCIPAL, USER_2_USERNAME, USER_2); + generic_create(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, DATABASE_2_RESEARCHER_READ_ACCESS, IDENTIFIER_2_DTO_REQUEST, IDENTIFIER_2, USER_2_PRINCIPAL, USER_2_ID, USER_2_USERNAME, USER_2); } @Test @@ -219,7 +222,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(IdentifierRequestException.class, () -> { - generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, request, null, USER_1_PRINCIPAL, USER_1_USERNAME, USER_1); + generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, DATABASE_1_RESEARCHER_READ_ACCESS, request, null, USER_1_PRINCIPAL, USER_1_ID, USER_1_USERNAME, USER_1); }); } @@ -243,78 +246,30 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(IdentifierRequestException.class, () -> { - generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, request, null, USER_1_PRINCIPAL, USER_1_USERNAME, USER_1); + generic_create(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, DATABASE_1_RESEARCHER_READ_ACCESS, request, null, USER_1_PRINCIPAL, USER_1_ID, USER_1_USERNAME, USER_1); }); } @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"}) - public void create_query_fails() { + public void create_queryForeign_fails() { /* test */ - assertThrows(at.tuwien.exception.AccessDeniedException.class, () -> { - generic_create(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, IDENTIFIER_2_DTO_REQUEST, IDENTIFIER_2, USER_1_PRINCIPAL, USER_1_USERNAME, USER_1); + assertThrows(NotAllowedException.class, () -> { + generic_create(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, null, IDENTIFIER_2_DTO_REQUEST, IDENTIFIER_2, USER_1_PRINCIPAL, USER_1_ID, USER_1_USERNAME, USER_1); }); } - @Test - public void update_anonymous_fails() { - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, this::generic_update); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, authorities = {}) - public void update_noRole_fails() { - - /* test */ - assertThrows(AccessDeniedException.class, this::generic_update); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, authorities = {"update-identifier"}) - public void update_hasRole_succeeds() throws IdentifierPublishingNotAllowedException, - IdentifierNotFoundException, IdentifierRequestException { - - /* test */ - generic_update(); - } - - @Test - @WithAnonymousUser - public void delete_anonymous_fails() { - - /* test */ - assertThrows(AccessDeniedException.class, this::generic_delete); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, authorities = {}) - public void delete_noRole_fails() { - - /* test */ - assertThrows(AccessDeniedException.class, this::generic_delete); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, authorities = {"delete-identifier"}) - public void delete_hasRole_succeeds() throws NotAllowedException, IdentifierNotFoundException { - - /* test */ - this.generic_delete(); - } - /* ################################################################################################### */ /* ## GENERIC TEST CASES ## */ /* ################################################################################################### */ - protected void generic_create(Long containerId, Long databaseId, Database database, IdentifierCreateDto data, - Identifier identifier, Principal principal, String username, User user) - throws QueryNotFoundException, RemoteUnavailableException, - IdentifierAlreadyExistsException, - UserNotFoundException, DatabaseNotFoundException, IdentifierPublishingNotAllowedException, - IdentifierRequestException, at.tuwien.exception.AccessDeniedException { + protected void generic_create(Long containerId, Long databaseId, Database database, DatabaseAccess access, + IdentifierCreateDto data, Identifier identifier, Principal principal, UUID userId, + String username, User user) throws QueryNotFoundException, RemoteUnavailableException, + IdentifierAlreadyExistsException, UserNotFoundException, DatabaseNotFoundException, + IdentifierPublishingNotAllowedException, IdentifierRequestException, NotAllowedException, + at.tuwien.exception.AccessDeniedException { /* mock */ when(databaseRepository.findById(databaseId)) @@ -326,6 +281,14 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { when(userRepository.findByUsername(username)) .thenReturn(Optional.of(user)); } + if (access != null) { + when(accessService.find(databaseId, userId)) + .thenReturn(access); + } else { + doThrow(at.tuwien.exception.AccessDeniedException.class) + .when(accessService) + .find(databaseId, userId); + } when(queryServiceGateway.find(containerId, databaseId, data, "ABC")) .thenReturn(QUERY_1_DTO); when(identifierService.create(data, principal, "ABC")) @@ -368,40 +331,4 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { return persistenceEndpoint.find(IDENTIFIER_1_ID, accept); } - protected void generic_update() - throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException, IdentifierRequestException { - - /* mock */ - when(identifierService.update(IDENTIFIER_3_ID, IDENTIFIER_3_DTO)) - .thenReturn(IDENTIFIER_3); - when(identifierRepository.save(IDENTIFIER_3)) - .thenReturn(IDENTIFIER_3); - - /* test */ - final ResponseEntity<IdentifierDto> response = identifierEndpoint.update(IDENTIFIER_3_ID, IDENTIFIER_3_DTO); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - final IdentifierDto body = response.getBody(); - assertNotNull(body); - assertEquals(IDENTIFIER_3_ID, body.getId()); - assertEquals(IDENTIFIER_3_TITLE, body.getTitle()); - assertEquals(IDENTIFIER_3_DESCRIPTION, body.getDescription()); - assertEquals(IDENTIFIER_3_QUERY, body.getQuery()); - assertEquals(IDENTIFIER_3_QUERY_HASH, body.getQueryHash()); - assertEquals(IDENTIFIER_3_RESULT_NUMBER, body.getResultNumber()); - assertEquals(IDENTIFIER_3_RESULT_HASH, body.getResultHash()); - } - - protected void generic_delete() throws IdentifierNotFoundException, NotAllowedException { - - /* mock */ - doNothing() - .when(identifierService) - .delete(IDENTIFIER_1_ID); - - /* test */ - final ResponseEntity<?> response = identifierEndpoint.delete(IDENTIFIER_1_ID); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNull(response.getBody()); - } - } diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java index 0fe8adf13fae63b6aec2f3a2d92ee30c2453d1bb..470f11c7a85ba1f0dabb951114dbe01eaef6d9bd 100644 --- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java +++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java @@ -1,20 +1,19 @@ package at.tuwien.endpoint; import at.tuwien.BaseUnitTest; +import at.tuwien.api.identifier.BibliographyTypeDto; import at.tuwien.api.identifier.CreatorDto; import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.api.identifier.IdentifierUpdateDto; import at.tuwien.config.IndexInitializer; import at.tuwien.config.ReadyConfig; import at.tuwien.endpoints.PersistenceEndpoint; -import at.tuwien.entities.identifier.Creator; -import at.tuwien.exception.IdentifierNotFoundException; -import at.tuwien.exception.IdentifierRequestException; -import at.tuwien.exception.QueryNotFoundException; -import at.tuwien.exception.RemoteUnavailableException; -import at.tuwien.gateway.QueryServiceGateway; -import at.tuwien.repository.jpa.IdentifierRepository; -import at.tuwien.repository.jpa.RealmRepository; -import com.fasterxml.jackson.core.JsonProcessingException; +import at.tuwien.entities.identifier.Identifier; +import at.tuwien.entities.user.User; +import at.tuwien.exception.*; +import at.tuwien.service.AccessService; +import at.tuwien.service.IdentifierService; +import at.tuwien.service.UserService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; @@ -30,27 +29,22 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.client.RestTemplate; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.StringWriter; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.Date; -import java.util.List; -import java.util.Optional; +import java.security.Principal; import java.util.TimeZone; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(SpringExtension.class) @SpringBootTest @@ -63,10 +57,13 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { private IndexInitializer indexInitializer; @MockBean - private IdentifierRepository identifierRepository; + private AccessService accessService; @MockBean - private QueryServiceGateway queryServiceGateway; + private IdentifierService identifierService; + + @MockBean + private UserService userService; @Autowired private ObjectMapper objectMapper; @@ -87,14 +84,15 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_json0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "application/json"; final IdentifierDto compare = objectMapper.readValue(FileUtils.readFileToString(new File("src/test/resources/json/metadata0.json"), StandardCharsets.UTF_8), IdentifierDto.class); /* mock */ - when(identifierRepository.findById(IDENTIFIER_4_ID)) - .thenReturn(Optional.of(IDENTIFIER_4)); + when(identifierService.find(IDENTIFIER_4_ID)) + .thenReturn(IDENTIFIER_4); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_4_ID, accept); @@ -118,14 +116,15 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_json1_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "application/json"; final IdentifierDto compare = objectMapper.readValue(FileUtils.readFileToString(new File("src/test/resources/json/metadata1.json"), StandardCharsets.UTF_8), IdentifierDto.class); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -145,6 +144,8 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { assertEquals(compare.getPublicationMonth(), body.getPublicationMonth()); assertEquals(compare.getPublicationYear(), body.getPublicationYear()); assertEquals(compare.getPublisher(), body.getPublisher()); + assertNotNull(compare.getCreators()); + assertNotNull(body.getCreators()); assertEquals(compare.getCreators().size(), body.getCreators().size()); final CreatorDto creator1 = body.getCreators().get(0); assertEquals(compare.getCreators().get(0).getFirstname(), creator1.getFirstname()); @@ -154,17 +155,18 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_csv_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/csv"; - final byte[] stream = FileUtils.readFileToByteArray(new File("src/test/resources/csv/keyboard.csv")); final InputStreamResource compare = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/csv/keyboard.csv"))); + final InputStreamResource mock = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/csv/keyboard.csv"))); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); - when(queryServiceGateway.export(CONTAINER_1_ID, DATABASE_1_ID, QUERY_1_ID)) - .thenReturn(stream); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); + when(identifierService.exportResource(IDENTIFIER_1_ID)) + .thenReturn(mock); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -175,15 +177,15 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test - @Disabled + @Disabled("not testable with xml") public void find_xml0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/xml"; final InputStreamResource compare = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/xml/metadata0.xml"))); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -194,15 +196,15 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test - @Disabled + @Disabled("not testable with xml") public void find_xml1_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/xml"; final InputStreamResource compare = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/xml/metadata1.xml"))); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -214,6 +216,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliography_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography"; @@ -221,8 +224,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierService.exportBibliography(IDENTIFIER_1_ID, BibliographyTypeDto.APA)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -233,6 +238,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyApa0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=apa"; @@ -240,8 +246,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_4_ID)) - .thenReturn(Optional.of(IDENTIFIER_4)); + when(identifierService.exportBibliography(IDENTIFIER_4_ID, BibliographyTypeDto.APA)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_4_ID)) + .thenReturn(IDENTIFIER_4); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_4_ID, accept); @@ -252,6 +260,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyApa1_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=apa"; @@ -259,8 +268,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierService.exportBibliography(IDENTIFIER_1_ID, BibliographyTypeDto.APA)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -271,6 +282,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyApa2_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=apa"; @@ -278,8 +290,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_2_ID)) - .thenReturn(Optional.of(IDENTIFIER_2)); + when(identifierService.exportBibliography(IDENTIFIER_2_ID, BibliographyTypeDto.APA)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_2_ID)) + .thenReturn(IDENTIFIER_2); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_2_ID, accept); @@ -290,6 +304,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyApa3_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=apa"; @@ -297,8 +312,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_3_ID)) - .thenReturn(Optional.of(IDENTIFIER_3)); + when(identifierService.exportBibliography(IDENTIFIER_3_ID, BibliographyTypeDto.APA)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_3_ID)) + .thenReturn(IDENTIFIER_3); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_3_ID, accept); @@ -309,6 +326,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyApa4_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=apa"; @@ -316,8 +334,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); + when(identifierService.exportBibliography(IDENTIFIER_1_ID, BibliographyTypeDto.APA)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1_WITH_DOI); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -328,6 +348,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyIeee0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=ieee"; @@ -335,8 +356,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_4_ID)) - .thenReturn(Optional.of(IDENTIFIER_4)); + when(identifierService.exportBibliography(IDENTIFIER_4_ID, BibliographyTypeDto.IEEE)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_4_ID)) + .thenReturn(IDENTIFIER_4); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_4_ID, accept); @@ -347,6 +370,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyIeee1_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=ieee"; @@ -354,8 +378,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierService.exportBibliography(IDENTIFIER_1_ID, BibliographyTypeDto.IEEE)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -366,6 +392,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyIeee2_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=ieee"; @@ -373,8 +400,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_2_ID)) - .thenReturn(Optional.of(IDENTIFIER_2)); + when(identifierService.exportBibliography(IDENTIFIER_2_ID, BibliographyTypeDto.IEEE)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_2_ID)) + .thenReturn(IDENTIFIER_2); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_2_ID, accept); @@ -385,6 +414,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyIeee3_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=ieee"; @@ -392,8 +422,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); + when(identifierService.exportBibliography(IDENTIFIER_1_ID, BibliographyTypeDto.IEEE)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1_WITH_DOI); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -404,6 +436,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyBibtex0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=bibtex"; @@ -411,8 +444,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_4_ID)) - .thenReturn(Optional.of(IDENTIFIER_4)); + when(identifierService.exportBibliography(IDENTIFIER_4_ID, BibliographyTypeDto.BIBTEX)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_4_ID)) + .thenReturn(IDENTIFIER_4); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_4_ID, accept); @@ -423,6 +458,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyBibtex1_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=bibtex"; @@ -430,8 +466,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierService.exportBibliography(IDENTIFIER_1_ID, BibliographyTypeDto.BIBTEX)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -442,6 +480,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyBibtex2_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=bibtex"; @@ -449,8 +488,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_2_ID)) - .thenReturn(Optional.of(IDENTIFIER_2)); + when(identifierService.exportBibliography(IDENTIFIER_2_ID, BibliographyTypeDto.BIBTEX)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_2_ID)) + .thenReturn(IDENTIFIER_2); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_2_ID, accept); @@ -461,6 +502,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { } @Test + @WithAnonymousUser public void find_bibliographyBibtex3_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { final String accept = "text/bibliography; style=bibtex"; @@ -468,8 +510,10 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { StandardCharsets.UTF_8); /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); + when(identifierService.exportBibliography(IDENTIFIER_1_ID, BibliographyTypeDto.BIBTEX)) + .thenReturn(compare); + when(identifierService.find(IDENTIFIER_1_ID)) + .thenReturn(IDENTIFIER_1_WITH_DOI); /* test */ final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); @@ -479,8 +523,158 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { assertEquals(compare, body); } + @Test + @WithAnonymousUser + public void update_anonymous_fails() { + + /* test */ + assertThrows(AccessDeniedException.class, () -> { + generic_update(IDENTIFIER_3_ID, IDENTIFIER_3, IDENTIFIER_3_DTO_UPDATE_REQUEST, null, null, null); + }); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {}) + public void update_noRole_fails() { + + /* test */ + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { + generic_update(IDENTIFIER_3_ID, IDENTIFIER_3, IDENTIFIER_3_DTO_UPDATE_REQUEST, USER_4_USERNAME, USER_4, USER_4_PRINCIPAL); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"modify-identifier-metadata"}) + public void update_hasRoleNoAccess_succeeds() throws at.tuwien.exception.AccessDeniedException { + + /* mock */ + doThrow(at.tuwien.exception.AccessDeniedException.class) + .when(accessService) + .find(IDENTIFIER_3_DATABASE_ID, USER_3_ID); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + generic_update(IDENTIFIER_3_ID, IDENTIFIER_3, IDENTIFIER_3_DTO_UPDATE_REQUEST, USER_3_USERNAME, USER_3, USER_3_PRINCIPAL); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"modify-identifier-metadata"}) + public void update_hasRoleHasAccess_succeeds() throws IdentifierNotFoundException, IdentifierRequestException, + UserNotFoundException, at.tuwien.exception.AccessDeniedException, NotAllowedException, IdentifierUpdateBadFormException { + + /* mock */ + when(accessService.find(IDENTIFIER_3_DATABASE_ID, USER_3_ID)) + .thenReturn(DATABASE_3_DATA_STEWARD_READ_ACCESS); + + /* test */ + generic_update(IDENTIFIER_3_ID, IDENTIFIER_3, IDENTIFIER_3_DTO_UPDATE_REQUEST, USER_3_USERNAME, USER_3, USER_3_PRINCIPAL); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-identifier-metadata"}) + public void update_doiChange_fails() throws at.tuwien.exception.AccessDeniedException { + final IdentifierUpdateDto request = IdentifierUpdateDto.builder() + .doi("10.000/thisisadifferentdoi") + .build(); + + /* mock */ + when(accessService.find(IDENTIFIER_1_DATABASE_ID, USER_1_ID)) + .thenReturn(DATABASE_1_DATA_STEWARD_READ_ACCESS); + + /* test */ + assertThrows(IdentifierRequestException.class, () -> { + generic_update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI, request, USER_1_USERNAME, USER_1, USER_1_PRINCIPAL); + }); + } + + @Test + @WithAnonymousUser + public void delete_anonymous_fails() { + + /* test */ + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { + this.generic_delete(IDENTIFIER_1_ID, IDENTIFIER_1); + }); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {}) + public void delete_noRole_fails() { + + /* test */ + assertThrows(AccessDeniedException.class, () -> { + this.generic_delete(IDENTIFIER_1_ID, IDENTIFIER_1); + }); + } + + @Test + @WithMockUser(username = USER_2_USERNAME, authorities = {"delete-identifier"}) + public void delete_hasRole_succeeds() throws NotAllowedException, IdentifierNotFoundException { + + /* test */ + this.generic_delete(IDENTIFIER_1_ID, IDENTIFIER_1); + } + + /* ################################################################################################### */ + /* ## GENERIC TEST CASES ## */ + /* ################################################################################################### */ + protected static String inputStreamToString(InputStream inputStream) throws IOException { return IOUtils.toString(inputStream, StandardCharsets.UTF_8); } + protected void generic_update(Long id, Identifier identifier, IdentifierUpdateDto data, String username, User user, + Principal principal) throws IdentifierNotFoundException, IdentifierRequestException, + UserNotFoundException, NotAllowedException, IdentifierUpdateBadFormException { + + /* mock */ + if (identifier != null) { + when(identifierService.update(id, data)) + .thenReturn(identifier); + when(identifierService.find(id)) + .thenReturn(identifier); + } else { + doThrow(IdentifierNotFoundException.class) + .when(identifierService) + .find(id); + } + if (user != null) { + when(userService.findByUsername(username)) + .thenReturn(user); + } else { + doThrow(UserNotFoundException.class) + .when(userService) + .findByUsername(username); + } + + /* test */ + final ResponseEntity<IdentifierDto> response = persistenceEndpoint.update(id, data, principal); + assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); + final IdentifierDto body = response.getBody(); + assertNotNull(body); + assertEquals(IDENTIFIER_3_ID, body.getId()); + assertEquals(IDENTIFIER_3_TITLE, body.getTitle()); + assertEquals(IDENTIFIER_3_DESCRIPTION, body.getDescription()); + assertEquals(IDENTIFIER_3_QUERY, body.getQuery()); + assertEquals(IDENTIFIER_3_QUERY_HASH, body.getQueryHash()); + assertEquals(IDENTIFIER_3_RESULT_NUMBER, body.getResultNumber()); + assertEquals(IDENTIFIER_3_RESULT_HASH, body.getResultHash()); + } + + protected void generic_delete(Long id, Identifier identifier) throws IdentifierNotFoundException, NotAllowedException { + + /* mock */ + when(identifierService.find(id)) + .thenReturn(identifier); + doNothing() + .when(identifierService) + .delete(id); + + /* test */ + final ResponseEntity<?> response = persistenceEndpoint.delete(id); + assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); + assertNull(response.getBody()); + } + } diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java index 36e265abbadfc726a5f3498f068bd44d819f8009..368b48e24a58b07b31701e61a84efba8d6999cb2 100644 --- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java +++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java @@ -5,7 +5,7 @@ import at.tuwien.api.datacite.DataCiteBody; import at.tuwien.api.datacite.DataCiteData; import at.tuwien.api.datacite.doi.DataCiteDoi; import at.tuwien.api.identifier.IdentifierCreateDto; -import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.api.identifier.IdentifierUpdateDto; import at.tuwien.config.DataCiteConfig; import at.tuwien.config.EndpointConfig; import at.tuwien.config.IndexInitializer; @@ -90,7 +90,6 @@ public class DataCiteIdentifierServiceUnitTest extends BaseUnitTest { containerRepository.save(CONTAINER_1); databaseRepository.save(DATABASE_1); when(restTemplateBuilder.build()).thenReturn(restTemplate); - IDENTIFIER_1.setCreators(null); } @Test @@ -162,29 +161,28 @@ public class DataCiteIdentifierServiceUnitTest extends BaseUnitTest { @Test public void update_existing_succeeds() - throws IdentifierRequestException, IdentifierNotFoundException { + throws IdentifierRequestException, IdentifierNotFoundException, IdentifierUpdateBadFormException { final DataCiteBody<DataCiteDoi> response = new DataCiteBody<>(new DataCiteData<>(null, "dois", new DataCiteDoi(IDENTIFIER_1_DOI_NOT_NULL))); /* mock */ - when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.class))) + when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierUpdateDto.class))) .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1_WITH_DOI)); when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), any(ParameterizedTypeReference.class), eq(IDENTIFIER_1_DOI_NOT_NULL))) .thenReturn(ResponseEntity.ok(response)); /* test */ - Identifier result = dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO); + Identifier result = dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_DTO_UPDATE_REQUEST); assertTrue(identifierRepository.existsById(IDENTIFIER_1_ID)); assertEquals(IDENTIFIER_1_DOI_NOT_NULL, result.getDoi()); } @Test - public void update_invalidMetadata_fails() - throws IdentifierRequestException, IdentifierNotFoundException { + public void update_invalidMetadata_fails() throws IdentifierUpdateBadFormException, IdentifierNotFoundException { /* mock */ - when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.class))) + when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierUpdateDto.class))) .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1_WITH_DOI)); when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), any(ParameterizedTypeReference.class), eq(IDENTIFIER_1_DOI_NOT_NULL))) @@ -192,17 +190,16 @@ public class DataCiteIdentifierServiceUnitTest extends BaseUnitTest { /* test */ assertThrows(IdentifierRequestException.class, () -> { - dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO); + dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_DTO_UPDATE_REQUEST); }); assertEquals(0, identifierRepository.count()); } @Test - public void update_restClientException_fails() - throws IdentifierRequestException, IdentifierNotFoundException { + public void update_restClientException_fails() throws IdentifierUpdateBadFormException, IdentifierNotFoundException { /* mock */ - when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.class))) + when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierUpdateDto.class))) .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1_WITH_DOI)); when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), any(ParameterizedTypeReference.class), eq(IDENTIFIER_1_DOI_NOT_NULL))) @@ -210,7 +207,7 @@ public class DataCiteIdentifierServiceUnitTest extends BaseUnitTest { /* test */ assertThrows(InternalError.class, () -> { - dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO); + dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_DTO_UPDATE_REQUEST); }); assertEquals(0, identifierRepository.count()); } diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java index 423c014e503edaa826f147ec0302479906af71f9..27bdb8c69b75578f56b203ec6f5fc9b0a570b136 100644 --- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java +++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java @@ -23,11 +23,9 @@ import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; import java.util.List; -import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -76,14 +74,14 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { @BeforeEach public void beforeEach() { - imageRepository.save(IMAGE_1); + imageRepository.save(IMAGE_1_SIMPLE); realmRepository.save(REALM_DBREPO); - userRepository.save(USER_1); - userRepository.save(USER_2); - containerRepository.save(CONTAINER_1); - databaseRepository.save(DATABASE_1); - containerRepository.save(CONTAINER_2); - databaseRepository.save(DATABASE_2); + userRepository.save(USER_1_SIMPLE); + userRepository.save(USER_2_SIMPLE); + containerRepository.save(CONTAINER_1_SIMPLE); + databaseRepository.save(DATABASE_1_SIMPLE); + containerRepository.save(CONTAINER_2_SIMPLE); + databaseRepository.save(DATABASE_2_SIMPLE); identifierRepository.save(IDENTIFIER_1); } @@ -134,42 +132,36 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { } @Test - public void update_notFound_fails() { - - /* test */ - assertThrows(IdentifierNotFoundException.class, () -> { - identifierService.update(IDENTIFIER_2_ID, IDENTIFIER_1_DTO); - }); - } - - @Test - @Transactional(readOnly = true) public void update_succeeds() - throws IdentifierNotFoundException, IdentifierPublishingNotAllowedException, IdentifierRequestException { + throws IdentifierNotFoundException, IdentifierRequestException, IdentifierUpdateBadFormException { /* mock */ when(identifierIdxRepository.save(any(IdentifierDto.class))) .thenReturn(IDENTIFIER_1_DTO); /* test */ - final Identifier response = identifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_DTO); + final Identifier response = identifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_DTO_UPDATE_REQUEST); assertEquals(IDENTIFIER_1_ID, response.getId()); assertEquals(IDENTIFIER_1_DATABASE_ID, response.getDatabaseId()); + assertEquals(IDENTIFIER_1_TITLE_MODIFY, response.getTitle()); assertEquals(IDENTIFIER_1_PUBLICATION_YEAR, response.getPublicationYear()); assertEquals(IDENTIFIER_1_PUBLICATION_MONTH, response.getPublicationMonth()); assertEquals(IDENTIFIER_1_PUBLICATION_DAY, response.getPublicationDay()); } @Test - public void delete_succeeds() throws IdentifierNotFoundException, NotAllowedException { + public void delete_succeeds() throws IdentifierNotFoundException { /* mock */ + when(identifierIdxRepository.existsById(IDENTIFIER_1_ID)) + .thenReturn(true); doNothing() .when(identifierIdxRepository) - .deleteById(IDENTIFIER_1_ID); + .deleteById(IDENTIFIER_1_ID); /* test */ identifierService.delete(IDENTIFIER_1_ID); + assertTrue(userRepository.findById(IDENTIFIER_1_CREATED_BY).isPresent()) /* no cascade of delete */; } @Test diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java index 4eaff8493e39161e29140f89a53a6473357de654..d7e8b28963278b32426509a2bffbe4816018a839 100644 --- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java +++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java @@ -3,14 +3,13 @@ package at.tuwien.service; import at.tuwien.BaseUnitTest; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.identifier.IdentifierDto; -import at.tuwien.api.identifier.VisibilityTypeDto; +import at.tuwien.api.identifier.IdentifierUpdateDto; import at.tuwien.config.IndexInitializer; import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.identifier.IdentifierType; import at.tuwien.exception.*; import at.tuwien.repository.elastic.IdentifierIdxRepository; import at.tuwien.repository.jpa.IdentifierRepository; -import org.apache.http.auth.BasicUserPrincipal; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -24,7 +23,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.web.client.RestTemplate; -import java.security.Principal; import java.util.List; import java.util.Optional; @@ -174,52 +172,6 @@ public class IdentifierServiceUnitTest extends BaseUnitTest { }); } - @Test - public void update_notFound_fails() { - - /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.empty()); - when(identifierRepository.save(IDENTIFIER_1)) - .thenReturn(IDENTIFIER_1); - - /* test */ - assertThrows(IdentifierNotFoundException.class, () -> { - identifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_DTO); - }); - } - - @Test - public void update_doiChange_fails() { - - IdentifierDto identifierWithNewDoiDto = IdentifierDto.builder().id(IDENTIFIER_1_ID).visibility(VisibilityTypeDto.EVERYONE).doi("10.000/thisisadifferentdoi").build(); - - /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); - - /* test */ - assertThrows(IdentifierRequestException.class, () -> { - identifierService.update(IDENTIFIER_1_ID, identifierWithNewDoiDto); - }); - } - - @Test - public void update_notVisibleByEveryone_fails() { - Identifier identifier = Identifier.builder().id(IDENTIFIER_1_ID).build(); - IdentifierDto identifierDto = IdentifierDto.builder().id(IDENTIFIER_1_ID).visibility(VisibilityTypeDto.TRUSTED).build(); - IDENTIFIER_1_DTO.setVisibility(VisibilityTypeDto.TRUSTED); - - /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(identifier)); - - /* test */ - assertThrows(IdentifierRequestException.class, () -> { - identifierService.update(IDENTIFIER_1_ID, identifierDto); - }); - } - @Test public void create_database_succeeds() throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException, @@ -281,11 +233,13 @@ public class IdentifierServiceUnitTest extends BaseUnitTest { } @Test - public void delete_succeeds() throws IdentifierNotFoundException, NotAllowedException { + public void delete_succeeds() throws IdentifierNotFoundException { /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); + when(identifierRepository.existsById(IDENTIFIER_1_ID)) + .thenReturn(true); + when(identifierIdxRepository.existsById(IDENTIFIER_1_ID)) + .thenReturn(true); doNothing() .when(identifierRepository) .delete(IDENTIFIER_1); @@ -313,22 +267,6 @@ public class IdentifierServiceUnitTest extends BaseUnitTest { }); } - @Test - public void delete_withDoi_fails() { - - /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); - doNothing() - .when(identifierRepository) - .delete(IDENTIFIER_1); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - identifierService.delete(IDENTIFIER_1_ID); - }); - } - @Test public void exportMetadata_succeeds() throws IdentifierNotFoundException { diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json index ab803627d4a39d33b65d777a480709d4d18cff9d..d7bb152193d38d9a50d4ab162bbca8a2b23456df 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json +++ b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json @@ -6,7 +6,6 @@ "title": "Sweden weather data", "description": "Selecting all from the weather Sweden table", "doi": null, - "visibility": "everyone", "publisher": "Swedish Government", "publication_day": 14, "publication_month": 7, diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json index be2009147bae92bae99101194a405f5ad6d337e0..347b13ed828b33c4926c680489df7082711746d7 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json +++ b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json @@ -13,7 +13,6 @@ "execution": "2018-11-07T10:59:12.000+00:00", "result_hash": "def", "result_number": 2, - "visibility": "everyone", "publisher": "Austrian Government", "creators": [ { diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java index 0592364d2de1925f1f1298e635c3ddc58d0e0e06..68268cd3c26d62d6ca16b058c7155e02a0f7cbad 100644 --- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/IndexInitializer.java @@ -11,7 +11,6 @@ import org.springframework.context.event.EventListener; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @@ -35,7 +34,6 @@ public class IndexInitializer { this.elasticsearchOperations = elasticsearchOperations; } - @Transactional @EventListener(ApplicationReadyEvent.class) public void initIndex() { log.debug("creating identifierindex"); diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/exception/IdentifierUpdateBadFormException.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/exception/IdentifierUpdateBadFormException.java new file mode 100644 index 0000000000000000000000000000000000000000..884a5d4bc5e0c3edf69eb98833405fd073f1582d --- /dev/null +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/exception/IdentifierUpdateBadFormException.java @@ -0,0 +1,21 @@ +package at.tuwien.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.BAD_REQUEST) +public class IdentifierUpdateBadFormException extends Exception { + + public IdentifierUpdateBadFormException(String msg) { + super(msg); + } + + public IdentifierUpdateBadFormException(String msg, Throwable thr) { + super(msg, thr); + } + + public IdentifierUpdateBadFormException(Throwable thr) { + super(thr); + } + +} diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java index 08fa62a8ec066cf57a1304a13e70e12ae27b9f68..74c61a567618e4c4dd1d55e4ab861b7c5d5f5cfa 100644 --- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java @@ -6,31 +6,38 @@ import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.identifier.IdentifierType; import at.tuwien.entities.identifier.RelatedIdentifier; import org.mapstruct.Mapper; -import org.springframework.transaction.annotation.Transactional; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; @Mapper(componentModel = "spring") public interface IdentifierMapper { - @Transactional IdentifierDto identifierToIdentifierDto(Identifier data); - @Transactional + @Mappings({ + @Mapping(target = "containerId", source = "cid"), + @Mapping(target = "databaseId", source = "dbid"), + @Mapping(target = "queryId", source = "qid"), + }) Identifier identifierCreateDtoToIdentifier(IdentifierCreateDto data); + @Mappings({ + @Mapping(target = "containerId", source = "cid"), + @Mapping(target = "databaseId", source = "dbid"), + @Mapping(target = "queryId", source = "qid"), + }) + Identifier identifierUpdateDtoToIdentifier(IdentifierUpdateDto data); + /* keep */ - @Transactional RelatedIdentifierDto relatedIdentifierToRelatedIdentifierDto(RelatedIdentifier data); - @Transactional Identifier identifierDtoToIdentifier(IdentifierDto data); - @Transactional + /* keep */ Creator creatorDtoToCreator(CreatorDto data); - @Transactional Creator creatorCreateDtoToCreator(CreatorCreateDto data); - @Transactional RelatedIdentifier relatedIdentifierCreateDtoToRelatedIdentifier(RelatedIdentifierCreateDto data); IdentifierType identifierTypeDtoToIdentifierType(IdentifierTypeDto data); diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java index 95071320600402cf97b75013593a429c237489a7..f7bab0d2c2c014670a500d5ce7a7772dc46da4e6 100644 --- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java @@ -2,7 +2,7 @@ package at.tuwien.service; import at.tuwien.api.identifier.BibliographyTypeDto; import at.tuwien.api.identifier.IdentifierCreateDto; -import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.api.identifier.IdentifierUpdateDto; import at.tuwien.entities.identifier.Identifier; import at.tuwien.exception.*; import org.springframework.core.io.InputStreamResource; @@ -108,16 +108,15 @@ public interface IdentifierService { * @param identifierId The identifier id. * @param data The metadata. * @return The updated identifier if successful. - * @throws IdentifierNotFoundException TThe identifier was not found in the metadata database or was deleted. + * @throws IdentifierNotFoundException TThe identifier was not found in the metadata database or was deleted. */ - Identifier update(Long identifierId, IdentifierDto data) throws IdentifierNotFoundException, IdentifierRequestException; + Identifier update(Long identifierId, IdentifierUpdateDto data) throws IdentifierNotFoundException, IdentifierRequestException; /** * Soft-deletes an identifier for a given id in the metadata database. Does not actually remove the entity from the database, but sets it as deleted. * * @param identifierId The identifier id. * @throws IdentifierNotFoundException The identifier was not found in the metadata database or was deleted. - * @throws NotAllowedException Identifiers with a valid DOI cannot be deleted. */ - void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException; + void delete(Long identifierId) throws IdentifierNotFoundException; } diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java index 908eadeefe900044b12e77626196204f9a33f993..a6568c82b87088e78d87fadd73d8973c16b02ecc 100644 --- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java @@ -6,7 +6,7 @@ import at.tuwien.api.datacite.doi.DataCiteCreateDoi; import at.tuwien.api.datacite.doi.DataCiteDoi; import at.tuwien.api.identifier.BibliographyTypeDto; import at.tuwien.api.identifier.IdentifierCreateDto; -import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.api.identifier.IdentifierUpdateDto; import at.tuwien.config.DataCiteConfig; import at.tuwien.config.EndpointConfig; import at.tuwien.entities.identifier.Identifier; @@ -107,17 +107,17 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { } ); - if(response.getStatusCode() != HttpStatus.CREATED || response.getBody() == null) { + if (response.getStatusCode() != HttpStatus.CREATED || response.getBody() == null) { log.error("Could not successfully create DOI. Response: {}", response); throw new IdentifierRequestException("Could not successfully create DOI."); } identifier.setDoi(response.getBody().getData().getAttributes().getDoi()); this.identifierRepository.save(identifier); - } catch(HttpClientErrorException e) { + } catch (HttpClientErrorException e) { log.error("Invalid DOI metadata.", e); throw new IdentifierRequestException("Invalid DOI metadata.", e); - } catch(RestClientException e) { + } catch (RestClientException e) { log.error("Could not fulfil request to DataCite server.", e); throw new InternalError("Could not fulfil request to DataCite server.", e); } @@ -155,10 +155,10 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { @Override @Transactional @javax.transaction.Transactional(rollbackOn = {Exception.class}) - public Identifier update(Long identifierId, IdentifierDto data) + public Identifier update(Long identifierId, IdentifierUpdateDto data) throws IdentifierNotFoundException, IdentifierRequestException { Identifier identifier = identifierService.update(identifierId, data); - if(identifier.getDoi() == null) { + if (identifier.getDoi() == null) { return identifier; } @@ -187,17 +187,17 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { identifier.getDoi() ); - if(response.getStatusCode() != HttpStatus.OK || response.getBody() == null) { + if (response.getStatusCode() != HttpStatus.OK || response.getBody() == null) { log.error("Could not successfully create DOI. Response: {}", response); throw new IdentifierRequestException("Could not successfully create DOI."); } identifier.setDoi(response.getBody().getData().getAttributes().getDoi()); this.identifierRepository.save(identifier); - } catch(HttpClientErrorException e) { + } catch (HttpClientErrorException e) { log.error("Invalid DOI metadata.", e); throw new IdentifierRequestException("Invalid DOI metadata.", e); - } catch(RestClientException e) { + } catch (RestClientException e) { log.error("Could not fulfil request to DataCite server.", e); throw new InternalError("Could not fulfil request to DataCite server.", e); } @@ -207,7 +207,7 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { @Override @Transactional - public void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException { + public void delete(Long identifierId) throws IdentifierNotFoundException { identifierService.delete(identifierId); } diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java index 00b639448bdf491fa20991cacb83ad36cb089b6a..4a3aa4437bc9d982f9dd3754b5599057062ad6e5 100644 --- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java @@ -112,10 +112,8 @@ public class IdentifierServiceImpl implements IdentifierService { } /* identifier */ final Identifier tmp = identifierMapper.identifierCreateDtoToIdentifier(data); - tmp.setContainerId(data.getCid()); - tmp.setDatabaseId(data.getDbid()); final User creator = userService.findByUsername(principal.getName()); - tmp.setCreatedBy(creator.getId()); + tmp.setCreator(creator); tmp.setCreators(List.of()); if (data.getType().equals(IdentifierTypeDto.SUBSET)) { log.debug("identifier describes a subset"); @@ -135,7 +133,7 @@ public class IdentifierServiceImpl implements IdentifierService { .map(c -> { final Creator creatorDto = identifierMapper.creatorCreateDtoToCreator(c); creatorDto.setPid(entity.getId()); - creatorDto.setCreatedBy(creator.getId()); + creatorDto.setCreator(creator); return creatorDto; }) .collect(Collectors.toList())); @@ -145,7 +143,7 @@ public class IdentifierServiceImpl implements IdentifierService { .forEach(r -> { final RelatedIdentifier id = identifierMapper.relatedIdentifierCreateDtoToRelatedIdentifier(r); id.setIid(entity.getId()); - id.setCreatedBy(creator.getId()); + id.setCreator(creator); final RelatedIdentifier relatedIdentifier = relatedIdentifierRepository.save(id); log.debug("identifier add related with id {}", relatedIdentifier.getId()); entity.getRelated().add(relatedIdentifier); @@ -177,7 +175,7 @@ public class IdentifierServiceImpl implements IdentifierService { final Identifier identifier = find(id); /* context */ final Context context = new Context(); - if(identifier.getDoi() != null) { + if (identifier.getDoi() != null) { context.setVariable("identifierType", "DOI"); context.setVariable("identifier", identifier.getDoi()); } else { @@ -207,7 +205,7 @@ public class IdentifierServiceImpl implements IdentifierService { final Identifier identifier = find(id); /* context */ final Context context = new Context(); - if(identifier.getDoi() != null) { + if (identifier.getDoi() != null) { context.setVariable("identifierType", "doi"); context.setVariable("identifier", identifier.getDoi()); } else { @@ -252,20 +250,16 @@ public class IdentifierServiceImpl implements IdentifierService { @Override @Transactional - public Identifier update(Long identifierId, IdentifierDto data) - throws IdentifierNotFoundException, IdentifierRequestException { - /* check */ - Identifier old = find(identifierId); - if(data.getVisibility() != VisibilityTypeDto.EVERYONE) { - throw new IdentifierRequestException("Cannot set visibility to other value than \"EVERYONE\"."); - } - if(data.getDoi() != null && !data.getDoi().equals(old.getDoi())) { - throw new IdentifierRequestException("The DOI of an identifier cannot be changed."); - } + public Identifier update(Long identifierId, IdentifierUpdateDto data) throws IdentifierNotFoundException { /* map */ - final Identifier entity = identifierMapper.identifierDtoToIdentifier(data); - entity.getCreators() - .forEach(creator -> creator.setPid(identifierId)); + final Identifier old = find(identifierId); + final Identifier entity = identifierMapper.identifierUpdateDtoToIdentifier(data); + entity.setId(identifierId); + entity.setCreator(old.getCreator()); + entity.getCreators().forEach(c -> { + c.setPid(identifierId); + c.setCreator(old.getCreator()); + }); /* update */ final Identifier identifier = identifierRepository.save(entity); log.info("Updated identifier with id {}", identifierId); @@ -278,17 +272,17 @@ public class IdentifierServiceImpl implements IdentifierService { @Override @Transactional - public void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException { - /* check */ - final Identifier identifier = find(identifierId); - if(identifier.getDoi() != null) { - throw new NotAllowedException("Identifiers with a DOI cannot be deleted."); + public void delete(Long identifierId) throws IdentifierNotFoundException { + /* delete in metadata database */ + if (!identifierRepository.existsById(identifierId)) { + throw new IdentifierNotFoundException("Identifier not found in metadata database"); } - /* delete */ - identifierRepository.delete(identifier); + identifierRepository.deleteById(identifierId); log.info("Deleted identifier with id {}", identifierId); - log.trace("deleted identifier {}", identifier); - /* elastic search */ + /* delete in elastic search */ + if (!identifierIdxRepository.existsById(identifierId)) { + throw new IdentifierNotFoundException("Identifier not found in metadata database"); + } identifierIdxRepository.deleteById(identifierId); log.info("Deleted identifier with id {} in elastic search", identifierId); } diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java index 1234beec9630665c55479a7c7929e3ffca2a86d9..69e448003e42fcf4be8e0f40ead5b7a62ec7055d 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java @@ -80,10 +80,6 @@ public class IdentifierDto { @Schema(example = "1") private Long resultNumber; - @NotNull - @Schema(example = "everyone") - private VisibilityTypeDto visibility; - @Schema(example = "10.1038/nphys1170") private String doi; diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierUpdateDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierUpdateDto.java new file mode 100644 index 0000000000000000000000000000000000000000..0f1f64b1dfd8b0a62755fe4a12a3494a358497bc --- /dev/null +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierUpdateDto.java @@ -0,0 +1,71 @@ +package at.tuwien.api.identifier; + +import at.tuwien.api.database.LanguageTypeDto; +import at.tuwien.api.database.LicenseDto; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +@Data +@Getter +@Setter +@Builder +public class IdentifierUpdateDto { + + @NotNull + private Long cid; + + @NotNull + private Long dbid; + + private Long qid; + + @Schema(example = "10.1038/nphys1170") + private String doi; + + @NotNull + private IdentifierTypeDto type; + + @NotBlank + @Schema(example = "Airquality Stephansplatz, Vienna, Austria") + private String title; + + @Schema(example = "Air quality reports at Stephansplatz, Vienna") + private String description; + + @JsonProperty("publication_day") + @Schema(example = "15") + private Integer publicationDay; + + @JsonProperty("publication_month") + @Schema(example = "12") + private Integer publicationMonth; + + @Schema(example = "TU Wien") + private String publisher; + + private LanguageTypeDto language; + + private LicenseDto license; + + @NotNull + @JsonProperty("publication_year") + @Schema(example = "2022") + private Integer publicationYear; + + @NotNull + @NotEmpty + private List<CreatorDto> creators; + + @JsonProperty("related_identifiers") + private List<RelatedIdentifierCreateDto> relatedIdentifiers; + +} diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/VisibilityTypeDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/VisibilityTypeDto.java deleted file mode 100644 index 25739d3d4d45cabcc32504807338dfb2d2252098..0000000000000000000000000000000000000000 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/VisibilityTypeDto.java +++ /dev/null @@ -1,29 +0,0 @@ -package at.tuwien.api.identifier; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Getter; -import lombok.ToString; - -@Getter -public enum VisibilityTypeDto { - - @JsonProperty("everyone") - EVERYONE("everyone"), - - @JsonProperty("trusted") - TRUSTED("trusted"), - - @JsonProperty("self") - SELF("self"); - - private String name; - - VisibilityTypeDto(String name) { - this.name = name; - } - - @Override - public String toString() { - return this.name; - } -} \ No newline at end of file diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/Table.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/Table.java index 6b9ee532fcbd3685382a6ea1ba4f877547836fd0..e05a1e03ae060f17bfec5da1027d3141104fa096 100644 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/Table.java +++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/Table.java @@ -7,16 +7,13 @@ import at.tuwien.entities.user.User; import lombok.*; import net.sf.jsqlparser.statement.select.FromItem; import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.Type; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; import java.time.Instant; import java.util.List; -import java.util.UUID; @Data @Entity @@ -42,14 +39,9 @@ public class Table { @EqualsAndHashCode.Include private Long tdbid; - @ToString.Exclude - @Column(name = "createdBy", nullable = false, columnDefinition = "VARCHAR(36)") - @Type(type = "uuid-char") - private UUID createdBy; - - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) @JoinColumns({ - @JoinColumn(name = "createdBy", referencedColumnName = "ID", updatable = false, insertable = false) + @JoinColumn(name = "createdBy", referencedColumnName = "ID", nullable = false, columnDefinition = "VARCHAR(36)", updatable = false) }) private User creator; @@ -69,11 +61,11 @@ public class Table { private String description; @ToString.Exclude - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + @ManyToOne(fetch = FetchType.LAZY, cascade = {}) @JoinColumn(name = "tdbid", insertable = false, updatable = false) private Database database; - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE, mappedBy = "table") + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "table") @OrderBy("ordinalPosition") private List<TableColumn> columns; @@ -90,7 +82,8 @@ public class Table { @PreRemove public void preRemove() { - this.database = null; + this.creator = null; + this.columns.forEach(c -> c.setCreator(null)); } /** diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java index 1c950898300c86d461518a7fe1dccf82ad5d5d43..02ad037bd67f2247460bd268af91d674e8b81c70 100644 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java +++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java @@ -60,14 +60,9 @@ public class TableColumn implements Comparable<TableColumn> { }) private Table table; - @ToString.Exclude - @Column(name = "createdBy", nullable = false, columnDefinition = "VARCHAR(36)") - @Type(type = "uuid-char") - private UUID createdBy; - - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) @JoinColumns({ - @JoinColumn(name = "createdBy", referencedColumnName = "ID", insertable = false, updatable = false) + @JoinColumn(name = "createdBy", referencedColumnName = "ID", nullable = false, columnDefinition = "VARCHAR(36)", updatable = false) }) private User creator; diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Creator.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Creator.java index 2b0a720cb7f0acc71a9a946d950e792e4af4faea..740fe5a4e5cbf278d49e56942287b4bcbe0e17e4 100644 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Creator.java +++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Creator.java @@ -56,14 +56,9 @@ public class Creator { @CreatedDate private Instant created; - @ToString.Exclude - @Column(name = "createdBy", nullable = false, columnDefinition = "VARCHAR(36)") - @Type(type = "uuid-char") - private UUID createdBy; - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumns({ - @JoinColumn(name = "createdBy", referencedColumnName = "ID", insertable = false, updatable = false) + @JoinColumn(name = "createdBy", referencedColumnName = "ID", nullable = false, columnDefinition = "VARCHAR(36)", updatable = false) }) private User creator; diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java index 66a68c8ff7a1df41c0ce13ef7773ee530cf2afa6..294a9c954db51f0144222de26f07e38e458be02e 100644 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java +++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java @@ -6,7 +6,6 @@ import at.tuwien.entities.database.License; import at.tuwien.entities.user.User; import lombok.*; import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.Type; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -16,7 +15,6 @@ import javax.validation.constraints.NotBlank; import java.io.Serializable; import java.time.Instant; import java.util.List; -import java.util.UUID; @Data @Entity @@ -46,14 +44,9 @@ public class Identifier implements Serializable { @Column(name = "qid") private Long queryId; - @ToString.Exclude - @Column(name = "createdBy", nullable = false, columnDefinition = "VARCHAR(36)") - @Type(type = "uuid-char") - private UUID createdBy; - - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) @JoinColumns({ - @JoinColumn(name = "createdBy", referencedColumnName = "ID", insertable = false, updatable = false) + @JoinColumn(name = "createdBy", referencedColumnName = "ID", nullable = false, columnDefinition = "VARCHAR(36)", updatable = false) }) private User creator; @@ -108,18 +101,13 @@ public class Identifier implements Serializable { @Column private Integer publicationDay; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + @OneToOne(fetch = FetchType.LAZY, cascade = {}) @JoinColumns({ @JoinColumn(name = "dbid", referencedColumnName = "id", insertable = false, updatable = false) }) private Database database; - @Column(nullable = false, columnDefinition = "enum('EVERYONE', 'TRUSTED', 'SELF')") - @Enumerated(EnumType.STRING) - @Builder.Default - private VisibilityType visibility = VisibilityType.EVERYONE; - - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumns({ @JoinColumn(name = "iid", referencedColumnName = "id", insertable = false, updatable = false) }) @@ -128,7 +116,7 @@ public class Identifier implements Serializable { @Column private String doi; - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "identifier") + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "identifier") private List<Creator> creators; @Column(nullable = false, updatable = false) @@ -139,6 +127,13 @@ public class Identifier implements Serializable { @LastModifiedDate private Instant lastModified; + @PreRemove + private void preRemove() { + this.creator = null; + this.related.forEach(r -> r.setCreator(null)); + this.creators.forEach(c -> c.setCreator(null)); + } + } diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/RelatedIdentifier.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/RelatedIdentifier.java index aed25776562e7f858629750f58af2c41fd517e42..54b90615a794dba57c251e1fe622704507f1d448 100644 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/RelatedIdentifier.java +++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/RelatedIdentifier.java @@ -50,14 +50,9 @@ public class RelatedIdentifier { @Enumerated(EnumType.STRING) private RelationType relation; - @ToString.Exclude - @Column(name = "created_by", nullable = false, columnDefinition = "VARCHAR(36)") - @Type(type = "uuid-char") - private UUID createdBy; - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumns({ - @JoinColumn(name = "created_by", referencedColumnName = "ID", insertable = false, updatable = false) + @JoinColumn(name = "created_by", referencedColumnName = "ID", nullable = false, columnDefinition = "VARCHAR(36)", updatable = false) }) private User creator; diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/VisibilityType.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/VisibilityType.java deleted file mode 100644 index 029ccb3994475eb9f934e2e568e6b8371e9261ae..0000000000000000000000000000000000000000 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/VisibilityType.java +++ /dev/null @@ -1,12 +0,0 @@ -package at.tuwien.entities.identifier; - -import lombok.Getter; -import lombok.ToString; - -@Getter -@ToString -public enum VisibilityType { - EVERYONE, - TRUSTED, - SELF; -} \ No newline at end of file diff --git a/dbrepo-metadata-db/setup-schema.sql b/dbrepo-metadata-db/setup-schema.sql index b78ca3ab39f8360b1fbac1399ae98c83e7c64bd0..31eefbbbd3700c3be6724c81caac9b7c4cb643a1 100644 --- a/dbrepo-metadata-db/setup-schema.sql +++ b/dbrepo-metadata-db/setup-schema.sql @@ -357,7 +357,6 @@ CREATE TABLE IF NOT EXISTS `fda`.`mdb_identifiers` language VARCHAR(50), license VARCHAR(50), description TEXT, - visibility ENUM ('SELF', 'TRUSTED', 'EVERYONE') NOT NULL, publication_year INTEGER NOT NULL, publication_month INTEGER, publication_day INTEGER, diff --git a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java index 56bdd5503d8a2ac0ed37cd216a1dd9cad8273de3..43b2d75a3aebe49d85f0d5197539597bee790cc0 100644 --- a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java @@ -116,7 +116,7 @@ public abstract class BaseTest { "create-identifier", "find-identifier", "list-identifiers"}; public final static String[] ESCALATED_IDENTIFIER_HANDLING = new String[]{"escalated-identifier-handling", - "modify-identifier-metadata", "delete-identifier"}; + "modify-identifier-metadata", "delete-identifier", "update-foreign-identifier", "create-foreign-identifier"}; public final static String[] DEFAULT_QUERY_HANDLING = new String[]{"default-query-handling", "view-table-data", "execute-query", "view-table-history", "list-database-views", "list-queries", "view-database-view-data", @@ -1496,6 +1496,8 @@ public abstract class BaseTest { public final static String TABLE_1_DESCRIPTION = "Weather in the world"; public final static String TABLE_1_QUEUE_NAME = DATABASE_1_EXCHANGE + "." + TABLE_1_INTERNALNAME; public final static String TABLE_1_ROUTING_KEY = TABLE_1_QUEUE_NAME; + public final static UUID TABLE_1_CREATED_BY = USER_1_ID; + public final static Long TABLE_1_DATABASE_ID = DATABASE_1_ID; public final static Instant TABLE_1_CREATED = Instant.ofEpochSecond(1677399975) /* 2023-02-26 08:26:15 (UTC) */; public final static Instant TABLE_1_LAST_MODIFIED = Instant.ofEpochSecond(1677399975) /* 2023-02-26 08:26:15 (UTC) */; @@ -1505,6 +1507,8 @@ public abstract class BaseTest { public final static String TABLE_2_DESCRIPTION = "Weather location"; public final static String TABLE_2_QUEUE_NAME = DATABASE_1_EXCHANGE + "." + TABLE_2_INTERNALNAME; public final static String TABLE_2_ROUTING_KEY = TABLE_2_QUEUE_NAME; + public final static UUID TABLE_2_CREATED_BY = USER_1_ID; + public final static Long TABLE_2_DATABASE_ID = DATABASE_1_ID; public final static Instant TABLE_2_CREATED = Instant.ofEpochSecond(1677400007) /* 2023-02-26 08:26:47 (UTC) */; public final static Instant TABLE_2_LAST_MODIFIED = Instant.ofEpochSecond(1677400007) /* 2023-02-26 08:26:47 (UTC) */; @@ -1514,6 +1518,8 @@ public abstract class BaseTest { public final static String TABLE_3_DESCRIPTION = "https://www.kaggle.com/laa283/zurich-public-transport/version/2"; public final static String TABLE_3_QUEUE_NAME = DATABASE_1_EXCHANGE + "." + TABLE_3_INTERNALNAME; public final static String TABLE_3_ROUTING_KEY = TABLE_3_QUEUE_NAME; + public final static UUID TABLE_3_CREATED_BY = USER_1_ID; + public final static Long TABLE_3_DATABASE_ID = DATABASE_1_ID; public final static Instant TABLE_3_CREATED = Instant.ofEpochSecond(1677400031) /* 2023-02-26 08:27:11 (UTC) */; public final static Instant TABLE_3_LAST_MODIFIED = Instant.ofEpochSecond(1677400031) /* 2023-02-26 08:27:11 (UTC) */; @@ -1589,7 +1595,7 @@ public abstract class BaseTest { .id(1L) .ordinalPosition(0) .cdbid(DATABASE_1_ID) - .tid(TABLE_1_ID) + .tid(TABLE_7_ID) .name("Timestamp") .internalName("timestamp") .columnType(TableColumnType.TIMESTAMP) @@ -1597,12 +1603,13 @@ public abstract class BaseTest { .isNullAllowed(false) .autoGenerated(false) .isPrimaryKey(true) + .creator(USER_1) .build(), TableColumn.builder() .id(2L) .ordinalPosition(1) .cdbid(DATABASE_1_ID) - .tid(TABLE_1_ID) + .tid(TABLE_7_ID) .name("Value") .internalName("value") .columnType(TableColumnType.DECIMAL) @@ -1610,6 +1617,7 @@ public abstract class BaseTest { .isNullAllowed(true) .autoGenerated(false) .isPrimaryKey(false) + .creator(USER_1) .build()); public final static Table TABLE_7 = Table.builder() @@ -1621,7 +1629,6 @@ public abstract class BaseTest { .tdbid(DATABASE_1_ID) .queueName(TABLE_7_QUEUE_NAME) .routingKey(TABLE_7_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(TABLE_7_COLUMNS) .creator(USER_1) .created(TABLE_7_CREATED) @@ -1637,7 +1644,6 @@ public abstract class BaseTest { .tdbid(DATABASE_1_ID) .queueName(TABLE_7_QUEUE_NAME) .routingKey(TABLE_7_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(List.of() /* for jpa */) .creator(null /* for jpa */) .created(TABLE_7_CREATED) @@ -2236,6 +2242,7 @@ public abstract class BaseTest { .isNullAllowed(COLUMN_8_1_NULL) .autoGenerated(COLUMN_8_1_AUTO_GENERATED) .isPrimaryKey(COLUMN_8_1_PRIMARY) + .creator(USER_3) .build(), TableColumn.builder() .id(COLUMN_8_2_ID) @@ -2249,6 +2256,7 @@ public abstract class BaseTest { .isNullAllowed(COLUMN_8_2_NULL) .autoGenerated(COLUMN_8_2_AUTO_GENERATED) .isPrimaryKey(COLUMN_8_2_PRIMARY) + .creator(USER_3) .build()); public final static Table TABLE_8 = Table.builder() @@ -2261,7 +2269,6 @@ public abstract class BaseTest { .queueName(TABLE_8_QUEUE_NAME) .routingKey(TABLE_8_ROUTING_KEY) .columns(TABLE_8_COLUMNS) - .createdBy(USER_1_ID) .creator(USER_1) .created(TABLE_8_CREATED) .lastModified(TABLE_8_LAST_MODIFIED) @@ -2560,6 +2567,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_1_1_AUTO_GENERATED) .isPrimaryKey(COLUMN_1_1_PRIMARY) .enumValues(COLUMN_1_1_ENUM_VALUES) + .creator(USER_1) .build(), TableColumn.builder() .id(COLUMN_1_2_ID) @@ -2575,6 +2583,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_1_2_AUTO_GENERATED) .isPrimaryKey(COLUMN_1_2_PRIMARY) .enumValues(COLUMN_1_2_ENUM_VALUES) + .creator(USER_1) .build(), TableColumn.builder() .id(COLUMN_1_3_ID) @@ -2589,6 +2598,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_1_3_AUTO_GENERATED) .isPrimaryKey(COLUMN_1_3_PRIMARY) .enumValues(COLUMN_1_3_ENUM_VALUES) + .creator(USER_1) .build(), TableColumn.builder() .id(COLUMN_1_4_ID) @@ -2603,6 +2613,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_1_4_AUTO_GENERATED) .isPrimaryKey(COLUMN_1_4_PRIMARY) .enumValues(COLUMN_1_4_ENUM_VALUES) + .creator(USER_1) .build(), TableColumn.builder() .id(COLUMN_1_5_ID) @@ -2617,6 +2628,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_1_5_AUTO_GENERATED) .isPrimaryKey(COLUMN_1_5_PRIMARY) .enumValues(COLUMN_1_5_ENUM_VALUES) + .creator(USER_1) .build()); public final static Table TABLE_1 = Table.builder() @@ -2630,7 +2642,6 @@ public abstract class BaseTest { .tdbid(DATABASE_1_ID) .queueName(TABLE_1_QUEUE_NAME) .routingKey(TABLE_1_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(TABLE_1_COLUMNS) .constraints(null) /* TABLE_1_CONSTRAINTS */ .creator(USER_1) @@ -2649,7 +2660,6 @@ public abstract class BaseTest { .tdbid(DATABASE_1_ID) .queueName(TABLE_1_QUEUE_NAME) .routingKey(TABLE_1_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(List.of() /* for jpa */) .constraints(null /* for jpa */) /* TABLE_1_CONSTRAINTS */ .creator(null /* for jpa */) @@ -2670,6 +2680,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_2_1_AUTO_GENERATED) .isPrimaryKey(COLUMN_2_1_PRIMARY) .enumValues(COLUMN_2_1_ENUM_VALUES) + .creator(USER_1) .build(), TableColumn.builder() .id(COLUMN_2_2_ID) @@ -2684,6 +2695,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_2_2_AUTO_GENERATED) .isPrimaryKey(COLUMN_2_2_PRIMARY) .enumValues(COLUMN_2_2_ENUM_VALUES) + .creator(USER_1) .build(), TableColumn.builder() .id(COLUMN_2_3_ID) @@ -2698,6 +2710,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_2_3_AUTO_GENERATED) .isPrimaryKey(COLUMN_2_3_PRIMARY) .enumValues(COLUMN_2_3_ENUM_VALUES) + .creator(USER_1) .build()); public final static Table TABLE_2 = Table.builder() @@ -2711,7 +2724,6 @@ public abstract class BaseTest { .tdbid(DATABASE_1_ID) .queueName(TABLE_2_QUEUE_NAME) .routingKey(TABLE_2_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(TABLE_2_COLUMNS) .creator(USER_1) .created(TABLE_2_CREATED) @@ -2729,7 +2741,6 @@ public abstract class BaseTest { .tdbid(DATABASE_1_ID) .queueName(TABLE_2_QUEUE_NAME) .routingKey(TABLE_2_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(List.of() /* for jpa */) .creator(null /* for jpa */) .created(TABLE_2_CREATED) @@ -2763,6 +2774,7 @@ public abstract class BaseTest { .isPrimaryKey(true) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2776,6 +2788,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2789,6 +2802,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2802,6 +2816,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dfid(IMAGE_DATE_2_ID) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2815,6 +2830,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2828,6 +2844,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2841,6 +2858,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2854,6 +2872,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2867,6 +2886,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2880,6 +2900,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2893,6 +2914,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dfid(IMAGE_DATE_2_ID) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2906,6 +2928,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2919,6 +2942,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2932,6 +2956,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2945,6 +2970,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2958,6 +2984,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2971,6 +2998,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2984,6 +3012,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -2997,6 +3026,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3010,6 +3040,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dfid(IMAGE_DATE_2_ID) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3023,6 +3054,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3036,6 +3068,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3049,6 +3082,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3062,6 +3096,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3075,6 +3110,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3088,6 +3124,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3101,6 +3138,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3114,6 +3152,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3127,6 +3166,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3140,6 +3180,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3153,6 +3194,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3166,6 +3208,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3179,6 +3222,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3192,6 +3236,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build(), TableColumn.builder() .tid(TABLE_3_ID) @@ -3205,6 +3250,7 @@ public abstract class BaseTest { .isPrimaryKey(false) .dateFormat(null) .enumValues(null) + .creator(USER_1) .build()); public final static Constraints TABLE_3_CONSTRAINTS = Constraints.builder() @@ -3224,7 +3270,6 @@ public abstract class BaseTest { .routingKey(TABLE_3_ROUTING_KEY) .columns(TABLE_3_COLUMNS) .constraints(TABLE_3_CONSTRAINTS) - .createdBy(USER_1_ID) .creator(USER_1) .created(TABLE_3_CREATED) .lastModified(TABLE_3_LAST_MODIFIED) @@ -3243,7 +3288,6 @@ public abstract class BaseTest { .routingKey(TABLE_3_ROUTING_KEY) .columns(List.of() /* for jpa */) .constraints(TABLE_3_CONSTRAINTS) - .createdBy(USER_1_ID) .creator(null /* for jpa */) .created(TABLE_3_CREATED) .lastModified(TABLE_3_LAST_MODIFIED) @@ -3262,6 +3306,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_1_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_1_PRIMARY) .enumValues(COLUMN_4_1_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_2_ID) @@ -3276,6 +3321,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_2_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_2_PRIMARY) .enumValues(COLUMN_4_2_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_3_ID) @@ -3290,6 +3336,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_3_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_3_PRIMARY) .enumValues(COLUMN_4_3_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_4_ID) @@ -3304,6 +3351,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_4_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_4_PRIMARY) .enumValues(COLUMN_4_4_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_5_ID) @@ -3318,6 +3366,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_5_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_5_PRIMARY) .enumValues(COLUMN_4_5_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_6_ID) @@ -3332,6 +3381,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_6_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_6_PRIMARY) .enumValues(COLUMN_4_6_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_7_ID) @@ -3346,6 +3396,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_7_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_7_PRIMARY) .enumValues(COLUMN_4_7_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_8_ID) @@ -3360,6 +3411,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_8_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_8_PRIMARY) .enumValues(COLUMN_4_8_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_9_ID) @@ -3374,6 +3426,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_9_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_9_PRIMARY) .enumValues(COLUMN_4_9_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_10_ID) @@ -3388,6 +3441,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_10_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_10_PRIMARY) .enumValues(COLUMN_4_10_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_11_ID) @@ -3402,6 +3456,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_11_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_11_PRIMARY) .enumValues(COLUMN_4_11_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_12_ID) @@ -3416,6 +3471,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_12_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_12_PRIMARY) .enumValues(COLUMN_4_12_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_13_ID) @@ -3430,6 +3486,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_13_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_13_PRIMARY) .enumValues(COLUMN_4_13_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_14_ID) @@ -3444,6 +3501,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_14_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_14_PRIMARY) .enumValues(COLUMN_4_14_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_15_ID) @@ -3458,6 +3516,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_15_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_15_PRIMARY) .enumValues(COLUMN_4_15_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_16_ID) @@ -3472,6 +3531,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_16_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_16_PRIMARY) .enumValues(COLUMN_4_16_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_17_ID) @@ -3486,6 +3546,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_17_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_17_PRIMARY) .enumValues(COLUMN_4_17_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_18_ID) @@ -3500,6 +3561,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_18_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_18_PRIMARY) .enumValues(COLUMN_4_18_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_19_ID) @@ -3514,6 +3576,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_19_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_19_PRIMARY) .enumValues(COLUMN_4_19_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_20_ID) @@ -3528,6 +3591,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_20_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_20_PRIMARY) .enumValues(COLUMN_4_20_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_4_21_ID) @@ -3542,6 +3606,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_4_21_AUTO_GENERATED) .isPrimaryKey(COLUMN_4_21_PRIMARY) .enumValues(COLUMN_4_21_ENUM_VALUES) + .creator(USER_2) .build()); public final static Constraints TABLE_4_CONSTRAINTS = Constraints.builder() @@ -3560,7 +3625,6 @@ public abstract class BaseTest { .routingKey(TABLE_4_ROUTING_KEY) .columns(TABLE_4_COLUMNS) .constraints(TABLE_4_CONSTRAINTS) - .createdBy(USER_1_ID) .creator(USER_1) .build(); @@ -3627,6 +3691,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_5_1_AUTO_GENERATED) .isPrimaryKey(COLUMN_5_1_PRIMARY) .enumValues(COLUMN_5_1_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_5_2_ID) @@ -3641,6 +3706,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_5_2_AUTO_GENERATED) .isPrimaryKey(COLUMN_5_2_PRIMARY) .enumValues(COLUMN_5_2_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_5_3_ID) @@ -3655,6 +3721,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_5_3_AUTO_GENERATED) .isPrimaryKey(COLUMN_5_3_PRIMARY) .enumValues(COLUMN_5_3_ENUM_VALUES) + .creator(USER_2) .build()); public final static Constraints TABLE_5_CONSTRAINTS = Constraints.builder() @@ -3673,7 +3740,6 @@ public abstract class BaseTest { .routingKey(TABLE_5_ROUTING_KEY) .columns(TABLE_5_COLUMNS) .constraints(TABLE_5_CONSTRAINTS) - .createdBy(USER_1_ID) .creator(USER_1) .created(TABLE_5_CREATED) .lastModified(TABLE_5_LAST_MODIFIED) @@ -3778,6 +3844,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_6_1_AUTO_GENERATED) .isPrimaryKey(COLUMN_6_1_PRIMARY) .enumValues(COLUMN_6_1_ENUM_VALUES) + .creator(USER_2) .build(), TableColumn.builder() .id(COLUMN_6_2_ID) @@ -3792,6 +3859,7 @@ public abstract class BaseTest { .autoGenerated(COLUMN_6_2_AUTO_GENERATED) .isPrimaryKey(COLUMN_6_2_PRIMARY) .enumValues(COLUMN_6_2_ENUM_VALUES) + .creator(USER_2) .build()); public final static Table TABLE_6 = Table.builder() @@ -3804,7 +3872,6 @@ public abstract class BaseTest { .tdbid(DATABASE_2_ID) .queueName(TABLE_6_QUEUE_NAME) .routingKey(TABLE_6_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(TABLE_6_COLUMNS) .creator(USER_1) .created(TABLE_6_CREATED) @@ -3821,7 +3888,6 @@ public abstract class BaseTest { .tdbid(DATABASE_1_ID) .queueName(TABLE_7_QUEUE_NAME) .routingKey(TABLE_7_ROUTING_KEY) - .createdBy(USER_1_ID) .columns(List.of()) .creator(USER_1) .build(); @@ -4056,8 +4122,6 @@ public abstract class BaseTest { public final static String IDENTIFIER_1_TITLE_MODIFY = "Austrian weather some data"; public final static String IDENTIFIER_1_DOI = null; public final static String IDENTIFIER_1_DOI_NOT_NULL = "10.1000/183"; - public final static VisibilityType IDENTIFIER_1_VISIBILITY = VisibilityType.EVERYONE; - public final static VisibilityTypeDto IDENTIFIER_1_VISIBILITY_DTO = VisibilityTypeDto.EVERYONE; public final static Instant IDENTIFIER_1_CREATED = Instant.ofEpochSecond(1641588352) /* 2022-01-07 20:45:52 */; public final static Instant IDENTIFIER_1_MODIFIED = Instant.ofEpochSecond(1541588352) /* 2022-01-07 20:45:52 */; public final static Instant IDENTIFIER_1_EXECUTION = Instant.ofEpochSecond(1541588352) /* 2022-01-07 20:45:52 */; @@ -4082,7 +4146,6 @@ public abstract class BaseTest { .lastname(CREATOR_1_LASTNAME) .orcid(CREATOR_1_ORCID) .affiliation(CREATOR_1_AFFIL) - .createdBy(IDENTIFIER_1_CREATED_BY) .creator(IDENTIFIER_1_CREATOR) .build(); @@ -4102,7 +4165,6 @@ public abstract class BaseTest { .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) .doi(IDENTIFIER_1_DOI) - .visibility(IDENTIFIER_1_VISIBILITY) .created(IDENTIFIER_1_CREATED) .lastModified(IDENTIFIER_1_MODIFIED) .execution(IDENTIFIER_1_EXECUTION) @@ -4115,7 +4177,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_1_RESULT_NUMBER) .publisher(IDENTIFIER_1_PUBLISHER) .type(IDENTIFIER_1_TYPE) - .createdBy(USER_1_ID) .creator(USER_1) .creators(List.of(IDENTIFIER_1_CREATOR_1)) .build(); @@ -4128,7 +4189,6 @@ public abstract class BaseTest { .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) .doi(IDENTIFIER_1_DOI) - .visibility(IDENTIFIER_1_VISIBILITY) .created(IDENTIFIER_1_CREATED) .lastModified(IDENTIFIER_1_MODIFIED) .execution(IDENTIFIER_1_EXECUTION) @@ -4141,7 +4201,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_1_RESULT_NUMBER) .publisher(IDENTIFIER_1_PUBLISHER) .type(IDENTIFIER_1_TYPE) - .createdBy(USER_1_ID) .creator(null /* for jpa */) .creators(List.of() /* for jpa */) .build(); @@ -4154,7 +4213,6 @@ public abstract class BaseTest { .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) .doi(IDENTIFIER_1_DOI_NOT_NULL) - .visibility(IDENTIFIER_1_VISIBILITY) .created(IDENTIFIER_1_CREATED) .lastModified(IDENTIFIER_1_MODIFIED) .execution(IDENTIFIER_1_EXECUTION) @@ -4168,7 +4226,6 @@ public abstract class BaseTest { .publisher(IDENTIFIER_1_PUBLISHER) .type(IDENTIFIER_1_TYPE) .creator(USER_1) - .createdBy(USER_1_ID) .creators(List.of(IDENTIFIER_1_CREATOR_1)) .build(); @@ -4180,7 +4237,6 @@ public abstract class BaseTest { .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) .doi(IDENTIFIER_1_DOI) - .visibility(IDENTIFIER_1_VISIBILITY_DTO) .created(IDENTIFIER_1_CREATED) .lastModified(IDENTIFIER_1_MODIFIED) .execution(IDENTIFIER_1_EXECUTION) @@ -4205,7 +4261,6 @@ public abstract class BaseTest { .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) .doi(IDENTIFIER_1_DOI_NOT_NULL) - .visibility(IDENTIFIER_1_VISIBILITY_DTO) .created(IDENTIFIER_1_CREATED) .lastModified(IDENTIFIER_1_MODIFIED) .execution(IDENTIFIER_1_EXECUTION) @@ -4229,8 +4284,6 @@ public abstract class BaseTest { public final static String IDENTIFIER_2_DESCRIPTION = "Selecting all from the weather Austria table"; public final static String IDENTIFIER_2_TITLE = "Australian weather data"; public final static String IDENTIFIER_2_DOI = null; - public final static VisibilityType IDENTIFIER_2_VISIBILITY = VisibilityType.EVERYONE; - public final static VisibilityTypeDto IDENTIFIER_2_VISIBILITY_DTO = VisibilityTypeDto.EVERYONE; public final static Instant IDENTIFIER_2_CREATED = Instant.ofEpochSecond(1641588352); public final static Instant IDENTIFIER_2_MODIFIED = Instant.ofEpochSecond(1541588352); public final static Instant IDENTIFIER_2_EXECUTION = Instant.ofEpochSecond(1541588352); @@ -4248,38 +4301,40 @@ public abstract class BaseTest { public final static UUID IDENTIFIER_2_CREATED_BY = USER_2_ID; public final static User IDENTIFIER_2_CREATOR = USER_2; + public final static Long IDENTIFIER_2_CREATOR_1_ID = 2L; + public final static Creator IDENTIFIER_2_CREATOR_1 = Creator.builder() - .id(CREATOR_1_ID) + .id(IDENTIFIER_2_CREATOR_1_ID) .pid(IDENTIFIER_2_ID) .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .orcid(CREATOR_1_ORCID) .affiliation(CREATOR_1_AFFIL) - .createdBy(IDENTIFIER_2_CREATED_BY) .creator(IDENTIFIER_2_CREATOR) .build(); public final static CreatorDto IDENTIFIER_2_CREATOR_1_DTO = CreatorDto.builder() - .id(CREATOR_1_ID) + .id(IDENTIFIER_2_CREATOR_1_ID) .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .orcid(CREATOR_1_ORCID) .affiliation(CREATOR_1_AFFIL) .build(); + public final static Long IDENTIFIER_2_CREATOR_2_ID = 3L; + public final static Creator IDENTIFIER_2_CREATOR_2 = Creator.builder() - .id(CREATOR_2_ID) + .id(IDENTIFIER_2_CREATOR_2_ID) .pid(IDENTIFIER_2_ID) .firstname(CREATOR_2_FIRSTNAME) .lastname(CREATOR_2_LASTNAME) .orcid(CREATOR_2_ORCID) .affiliation(CREATOR_2_AFFIL) - .createdBy(IDENTIFIER_2_CREATED_BY) .creator(IDENTIFIER_2_CREATOR) .build(); public final static CreatorDto IDENTIFIER_2_CREATOR_2_DTO = CreatorDto.builder() - .id(CREATOR_2_ID) + .id(IDENTIFIER_2_CREATOR_2_ID) .firstname(CREATOR_2_FIRSTNAME) .lastname(CREATOR_2_LASTNAME) .orcid(CREATOR_2_ORCID) @@ -4294,7 +4349,6 @@ public abstract class BaseTest { .description(IDENTIFIER_2_DESCRIPTION) .title(IDENTIFIER_2_TITLE) .doi(IDENTIFIER_2_DOI) - .visibility(IDENTIFIER_2_VISIBILITY) .created(IDENTIFIER_2_CREATED) .lastModified(IDENTIFIER_2_MODIFIED) .execution(IDENTIFIER_2_EXECUTION) @@ -4308,7 +4362,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_2_RESULT_NUMBER) .publisher(IDENTIFIER_2_PUBLISHER) .type(IDENTIFIER_2_TYPE) - .createdBy(USER_2_ID) .creator(USER_2) .creators(List.of(IDENTIFIER_2_CREATOR_1, IDENTIFIER_2_CREATOR_2)) .build(); @@ -4321,7 +4374,6 @@ public abstract class BaseTest { .description(IDENTIFIER_2_DESCRIPTION) .title(IDENTIFIER_2_TITLE) .doi(IDENTIFIER_2_DOI) - .visibility(IDENTIFIER_2_VISIBILITY) .created(IDENTIFIER_2_CREATED) .lastModified(IDENTIFIER_2_MODIFIED) .execution(IDENTIFIER_2_EXECUTION) @@ -4335,7 +4387,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_2_RESULT_NUMBER) .publisher(IDENTIFIER_2_PUBLISHER) .type(IDENTIFIER_2_TYPE) - .createdBy(USER_2_ID) .creator(null /* for jpa */) .creators(List.of() /* for jpa */) .build(); @@ -4348,7 +4399,6 @@ public abstract class BaseTest { .description(IDENTIFIER_2_DESCRIPTION) .title(IDENTIFIER_2_TITLE) .doi(IDENTIFIER_2_DOI) - .visibility(IDENTIFIER_2_VISIBILITY_DTO) .created(IDENTIFIER_2_CREATED) .lastModified(IDENTIFIER_2_MODIFIED) .execution(IDENTIFIER_2_EXECUTION) @@ -4444,7 +4494,6 @@ public abstract class BaseTest { .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) .type(IDENTIFIER_1_TYPE_DTO) - .visibility(IDENTIFIER_1_VISIBILITY_DTO) .created(IDENTIFIER_1_CREATED) .lastModified(IDENTIFIER_1_MODIFIED) .creators(List.of(CREATOR_1_DTO)) @@ -4463,28 +4512,16 @@ public abstract class BaseTest { .type(IDENTIFIER_1_TYPE_DTO) .build(); - public final static IdentifierCreateDto IDENTIFIER_1_DTO_TRUSTED_REQUEST = IdentifierCreateDto.builder() + public final static IdentifierUpdateDto IDENTIFIER_1_DTO_UPDATE_REQUEST = IdentifierUpdateDto.builder() .cid(IDENTIFIER_1_CONTAINER_ID) .dbid(IDENTIFIER_1_DATABASE_ID) .description(IDENTIFIER_1_DESCRIPTION) - .title(IDENTIFIER_1_TITLE) - .relatedIdentifiers(List.of()) - .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) - .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) - .creators(List.of(CREATOR_1_CREATE_DTO)) - .publisher(IDENTIFIER_1_PUBLISHER) - .type(IDENTIFIER_1_TYPE_DTO) - .build(); - - public final static IdentifierCreateDto IDENTIFIER_1_DTO_SELF_REQUEST = IdentifierCreateDto.builder() - .cid(IDENTIFIER_1_CONTAINER_ID) - .dbid(IDENTIFIER_1_DATABASE_ID) - .description(IDENTIFIER_1_DESCRIPTION) - .title(IDENTIFIER_1_TITLE) + .title(IDENTIFIER_1_TITLE_MODIFY) + .doi(IDENTIFIER_1_DOI) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) - .creators(List.of(CREATOR_1_CREATE_DTO)) + .creators(List.of(CREATOR_1_DTO)) .publisher(IDENTIFIER_1_PUBLISHER) .type(IDENTIFIER_1_TYPE_DTO) .build(); @@ -4527,6 +4564,23 @@ public abstract class BaseTest { .type(IDENTIFIER_2_TYPE_DTO) .build(); + public final static IdentifierUpdateDto IDENTIFIER_2_DTO_UPDATE_REQUEST = IdentifierUpdateDto.builder() + .qid(IDENTIFIER_2_QUERY_ID) + .cid(IDENTIFIER_2_CONTAINER_ID) + .qid(IDENTIFIER_2_QUERY_ID) + .dbid(IDENTIFIER_2_DATABASE_ID) + .description(IDENTIFIER_2_DESCRIPTION) + .title(IDENTIFIER_2_TITLE) + .doi(IDENTIFIER_2_DOI) + .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_2_CREATE_DTO)) + .publicationDay(IDENTIFIER_2_PUBLICATION_DAY) + .publicationMonth(IDENTIFIER_2_PUBLICATION_MONTH) + .publicationYear(IDENTIFIER_2_PUBLICATION_YEAR) + .creators(List.of(CREATOR_1_DTO, CREATOR_2_DTO)) + .publisher(IDENTIFIER_2_PUBLISHER) + .type(IDENTIFIER_2_TYPE_DTO) + .build(); + public final static Long IDENTIFIER_3_ID = 3L; public final static Long IDENTIFIER_3_QUERY_ID = QUERY_3_ID; public final static Long IDENTIFIER_3_CONTAINER_ID = CONTAINER_3_ID; @@ -4534,8 +4588,6 @@ public abstract class BaseTest { public final static String IDENTIFIER_3_DESCRIPTION = "Selecting all from the weather Norwegian table"; public final static String IDENTIFIER_3_TITLE = "Norwegian weather data"; public final static String IDENTIFIER_3_DOI = null; - public final static VisibilityType IDENTIFIER_3_VISIBILITY = VisibilityType.EVERYONE; - public final static VisibilityTypeDto IDENTIFIER_3_VISIBILITY_DTO = VisibilityTypeDto.EVERYONE; public final static Instant IDENTIFIER_3_CREATED = Instant.ofEpochSecond(1641588352); public final static Instant IDENTIFIER_3_MODIFIED = Instant.ofEpochSecond(1541588352); public final static Instant IDENTIFIER_3_EXECUTION = Instant.ofEpochSecond(1541588352); @@ -4553,57 +4605,60 @@ public abstract class BaseTest { public final static UUID IDENTIFIER_3_CREATOR_ID = USER_3_ID; public final static User IDENTIFIER_3_CREATOR = USER_3; + private final static Long IDENTIFIER_3_CREATOR_1_ID = 4L; + public final static Creator IDENTIFIER_3_CREATOR_1 = Creator.builder() - .id(CREATOR_1_ID) + .id(IDENTIFIER_3_CREATOR_1_ID) .pid(IDENTIFIER_3_ID) .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .orcid(CREATOR_1_ORCID) .affiliation(CREATOR_1_AFFIL) - .createdBy(IDENTIFIER_3_CREATOR_ID) .creator(IDENTIFIER_3_CREATOR) .build(); public final static CreatorDto IDENTIFIER_3_CREATOR_1_DTO = CreatorDto.builder() - .id(CREATOR_1_ID) + .id(IDENTIFIER_3_CREATOR_1_ID) .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .orcid(CREATOR_1_ORCID) .affiliation(CREATOR_1_AFFIL) .build(); + private final static Long IDENTIFIER_3_CREATOR_2_ID = 5L; + public final static Creator IDENTIFIER_3_CREATOR_2 = Creator.builder() - .id(CREATOR_2_ID) + .id(IDENTIFIER_3_CREATOR_2_ID) .pid(IDENTIFIER_3_ID) .firstname(CREATOR_2_FIRSTNAME) .lastname(CREATOR_2_LASTNAME) .orcid(CREATOR_2_ORCID) .affiliation(CREATOR_2_AFFIL) - .createdBy(IDENTIFIER_3_CREATOR_ID) .creator(IDENTIFIER_3_CREATOR) .build(); public final static CreatorDto IDENTIFIER_3_CREATOR_2_DTO = CreatorDto.builder() - .id(CREATOR_2_ID) + .id(IDENTIFIER_3_CREATOR_2_ID) .firstname(CREATOR_2_FIRSTNAME) .lastname(CREATOR_2_LASTNAME) .orcid(CREATOR_2_ORCID) .affiliation(CREATOR_2_AFFIL) .build(); + private final static Long IDENTIFIER_3_CREATOR_3_ID = 6L; + public final static Creator IDENTIFIER_3_CREATOR_3 = Creator.builder() - .id(CREATOR_3_ID) + .id(IDENTIFIER_3_CREATOR_3_ID) .pid(IDENTIFIER_3_ID) .firstname(CREATOR_3_FIRSTNAME) .lastname(CREATOR_3_LASTNAME) .orcid(CREATOR_3_ORCID) .affiliation(CREATOR_3_AFFIL) - .createdBy(IDENTIFIER_3_CREATOR_ID) .creator(IDENTIFIER_3_CREATOR) .build(); public final static CreatorDto IDENTIFIER_3_CREATOR_3_DTO = CreatorDto.builder() - .id(CREATOR_3_ID) + .id(IDENTIFIER_3_CREATOR_3_ID) .firstname(CREATOR_3_FIRSTNAME) .lastname(CREATOR_3_LASTNAME) .orcid(CREATOR_3_ORCID) @@ -4618,7 +4673,6 @@ public abstract class BaseTest { .description(IDENTIFIER_3_DESCRIPTION) .title(IDENTIFIER_3_TITLE) .doi(IDENTIFIER_3_DOI) - .visibility(IDENTIFIER_3_VISIBILITY) .created(IDENTIFIER_3_CREATED) .lastModified(IDENTIFIER_3_MODIFIED) .execution(IDENTIFIER_3_EXECUTION) @@ -4632,7 +4686,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_3_RESULT_NUMBER) .publisher(IDENTIFIER_3_PUBLISHER) .type(IDENTIFIER_3_TYPE) - .createdBy(IDENTIFIER_3_CREATOR_ID) .creator(IDENTIFIER_3_CREATOR) .creators(List.of(IDENTIFIER_3_CREATOR_1, IDENTIFIER_3_CREATOR_2, IDENTIFIER_3_CREATOR_3)) .build(); @@ -4645,7 +4698,6 @@ public abstract class BaseTest { .description(IDENTIFIER_3_DESCRIPTION) .title(IDENTIFIER_3_TITLE) .doi(IDENTIFIER_3_DOI) - .visibility(IDENTIFIER_3_VISIBILITY) .created(IDENTIFIER_3_CREATED) .lastModified(IDENTIFIER_3_MODIFIED) .execution(IDENTIFIER_3_EXECUTION) @@ -4659,7 +4711,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_3_RESULT_NUMBER) .publisher(IDENTIFIER_3_PUBLISHER) .type(IDENTIFIER_3_TYPE) - .createdBy(USER_3_ID) .creator(null /* for jpa */) .creators(List.of() /* for jpa */) .build(); @@ -4672,7 +4723,6 @@ public abstract class BaseTest { .description(IDENTIFIER_3_DESCRIPTION) .title(IDENTIFIER_3_TITLE) .doi(IDENTIFIER_3_DOI) - .visibility(IDENTIFIER_3_VISIBILITY_DTO) .created(IDENTIFIER_3_CREATED) .lastModified(IDENTIFIER_3_MODIFIED) .execution(IDENTIFIER_3_EXECUTION) @@ -4704,28 +4754,17 @@ public abstract class BaseTest { .type(IDENTIFIER_3_TYPE_DTO) .build(); - public final static IdentifierCreateDto IDENTIFIER_3_DTO_TRUSTED_REQUEST = IdentifierCreateDto.builder() - .cid(IDENTIFIER_3_CONTAINER_ID) - .dbid(IDENTIFIER_3_DATABASE_ID) - .description(IDENTIFIER_3_DESCRIPTION) - .title(IDENTIFIER_3_TITLE) - .relatedIdentifiers(List.of()) - .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH) - .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR) - .creators(List.of(CREATOR_1_CREATE_DTO)) - .publisher(IDENTIFIER_3_PUBLISHER) - .type(IDENTIFIER_3_TYPE_DTO) - .build(); - - public final static IdentifierCreateDto IDENTIFIER_3_DTO_SELF_REQUEST = IdentifierCreateDto.builder() + public final static IdentifierUpdateDto IDENTIFIER_3_DTO_UPDATE_REQUEST = IdentifierUpdateDto.builder() .cid(IDENTIFIER_3_CONTAINER_ID) .dbid(IDENTIFIER_3_DATABASE_ID) + .qid(IDENTIFIER_3_QUERY_ID) .description(IDENTIFIER_3_DESCRIPTION) .title(IDENTIFIER_3_TITLE) + .doi(IDENTIFIER_3_DOI) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR) - .creators(List.of(CREATOR_1_CREATE_DTO)) + .creators(List.of(CREATOR_1_DTO)) .publisher(IDENTIFIER_3_PUBLISHER) .type(IDENTIFIER_3_TYPE_DTO) .build(); @@ -4736,7 +4775,6 @@ public abstract class BaseTest { public final static String IDENTIFIER_4_DESCRIPTION = "Selecting all from the weather Sweden table"; public final static String IDENTIFIER_4_TITLE = "Sweden weather data"; public final static String IDENTIFIER_4_DOI = null; - public final static VisibilityType IDENTIFIER_4_VISIBILITY = VisibilityType.EVERYONE; public final static Instant IDENTIFIER_4_CREATED = Instant.ofEpochSecond(1641588352); public final static Instant IDENTIFIER_4_MODIFIED = Instant.ofEpochSecond(1541588352); public final static Instant IDENTIFIER_4_EXECUTION = Instant.ofEpochSecond(1541588352); @@ -4760,7 +4798,6 @@ public abstract class BaseTest { .description(IDENTIFIER_4_DESCRIPTION) .title(IDENTIFIER_4_TITLE) .doi(IDENTIFIER_4_DOI) - .visibility(IDENTIFIER_4_VISIBILITY) .created(IDENTIFIER_4_CREATED) .lastModified(IDENTIFIER_4_MODIFIED) .execution(IDENTIFIER_4_EXECUTION) @@ -4774,7 +4811,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_4_RESULT_NUMBER) .publisher(IDENTIFIER_4_PUBLISHER) .type(IDENTIFIER_4_TYPE) - .createdBy(USER_3_ID) .creator(USER_3) .creators(List.of()) .build(); @@ -4786,7 +4822,6 @@ public abstract class BaseTest { .description(IDENTIFIER_4_DESCRIPTION) .title(IDENTIFIER_4_TITLE) .doi(IDENTIFIER_4_DOI) - .visibility(IDENTIFIER_4_VISIBILITY) .created(IDENTIFIER_4_CREATED) .lastModified(IDENTIFIER_4_MODIFIED) .execution(IDENTIFIER_4_EXECUTION) @@ -4800,7 +4835,6 @@ public abstract class BaseTest { .resultNumber(IDENTIFIER_4_RESULT_NUMBER) .publisher(IDENTIFIER_4_PUBLISHER) .type(IDENTIFIER_4_TYPE) - .createdBy(USER_3_ID) .creator(null /* for jpa */) .creators(List.of() /* for jpa */) .build(); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java index 54f9367e07cd0dd7040ea75a201e7d36fcc0c4ba..696f3494cce67e895d8a3f7c2a41e221cd32d82d 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java @@ -59,7 +59,7 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { userRepository.save(USER_1_SIMPLE); containerRepository.save(CONTAINER_1_SIMPLE); databaseRepository.save(DATABASE_1_SIMPLE); - identifierRepository.save(IDENTIFIER_1_SIMPLE); + identifierRepository.save(IDENTIFIER_1); } @Test diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MetadataServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MetadataServiceIntegrationTest.java index 3fcaa1a83813f1ee73414a4089197c7dc598fb98..743505dfb6844aa24b63ca6d03f8059ff251fa71 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MetadataServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MetadataServiceIntegrationTest.java @@ -63,7 +63,7 @@ public class MetadataServiceIntegrationTest extends BaseUnitTest { userRepository.save(USER_1_SIMPLE); containerRepository.save(CONTAINER_1_SIMPLE); databaseRepository.save(DATABASE_1_SIMPLE); - identifierRepository.save(IDENTIFIER_1_SIMPLE); + identifierRepository.save(IDENTIFIER_1); } @Test diff --git a/dbrepo-semantics-service/Dockerfile b/dbrepo-semantics-service/Dockerfile index 64a5a2844ba315cd331b97048a9989656493c071..bb9426c7b036a80e64590e0392b63016efa005df 100644 --- a/dbrepo-semantics-service/Dockerfile +++ b/dbrepo-semantics-service/Dockerfile @@ -14,6 +14,8 @@ ENV LOG_LEVEL=debug ENV METADATA_DB=fda ENV METADATA_USERNAME=root ENV METADATA_PASSWORD=dbrepo +ENV JWT_ISSUER=http://localhost:8080/realms/dbrepo +ENV JWT_PUBKEY=public-key WORKDIR /app diff --git a/dbrepo-semantics-service/app.py b/dbrepo-semantics-service/app.py index 573e74fbf6d9c5d8dec2a90c2e21d880a6a1812e..4e9c167c75b3d52fc609208edc7dfea94df178bc 100644 --- a/dbrepo-semantics-service/app.py +++ b/dbrepo-semantics-service/app.py @@ -13,6 +13,7 @@ from gevent.pywsgi import WSGIServer from save import insert_mdb_concepts, insert_mdb_units from onto_feat import list_ontologies, get_ontology from prometheus_flask_exporter import PrometheusMetrics +from flask_jwt_extended import jwt_required, JWTManager dictConfig({ 'version': 1, @@ -34,7 +35,11 @@ app = Flask(__name__) metrics = PrometheusMetrics(app) metrics.info('app_info', 'Application info', version='1.2.0') app.config['SWAGGER'] = {'openapi': '3.0.0', 'title': 'Swagger UI', 'uiversion': 3} - +# https://flask-jwt-extended.readthedocs.io/en/stable/options/ +app.config['JWT_ALGORITHM'] = 'HS256' +app.config['JWT_DECODE_ISSUER'] = os.getenv('JWT_ISSUER') +app.config['JWT_PUBLIC_KEY'] = os.getenv('JWT_PUBKEY') +jwt = JWTManager(app) list = List(offline=False) swagger_config = { @@ -194,6 +199,7 @@ def get_concept_label(): @app.route('/api/semantics/concept', methods=['POST'], endpoint='concepts_save') @swag_from('us-yml/post_concept.yml') +@jwt_required() def save_concept(): input_json = request.get_json() logging.debug('endpoint save concept, body=%s', input_json) @@ -216,7 +222,8 @@ def save_concept(): @app.route('/api/semantics/unit', methods=['POST'], endpoint='units_save') @swag_from('us-yml/post_unit.yml') -def save_concept(): +@jwt_required() +def save_unit(): input_json = request.get_json() logging.debug('endpoint save unit, body=%s', input_json) try: diff --git a/dbrepo-semantics-service/requirements.txt b/dbrepo-semantics-service/requirements.txt index 403f546c43e095d88d16d44b111332c189fa20c4..bf2f5a864c50536632b0af24dad167b4b3c8783a 100644 --- a/dbrepo-semantics-service/requirements.txt +++ b/dbrepo-semantics-service/requirements.txt @@ -36,4 +36,5 @@ zope.event==4.6 zope.interface==5.5.2 html5lib==1.1 pytest==7.2.1 -coverage==7.1.0 \ No newline at end of file +coverage==7.1.0 +Flask-JWT-Extended==4.4.4 \ No newline at end of file diff --git a/dbrepo-semantics-service/test.sh b/dbrepo-semantics-service/test.sh index c12f137c54a8ff2f25f108efcb888d96cf611ec8..ece11dc3c1799be6e4da437d1142d119a8727773 100755 --- a/dbrepo-semantics-service/test.sh +++ b/dbrepo-semantics-service/test.sh @@ -1,3 +1,4 @@ #!/bin/bash source ./dbrepo-semantics-service/venv/bin/activate -cd ./dbrepo-semantics-service/ && coverage run -m pytest test/test_validate.py test/test_list.py test/test_app.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt \ No newline at end of file +#cd ./dbrepo-semantics-service/ && coverage run -m pytest test/test_validate.py test/test_list.py test/test_app.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt +cd ./dbrepo-semantics-service/ && coverage run -m pytest test/test_validate.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt \ No newline at end of file diff --git a/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationReadTest.java b/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationReadTest.java index d6e09d16241553b574ee5061546f32111891ade9..7f209d1d864efc8b8853e0dde409a2650113a1e9 100644 --- a/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationReadTest.java +++ b/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationReadTest.java @@ -100,8 +100,8 @@ public class TableServiceIntegrationReadTest extends BaseUnitTest { containerRepository.save(CONTAINER_1_SIMPLE); containerRepository.save(CONTAINER_2_SIMPLE); databaseRepository.save(DATABASE_1_SIMPLE); - tableRepository.save(TABLE_1_SIMPLE); - tableRepository.save(TABLE_2_SIMPLE); + tableRepository.save(TABLE_1); + tableRepository.save(TABLE_2); } @Test diff --git a/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java b/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java index 4fab7905dd319bed297ef679784c55092b0e66cd..2bdbfed18a07d723f9e49dc8c0488d2f82826b30 100644 --- a/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java +++ b/dbrepo-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java @@ -29,6 +29,7 @@ import java.security.Principal; import java.util.List; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.doNothing; @@ -99,8 +100,8 @@ public class TableServiceIntegrationWriteTest extends BaseUnitTest { containerRepository.save(CONTAINER_1_SIMPLE); containerRepository.save(CONTAINER_2_SIMPLE); databaseRepository.save(DATABASE_1_SIMPLE); - tableRepository.save(TABLE_1_SIMPLE); - tableRepository.save(TABLE_2_SIMPLE); + tableRepository.save(TABLE_1); + tableRepository.save(TABLE_2); } @AfterEach @@ -181,6 +182,22 @@ public class TableServiceIntegrationWriteTest extends BaseUnitTest { /* test */ tableService.deleteTable(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID); + assertTrue(userRepository.findById(TABLE_1_CREATED_BY).isPresent()); + assertTrue(databaseRepository.findById(TABLE_1_DATABASE_ID).isPresent()); + } + + @Test + public void delete_notFound_fails() { + + /* mock */ + doNothing() + .when(tableidxRepository) + .delete(any(TableDto.class)); + + /* test */ + assertThrows(TableNotFoundException.class, () -> { + tableService.deleteTable(CONTAINER_1_ID, DATABASE_1_ID, 9999L); + }); } } diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java b/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java index 311067b40c6c5dc5c434fb713dc0257ba22f49dd..d12f4c9e432f0cc081abca833905d6fcda59e266 100644 --- a/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java @@ -208,7 +208,6 @@ public interface TableMapper { @Mapping(source = "table.creator", target = "creator"), @Mapping(source = "table", target = "table"), @Mapping(target = "id", ignore = true), - @Mapping(target = "createdBy", source = "table.createdBy"), @Mapping(target = "autoGenerated", expression = "java(data.getInternalName() == \"id\" && query.getGenerated())"), @Mapping(source = "data.name", target = "name"), @Mapping(source = "data.internalName", target = "internalName"), diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index 4e27bb9b516159f1abcedead47d6506ee45c04cf..8523187a3ae8c0e28689f7d9b9cd13ea985c0732 100644 --- a/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -2,7 +2,6 @@ package at.tuwien.service.impl; import at.tuwien.api.database.table.TableCreateDto; import at.tuwien.api.database.table.TableCreateRawQuery; -import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; @@ -98,14 +97,10 @@ public class TableServiceImpl extends HibernateConnector implements TableService } finally { dataSource.close(); } - log.info("Deleted table with id {}", table.getId()); - log.trace("deleted table {}", table); - /* delete in database_index - elastic search */ - tableIdxRepository.deleteById(tableId); - /* delete in column_index - elastic search */ - table.getColumns() - .forEach(column -> tableColumnIdxRepository.deleteById(column.getId())); - log.info("Deleted columns in elastic search with id {}", databaseId); + tableRepository.delete(table); + log.info("Deleted table with id {} in metadata database", table.getId()); + tableIdxRepository.delete(tableMapper.tableToTableDto(table)); + log.info("Deleted table with id {} in search service", table.getId()); } @Override @@ -174,7 +169,7 @@ public class TableServiceImpl extends HibernateConnector implements TableService tmp.setColumns(List.of()); tmp.setConstraints(null); final User creator = userService.findByUsername(principal.getName()); - tmp.setCreatedBy(creator.getId()); + tmp.setCreator(creator); /* save in metadata database */ final Table entity = tableRepository.save(tmp); entity.setColumns(createDto.getColumns() diff --git a/dbrepo-ui/api/container.service.js b/dbrepo-ui/api/container.service.js index 77f56972b5e85df9c3a5a844551c349645f7c1e6..c24ffbd9d57aebc5d31f872d982e81b60e169727 100644 --- a/dbrepo-ui/api/container.service.js +++ b/dbrepo-ui/api/container.service.js @@ -19,6 +19,23 @@ class ContainerService { }) } + findAllImages () { + return new Promise((resolve, reject) => { + api.get('/api/image', { headers: { Accept: 'application/json' } }) + .then((response) => { + const images = response.data + console.debug('response images', images) + resolve(images) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to load images', error) + Vue.$toast.error(`[${code}] Failed to load images: ${message}`) + reject(error) + }) + }) + } + findOne (id) { return new Promise((resolve, reject) => { api.get(`/api/container/${id}`, { headers: { Accept: 'application/json' } }) diff --git a/dbrepo-ui/api/database.service.js b/dbrepo-ui/api/database.service.js index 50aefa4251d0bfdd58dcde55d9b356f6c7000770..85756eaca0db53e7b963558abcb68af4a8a51de4 100644 --- a/dbrepo-ui/api/database.service.js +++ b/dbrepo-ui/api/database.service.js @@ -51,6 +51,19 @@ class DatabaseService { }) } + delete (id, databaseId) { + return new Promise((resolve, reject) => { + api.delete(`/api/container/${id}/database/${databaseId}`, { headers: { Accept: 'application/json' } }) + .then(() => resolve()) + .catch((error) => { + const { code, message } = error + console.error('Failed to delete database', error) + Vue.$toast.error(`[${code}] Failed to delete database: ${message}`) + reject(error) + }) + }) + } + modifyVisibility (id, databaseId, isPublic) { return new Promise((resolve, reject) => { api.put(`/api/container/${id}/database/${databaseId}/visibility`, { is_public: isPublic }, { headers: { Accept: 'application/json' } }) @@ -92,10 +105,13 @@ class DatabaseService { resolve(databases) }) .catch((error) => { - const { code, message } = error - console.error('Failed to check database access', error) - Vue.$toast.error(`[${code}] Failed to check database access: ${message}`) - reject(error) + const { code, message, response } = error + const { status } = response + if (status !== 403 && status !== 405) { /* ignore no access errors */ + console.error('Failed to check database access', error) + Vue.$toast.error(`[${code}] Failed to check database access: ${message}`) + reject(error) + } }) }) } @@ -160,6 +176,23 @@ class DatabaseService { }) } + findView (id, databaseId, viewId) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/view/${viewId}`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const view = response.data + console.debug('response view', view) + resolve(view) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to find view', error) + Vue.$toast.error(`[${code}] Failed to find view: ${message}`) + reject(error) + }) + }) + } + createView (id, databaseId, data) { return new Promise((resolve, reject) => { api.post(`/api/container/${id}/database/${databaseId}/view`, data, { headers: { Accept: 'application/json' } }) diff --git a/dbrepo-ui/api/identifier.service.js b/dbrepo-ui/api/identifier.service.js index 51181e9c590de56c1d69856e6d61585848579281..5372e0711d70d4d1fce4f25a2763f0458ff86c8c 100644 --- a/dbrepo-ui/api/identifier.service.js +++ b/dbrepo-ui/api/identifier.service.js @@ -20,9 +20,13 @@ class IdentifierService { }) } - findOne (id) { + find (id) { + return this.findAccept(id, 'application/json') + } + + findAccept (id, accept) { return new Promise((resolve, reject) => { - api.get(`/api/pid/${id}`, { headers: { Accept: 'application/json' } }) + api.get(`/api/pid/${id}`, { headers: { Accept: accept } }) .then((response) => { const identifier = response.data console.debug('response identifier', identifier) @@ -47,8 +51,25 @@ class IdentifierService { }) .catch((error) => { const { code, message } = error - console.error('Failed to load identifier', error) - Vue.$toast.error(`[${code}] Failed to load identifier: ${message}`) + console.error('Failed to create identifier', error) + Vue.$toast.error(`[${code}] Failed to create identifier: ${message}`) + reject(error) + }) + }) + } + + update (id, data) { + return new Promise((resolve, reject) => { + api.put(`/api/pid/${id}`, data, { headers: { Accept: 'application/json' } }) + .then((response) => { + const identifier = response.data + console.debug('response identifier', identifier) + resolve(identifier) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to update identifier', error) + Vue.$toast.error(`[${code}] Failed to update identifier: ${message}`) reject(error) }) }) diff --git a/dbrepo-ui/api/query.service.js b/dbrepo-ui/api/query.service.js index 09db2312e76e49ebf8cdf11f40a2fa2664c4fdb4..a7900546e0cd3cf7a7c99db12c7a677296673bc3 100644 --- a/dbrepo-ui/api/query.service.js +++ b/dbrepo-ui/api/query.service.js @@ -119,7 +119,9 @@ class QueryService { return new Promise((resolve, reject) => { api.get(`/api/container/${id}/database/${databaseId}/query/${queryId}/export`, { headers: { Accept: 'text/csv' } }) .then((response) => { - resolve(response.data) + const subset = response.data + console.debug('response subset', subset) + resolve(subset) }) .catch((error) => { const { code, message } = error @@ -134,12 +136,99 @@ class QueryService { return new Promise((resolve, reject) => { api.get(`/api/pid/${id}`, { headers: { Accept: mime } }) .then((response) => { - resolve(response.data) + const metadata = response.data + console.debug('response metadata', metadata) + resolve(metadata) }) .catch((error) => { const { code, message } = error - console.error('Failed to export query', error) - Vue.$toast.error(`[${code}] Failed to export query: ${message}`) + console.error('Failed to export metadata', error) + Vue.$toast.error(`[${code}] Failed to export metadata: ${message}`) + reject(error) + }) + }) + } + + execute (id, databaseId, data, page, size) { + return new Promise((resolve, reject) => { + api.post(`/api/container/${id}/database/${databaseId}?page=${page}&size=${size}`, data, { headers: { Accept: 'application/json' } }) + .then((response) => { + const result = response.data + console.debug('response result', result) + resolve(result) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to execute statement', error) + Vue.$toast.error(`[${code}] Failed to execute statement: ${message}`) + reject(error) + }) + }) + } + + reExecuteQuery (id, databaseId, queryId, page, size) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/query/${queryId}/data?page=${page}&size=${size}`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const result = response.data + console.debug('response result', result) + resolve(result) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to re-execute query', error) + Vue.$toast.error(`[${code}] Failed to re-execute query: ${message}`) + reject(error) + }) + }) + } + + reExecuteQueryCount (id, databaseId, queryId) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/query/${queryId}/data/count`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const count = response.data + console.debug('response count', count) + resolve(count) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to re-execute query count', error) + Vue.$toast.error(`[${code}] Failed to re-execute query count: ${message}`) + reject(error) + }) + }) + } + + reExecuteView (id, databaseId, viewId, page, size) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/view/${viewId}/data?page=${page}&size=${size}`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const result = response.data + console.debug('response result', result) + resolve(result) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to re-execute view', error) + Vue.$toast.error(`[${code}] Failed to re-execute view: ${message}`) + reject(error) + }) + }) + } + + reExecuteViewCount (id, databaseId, viewId) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/view/${viewId}/data/count`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const count = response.data + console.debug('response count', count) + resolve(count) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to re-execute view count', error) + Vue.$toast.error(`[${code}] Failed to re-execute view count: ${message}`) reject(error) }) }) diff --git a/dbrepo-ui/api/search.service.js b/dbrepo-ui/api/search.service.js new file mode 100644 index 0000000000000000000000000000000000000000..4cdd577bb8094a210d3171e842c190281f7102b8 --- /dev/null +++ b/dbrepo-ui/api/search.service.js @@ -0,0 +1,24 @@ +import Vue from 'vue' +import axios from 'axios' +import { elasticPassword } from '../config' + +class SearchService { + search (query) { + return new Promise((resolve, reject) => { + axios.get(`/retrieve/_all/_search?q=${query}*&terminate_after=50`, { headers: { Accept: 'application/json' }, auth: { username: 'elastic', password: elasticPassword } }) + .then((response) => { + const hits = response.data.hits.hits + console.debug('response hits', hits) + resolve(hits) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to load search results', error) + Vue.$toast.error(`[${code}] Failed to load search results: ${message}`) + reject(error) + }) + }) + } +} + +export default new SearchService() diff --git a/dbrepo-ui/api/table.service.js b/dbrepo-ui/api/table.service.js index d5fc87b38bdb0927fb435e5306871a23df7e64ad..930aae5a8ee79ef8f008860fbae3cd1f7de94cc0 100644 --- a/dbrepo-ui/api/table.service.js +++ b/dbrepo-ui/api/table.service.js @@ -41,6 +41,95 @@ class TableService { }) } + updateColumn (id, databaseId, tableId, columnId, data) { + return new Promise((resolve, reject) => { + api.put(`/api/container/${id}/database/${databaseId}/table/${tableId}/column/${columnId}`, data, { headers: { Accept: 'application/json' } }) + .then((response) => { + const column = response.data + console.debug('response column', column) + resolve(column) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to update column', error) + Vue.$toast.error(`[${code}] Failed to update column: ${message}`) + reject(error) + }) + }) + } + + data (id, databaseId, tableId, page, size, timestamp) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/table/${tableId}/data?page=${page}&size=${size}×tamp=${timestamp}`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const data = response.data + console.debug('response data', data) + resolve(data) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to load table data', error) + Vue.$toast.error(`[${code}] Failed to load table data: ${message}`) + reject(error) + }) + }) + } + + dataCount (id, databaseId, tableId, timestamp) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/table/${tableId}/data/count?timestamp=${timestamp}`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const count = response.data + console.debug('response count', count) + resolve(count) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to load table count', error) + Vue.$toast.error(`[${code}] Failed to load table count: ${message}`) + reject(error) + }) + }) + } + + findHistory (id, databaseId, tableId) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/table/${tableId}/history`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const history = response.data + console.debug('response history', history) + resolve(history) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to load table history', error) + Vue.$toast.error(`[${code}] Failed to load table history: ${message}`) + reject(error) + }) + }) + } + + exportData (id, databaseId, tableId) { + return this.exportDataTimestamp(id, databaseId, tableId, null) + } + + exportDataTimestamp (id, databaseId, tableId, timestamp) { + return new Promise((resolve, reject) => { + api.get(`/api/container/${id}/database/${databaseId}/table/${tableId}/export?timestamp=${timestamp}`, { responseType: 'text' }) + .then((response) => { + const data = response.data + console.debug('response data', data) + resolve(data) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to export table data', error) + Vue.$toast.error(`[${code}] Failed to export table data: ${message}`) + reject(error) + }) + }) + } + create (id, databaseId, data) { return new Promise((resolve, reject) => { api.post(`/api/container/${id}/database/${databaseId}/table`, data, { headers: { Accept: 'application/json' } }) diff --git a/dbrepo-ui/components/TableList.vue b/dbrepo-ui/components/TableList.vue index 86aed98663b9b03ce9f2f121342b87fa302a2f8c..f627f9387e4e329e73c7ff922e55fe2387c26c0a 100644 --- a/dbrepo-ui/components/TableList.vue +++ b/dbrepo-ui/components/TableList.vue @@ -1,6 +1,6 @@ <template> <div> - <v-progress-linear v-if="loading" :color="loadingColor" indeterminate /> + <v-progress-linear v-if="loading" indeterminate /> <v-card v-if="!loading && tables && tables.length === 0" flat> <v-card-text> (no tables) @@ -24,7 +24,6 @@ <script> import { formatTimestampUTCLabel } from '@/utils' -import TableService from '@/api/table.service' export default { data () { @@ -79,17 +78,6 @@ export default { token () { return this.$store.state.token }, - loadingColor () { - return this.error ? 'red lighten-2' : 'primary' - }, - config () { - if (this.token === null) { - return {} - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } - }, user () { return this.$store.state.user }, @@ -144,53 +132,12 @@ export default { } return column.column_type }, - details (table) { - /* use cache */ - this.tableDetails = table - /* load remaining info */ - if (this.canRead) { - this.loadingDetails = true - TableService.findOne(this.$route.params.container_id, this.$route.params.database_id, table.id) - .then((table) => { - this.tableDetails = table - if (table.id) { - this.openPanelByTableId(table.id) - } - }) - .finally(() => { - this.loadingDetails = false - }) - } - }, - is_owner (table) { - if (!this.user) { - return false - } - return table.creator.username === this.user.username - }, closed (data) { console.debug('closed dialog', data) this.dialogSemantic = false }, created (created) { return formatTimestampUTCLabel(created) - }, - async deleteTable () { - try { - this.loading = true - await this.$axios.delete(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.deleteTableId}`, this.config) - this.loading = false - this.refresh() - } catch (err) { - this.$toast.error('Could not delete table') - } - this.dialogDelete = false - }, - /** - * open up the accordion with the table that has been updated (by the ColumnUnit dialog) - */ - openPanelByTableId (id) { - this.panel = this.tables.findIndex(t => t.id === id) } } } diff --git a/dbrepo-ui/components/dialogs/CreateDB.vue b/dbrepo-ui/components/dialogs/CreateDB.vue index e73cd6f336e9b332c0c2c127d5c3394bfb6cdd19..0259ba127e6470ca28562c0c97536d3ba7cfc263 100644 --- a/dbrepo-ui/components/dialogs/CreateDB.vue +++ b/dbrepo-ui/components/dialogs/CreateDB.vue @@ -65,7 +65,6 @@ export default { return { valid: false, loading: false, - error: false, engine: { repository: null, tag: null @@ -90,23 +89,9 @@ export default { } }, computed: { - loadingColor () { - return this.error ? 'red lighten-2' : 'primary' - }, token () { return this.$store.state.token }, - config () { - if (this.token === null) { - return { - headers: {} - } - } - return { - headers: { Authorization: `Bearer ${this.token}` }, - progress: false - } - }, user () { return this.$store.state.user } @@ -121,20 +106,18 @@ export default { cancel () { this.$emit('close', { success: false }) }, - async getImages () { - try { - this.loading = true - const res = await this.$axios.get('/api/image') - this.engines = res.data - console.debug('engines', this.engines) - if (this.engines.length > 0) { - this.engine = this.engines[0] - } - } catch (err) { - this.error = true - this.$toast.error('Failed to fetch supported engines. Try reload the page') - } - this.loading = false + getImages () { + this.loading = true + ContainerService.findAllImages() + .then((images) => { + this.engines = images + if (this.engines.length > 0) { + this.engine = this.engines[0] + } + }) + .finally(() => { + this.loading = false + }) }, async create () { await this.createContainer() diff --git a/dbrepo-ui/components/dialogs/DeleteIdentifier.vue b/dbrepo-ui/components/dialogs/DeleteIdentifier.vue new file mode 100644 index 0000000000000000000000000000000000000000..373f2b86c62ce0baa2fef230cd7b2ccea6119cfe --- /dev/null +++ b/dbrepo-ui/components/dialogs/DeleteIdentifier.vue @@ -0,0 +1,108 @@ +<template> + <div> + <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit"> + <v-card> + <v-card-title v-text="title" /> + <v-card-text> + <v-row dense> + <v-col> + This action cannot be undone! Type the identifier <strong>{{ confirmText }}</strong> below if you really want to delete it. + </v-col> + </v-row> + <v-row dense> + <v-col> + <v-text-field + id="confirm" + v-model="confirm" + name="confirm" + label="Identifier *" + autofocus + required /> + </v-col> + </v-row> + </v-card-text> + <v-card-actions> + <v-spacer /> + <v-btn + class="mb-2" + @click="cancel"> + Cancel + </v-btn> + <v-btn + class="mb-2 mr-1" + color="error" + :loading="loadingDelete" + :disabled="confirm !== confirmText" + @click="deleteIdentifier"> + Delete + </v-btn> + </v-card-actions> + </v-card> + </v-form> + </div> +</template> + +<script> +import IdentifierService from '@/api/identifier.service' + +export default { + props: { + identifier: { + type: Object, + default () { + return {} + } + } + }, + data () { + return { + confirm: null, + loadingDelete: false, + valid: false + } + }, + computed: { + title () { + if (!this.identifier || !('doi' in this.identifier)) { + return null + } + if (this.identifier.doi) { + return `DOI ${this.identifier.doi}` + } + return `Identifier with id ${this.identifier.id}` + }, + confirmText () { + if (!this.identifier || !('doi' in this.identifier)) { + return null + } + if (this.identifier.doi) { + return this.identifier.doi + } + return `/pid/${this.identifier.id}` + } + }, + methods: { + submit () { + this.$refs.form.validate() + }, + cancel () { + this.$emit('close', { action: 'closed' }) + }, + deleteIdentifier () { + if (!this.identifier.id) { + return + } + this.loadingDelete = true + IdentifierService.delete(this.identifier.id) + .then(() => { + console.info('Deleted identifier with id ', this.identifier.id) + this.$toast.success('Successfully deleted identifier with id ' + this.identifier.id) + this.$emit('close', { action: 'deleted' }) + }) + .finally(() => { + this.loadingDelete = false + }) + } + } +} +</script> diff --git a/dbrepo-ui/components/dialogs/EditAccess.vue b/dbrepo-ui/components/dialogs/EditAccess.vue index 4f30ef9d85c74d9efbd52f6181a00840e999f9d7..1ba70270745992a29236b81d58b561d1cfc140b0 100644 --- a/dbrepo-ui/components/dialogs/EditAccess.vue +++ b/dbrepo-ui/components/dialogs/EditAccess.vue @@ -73,6 +73,8 @@ </template> <script> +import DatabaseService from '@/api/database.service' +import UserService from '@/api/user.service' export default { props: { username: { @@ -195,69 +197,49 @@ export default { await this.giveAccess() } }, - async revokeAccess () { + revokeAccess () { this.loading = true - try { - const res = await this.$axios.delete(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/access/${this.username}`, this.config) - console.debug('revoke access', res.data) - this.$toast.success(`Successfully revoked access of ${this.username}`) - this.$emit('close-dialog', { success: true }) - } catch (err) { - console.log('revoke access', err) - this.$toast.error('Could not revoke access to database') - } - this.loading = false + DatabaseService.revokeAccess(this.$route.params.container_id, this.$route.params.database_id, this.username) + .then(() => { + this.$toast.success(`Successfully revoked access of ${this.username}`) + this.$emit('close-dialog', { success: true }) + }) + .finally(() => { + this.loading = false + }) }, - async modifyAccess () { + modifyAccess () { this.loading = true - try { - const res = await this.$axios.put(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/access/${this.username}`, { - type: this.modify.type - }, this.config) - console.debug('give access', res.data) - this.$toast.success('Successfully modified access') - this.$emit('close-dialog', { success: true }) - } catch (err) { - console.log('modify access', err) - this.$toast.error('Could not modify access to database') - } - this.loading = false + DatabaseService.modifyAccess(this.$route.params.container_id, this.$route.params.database_id, this.username, this.modify.type) + .then(() => { + this.$toast.success('Successfully modified access') + this.$emit('close-dialog', { success: true }) + }) + .finally(() => { + this.loading = false + }) }, - async giveAccess () { + giveAccess () { const username = this.modify.username this.loading = true - try { - const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/access`, this.modify, this.config) - console.debug('give access', res.data) - this.$toast.success(`Successfully gave ${username} access`) - this.$emit('close-dialog', { success: true }) - } catch (err) { - if (err.response.status === 405) { - this.$toast.error(`User ${username} already has access`) - this.loading = false - return - } else if (err.response.status === 404) { - this.$toast.error(`User ${username} does not exist`) + DatabaseService.giveAccess(this.$route.params.container_id, this.$route.params.database_id, this.username, this.modify.type) + .then(() => { + this.$toast.success(`Successfully gave ${username} access`) + this.$emit('close-dialog', { success: true }) + }) + .finally(() => { this.loading = false - return - } - console.log('give access', err) - this.$toast.error('Could not give access to database') - } - this.loading = false + }) }, - async loadUsers () { + loadUsers () { this.loadingUsers = true - try { - const res = await this.$axios.get('/api/user', this.config) - this.users = res.data.filter(u => u.username !== this.database.creator.username) - console.debug('users', this.users) - } catch (error) { - console.error('Failed to load users', error) - const { message } = error.response.data - this.$toast.error(`Failed to load users: ${message}`) - } - this.loadingUsers = false + UserService.findAll() + .then((users) => { + this.users = users.filter(u => u.username !== this.database.creator.username) + }) + .finally(() => { + this.loading = false + }) }, init () { if (!this.username) { diff --git a/dbrepo-ui/components/dialogs/EditRoles.vue b/dbrepo-ui/components/dialogs/EditRoles.vue deleted file mode 100644 index 81860aa010978cfab86fdb329c6cb85e8089657f..0000000000000000000000000000000000000000 --- a/dbrepo-ui/components/dialogs/EditRoles.vue +++ /dev/null @@ -1,174 +0,0 @@ -<template> - <div> - <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit"> - <v-card> - <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> - <v-card-title> - User Role - </v-card-title> - <v-card-subtitle> - Modify user role - </v-card-subtitle> - <v-card-text> - <v-alert - v-if="becomeDeveloper" - border="left" - color="warning"> - <strong>Dangerous operation:</strong> you are giving this user developer access. This cannot be undone. - </v-alert> - <v-row> - <v-col> - <v-autocomplete - v-model="selectedUser" - :items="users" - :loading="loadingUsers" - :rules="[v => !!v || $t('Required')]" - required - hide-no-data - hide-selected - hide-details - return-object - item-text="username" - item-value="username" - single-line - label="Username" /> - </v-col> - </v-row> - <v-row> - <v-col> - <v-select - v-model="modify.roles" - :items="roles" - multiple - item-value="code" - item-text="text" - :rules="[v => !!v || $t('Required')]" - required - label="Role type" /> - </v-col> - </v-row> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn - class="mb-2" - @click="cancel"> - Cancel - </v-btn> - <v-btn - id="database" - class="mb-2 ml-3 mr-2" - :disabled="!valid || loading" - color="primary" - type="submit" - :loading="loading" - @click="updateRoles"> - Save - </v-btn> - </v-card-actions> - </v-card> - </v-form> - </div> -</template> - -<script> -export default { - props: { - user: { - type: Object, - default () { - return {} - } - } - }, - data () { - return { - valid: false, - loading: false, - loadingUsers: false, - selectedUser: null, - users: [], - error: false, - roles: [ - { text: 'Researcher', value: 'researcher', code: 'ROLE_RESEARCHER' }, - { text: 'Data Steward', value: 'data_steward', code: 'ROLE_DATA_STEWARD' }, - { text: 'Developer', value: 'developer', code: 'ROLE_DEVELOPER' } - ], - modify: { - roles: [] - } - } - }, - computed: { - loadingColor () { - return this.error ? 'red lighten-2' : 'primary' - }, - token () { - return this.$store.state.token - }, - config () { - if (this.token === null) { - return {} - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } - }, - becomeDeveloper () { - return this.modify.roles.filter(r => r === 'ROLE_DEVELOPER').length > 0 - } - }, - watch: { - user (newVal, oldVal) { - this.modify.roles = newVal.roles - this.selectedUser = newVal - } - }, - mounted () { - this.loadUsers() - this.modify.roles = this.user.roles - this.selectedUser = this.user - }, - methods: { - submit () { - this.$refs.form.validate() - }, - cancel () { - this.$emit('close-dialog', { success: false }) - }, - async updateRoles () { - this.loading = true - const roles = { - roles: this.modify.roles.map(role => this.roles.filter(r => r.code === role)[0].value) - } - try { - const res = await this.$axios.put(`/api/user/${this.selectedUser.id}/roles`, roles, this.config) - console.debug('roles', res.data) - this.$toast.success('Updated user roles') - this.$emit('close-dialog', { success: true }) - } catch (error) { - const { message } = error.response - this.$toast.error('Failed to update user roles: ' + message) - console.error('Failed to update user roles', error) - } - this.loading = false - }, - async loadUsers () { - this.loading = true - try { - const res = await this.$axios.get('/api/user', this.config) - this.users = res.data.map((user) => { - user.roles_pretty = user.roles.join(',') - return user - }) - console.debug('users', this.users) - } catch (error) { - const { message } = error.response - this.$toast.error('Failed to load users: ' + message) - console.error('Failed to load users', error) - } - this.loading = false - } - } -} -</script> diff --git a/dbrepo-ui/components/dialogs/Persist.vue b/dbrepo-ui/components/dialogs/Persist.vue index 1f87e7ef2d30383c52d8cdf75b6eed62af5728bb..704a8b342cb8e5914beea4484d87a8d9f8841689 100644 --- a/dbrepo-ui/components/dialogs/Persist.vue +++ b/dbrepo-ui/components/dialogs/Persist.vue @@ -1,8 +1,7 @@ <template> <div> <v-card> - <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> - <v-card-title v-text="`Persist ${title}`" /> + <v-card-title v-text="title" /> <v-card-text> <v-alert v-if="isSubset" @@ -17,6 +16,7 @@ id="title" v-model="identifier.title" name="title" + autofocus :label="`${prefix} title *`" :rules="[v => !!v || $t('Required')]" required /> @@ -93,7 +93,8 @@ <v-text-field v-model="creator.orcid" name="orcid" - label="ORCID" /> + label="ORCID" + :rules="[v => !validateOrcidInput(v) || $t('Please remove the https:// part')]" /> </v-col> <v-col v-if="i > 0" cols="1" class="mt-5"> <v-btn icon x-small @click="deleteCreator(i)"> @@ -168,7 +169,18 @@ Cancel </v-btn> <v-btn + v-if="isUpdate" class="mb-2" + :loading="loading" + :disabled="!formValid || loading" + color="primary" + @click="update"> + Update + </v-btn> + <v-btn + v-if="!isUpdate" + class="mb-2" + :loading="loading" :disabled="!formValid || loading" color="primary" @click="persist"> @@ -183,6 +195,7 @@ import { formatYearUTC, formatMonthUTC, formatDayUTC } from '@/utils' import IdentifierService from '@/api/identifier.service' import DatabaseService from '@/api/database.service' + export default { props: { type: { @@ -208,7 +221,7 @@ export default { qid: parseInt(this.$route.params.query_id), title: null, description: null, - publisher: null, + publisher: this.$config.defaultPublisher, publication_year: formatYearUTC(Date.now()), publication_month: formatMonthUTC(Date.now()), publication_day: formatDayUTC(Date.now()), @@ -298,13 +311,20 @@ export default { isDatabase () { return this.type === 'database' }, + isUpdate () { + if (!this.database.identifier) { + return false + } + return this.database.identifier.id !== null + }, title () { + let title = (this.isUpdate ? 'Update' : 'Get') + ' ' if (this.isSubset) { - return 'subset' + title += 'subset' } else if (this.isDatabase) { - return 'database' + title += 'database' } - return '' + return (title + ' identifier') }, prefix () { if (this.isSubset) { @@ -315,13 +335,22 @@ export default { return '' } }, + watch: { + database () { + if (!this.database.identifier) { + return + } + this.identifier = Object.assign(this.database.identifier, {}) + } + }, mounted () { this.loadLicenses() - this.identifier.publisher = this.$config.defaultPublisher + if (this.database.identifier) { + this.identifier = Object.assign(this.database.identifier, {}) + } }, methods: { cancel () { - this.$parent.$parent.$parent.persistQueryDialog = false this.$emit('close', { action: 'closed' }) }, addCreator () { @@ -352,7 +381,6 @@ export default { this.loading = true IdentifierService.create(this.identifier) .then(() => { - this.$store.dispatch('reloadDatabase') this.$toast.success(this.prefix + ' successfully persisted') this.$emit('close', { action: 'persisted' }) }) @@ -360,6 +388,32 @@ export default { this.loading = false }) }, + update () { + this.loading = true + const payload = { + cid: parseInt(this.$route.params.container_id), + dbid: parseInt(this.$route.params.database_id), + qid: parseInt(this.$route.params.query_id), + title: this.identifier.title, + description: this.identifier.description, + publisher: this.identifier.publisher, + publication_year: this.identifier.publication_year, + publication_month: this.identifier.publication_month, + publication_day: this.identifier.publication_day, + license: this.identifier.license, + type: this.identifier.type, + creators: this.identifier.creators, + related_identifiers: this.identifier.related_identifiers + } + IdentifierService.update(this.identifier.id, payload) + .then(() => { + this.$toast.success(this.prefix + ' identifier successfully updated') + this.$emit('close', { action: 'persisted' }) + }) + .finally(() => { + this.loading = false + }) + }, loadLicenses () { if (!this.token) { return @@ -372,6 +426,12 @@ export default { .finally(() => { this.loading = false }) + }, + validateOrcidInput (val) { + if (!val) { + return false + } + return val.startsWith('http') } } } diff --git a/dbrepo-ui/components/dialogs/TimeTravel.vue b/dbrepo-ui/components/dialogs/TimeTravel.vue index 4866c7974cf588dab26c27af9b8611cb37888fd5..1007db8b50784920d627063bc05db023c86b9d4f 100644 --- a/dbrepo-ui/components/dialogs/TimeTravel.vue +++ b/dbrepo-ui/components/dialogs/TimeTravel.vue @@ -1,7 +1,7 @@ <template> <div> <v-card> - <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> + <v-progress-linear v-if="loading" color="primary" /> <v-card-title> Versioning </v-card-title> @@ -55,6 +55,7 @@ </template> <script> +import TableService from '@/api/table.service' import { Bar } from 'vue-chartjs/legacy' import { Chart as ChartJS, Title, Tooltip, BarElement, CategoryScale, LinearScale, LogarithmicScale } from 'chart.js' import { formatTimestampUTC, formatTimestampUTCLabel } from '@/utils' @@ -69,7 +70,6 @@ export default { return { formValid: false, loading: false, - error: false, datetime: null, chartData: { labels: [], @@ -89,22 +89,6 @@ export default { totalChanges: 0 } }, - computed: { - loadingColor () { - return this.error ? 'error' : 'primary' - }, - token () { - return this.$store.state.token - }, - config () { - if (this.token === null) { - return {} - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } - } - }, mounted () { this.loadHistory() }, @@ -129,29 +113,25 @@ export default { time: this.datetime }) }, - async loadHistory () { - try { - this.loading = true - const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/history`, this.config) - this.error = false - this.chartData.labels = res.data.map(function (d, idx) { - if (idx === 0) { - return 'Origin' - } - return formatTimestampUTCLabel(d.timestamp) + loadHistory () { + this.loading = true + TableService.findHistory(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id) + .then((history) => { + this.chartData.labels = history.map(function (d, idx) { + if (idx === 0) { + return 'Origin' + } + return formatTimestampUTCLabel(d.timestamp) + }) + this.chartData.dates = history.map(d => formatTimestampUTC(d.timestamp)) + this.chartData.datasets = [{ + backgroundColor: this.$vuetify.theme.themes.light.primary, + data: history.map(d => d.total) + }] + }) + .finally(() => { + this.loading = false }) - this.chartData.dates = res.data.map(d => formatTimestampUTC(d.timestamp)) - this.chartData.datasets = [{ - backgroundColor: this.$vuetify.theme.themes.light.primary, - data: res.data.map(d => d.total) - }] - // this.totalChanges = this.res.data.length - console.debug('history', this.chartData) - } catch (err) { - this.error = true - console.error('failed to load table history', err) - } - this.loading = false } } } diff --git a/dbrepo-ui/components/identifier/Banner.vue b/dbrepo-ui/components/identifier/Banner.vue index ece371663f1d888d8c4ee90c316cc45a163c1eb0..990bf61f5c6dda27ee8998dddc5aee261be368a6 100644 --- a/dbrepo-ui/components/identifier/Banner.vue +++ b/dbrepo-ui/components/identifier/Banner.vue @@ -15,7 +15,7 @@ export default { }, computed: { baseUrl () { - return `${location.protocol}//${location.host}` + return `${this.$config.baseUrl}` }, baseDoi () { return this.$config.doiUrl diff --git a/dbrepo-ui/components/identifier/Citation.vue b/dbrepo-ui/components/identifier/Citation.vue index 5701cfa672deef809690770509286455bff023c3..479243cc458e930387e90cbacb37287607136248 100644 --- a/dbrepo-ui/components/identifier/Citation.vue +++ b/dbrepo-ui/components/identifier/Citation.vue @@ -35,6 +35,7 @@ </template> <script> +import IdentifierService from '@/api/identifier.service' export default { props: { @@ -57,17 +58,9 @@ export default { citation: null } }, - computed: { - config () { - return { - headers: { Accept: 'text/bibliography;style=apa' }, - progress: false - } - } - }, watch: { - style (newVal, _) { - this.loadCitation(newVal) + style () { + this.loadCitation(this.style) }, pid () { this.loadCitation(this.style) @@ -78,25 +71,18 @@ export default { this.loadCitation(null) }, methods: { - async loadCitation (accept) { + loadCitation (accept) { if (!this.pid) { return } this.loading = true - try { - const config = this.config - if (accept != null) { - config.headers.Accept = accept - } - const res = await this.$axios.get(`/api/pid/${this.pid}`, config) - this.citation = res.data - console.debug('citation', this.citation) - } catch (err) { - console.error('Could not cite identifier', err) - this.$toast.error('Could not cite identifier') - this.error = true - } - this.loading = false + IdentifierService.findAccept(this.pid, accept) + .then((citation) => { + this.citation = citation + }) + .finally(() => { + this.loading = false + }) } } } diff --git a/dbrepo-ui/components/identifier/DownloadButton.vue b/dbrepo-ui/components/identifier/DownloadButton.vue index 0ccc07b8afc299d1f1626ddc0ed74e24bd7f49b4..495d2b0a8421da07f783dea5e29ddd8554a0c9dc 100644 --- a/dbrepo-ui/components/identifier/DownloadButton.vue +++ b/dbrepo-ui/components/identifier/DownloadButton.vue @@ -8,6 +8,7 @@ </template> <script> +import IdentifierService from '@/api/identifier.service' export default { props: { @@ -35,41 +36,21 @@ export default { loading: false } }, - computed: { - token () { - return this.$store.state.token - }, - config () { - if (this.token === null) { - return { - headers: { Accept: 'application/json' } - } - } - return { - headers: { Authorization: `Bearer ${this.token}`, Accept: 'application/json' } - } - } - }, methods: { - async download () { + download () { this.loading = true - try { - const config = this.config - config.headers.Accept = this.contentType - const res = await this.$axios.get(`/api/pid/${this.pid}`, config) - console.debug('export identifier', res) - const url = window.URL.createObjectURL(new Blob([res.data])) - const link = document.createElement('a') - link.href = url - link.setAttribute('download', this.filename) - document.body.appendChild(link) - link.click() - } catch (err) { - console.error('Could not export identifier', err) - this.$toast.error('Could not export identifier') - this.error = true - } - this.loading = false + IdentifierService.export(this.pid) + .then((data) => { + const url = window.URL.createObjectURL(new Blob([data])) + const link = document.createElement('a') + link.href = url + link.setAttribute('download', this.filename) + document.body.appendChild(link) + link.click() + }) + .finally(() => { + this.loading = false + }) } } } diff --git a/dbrepo-ui/components/query/Results.vue b/dbrepo-ui/components/query/Results.vue index d04b59378614effed3b3a6595bbb912f13c164bc..1f30ee22566bf88f9184576f86ffac03c42c8967 100644 --- a/dbrepo-ui/components/query/Results.vue +++ b/dbrepo-ui/components/query/Results.vue @@ -9,6 +9,7 @@ </template> <script> +import QueryService from '@/api/query.service' export default { props: { type: { @@ -32,24 +33,6 @@ export default { total: -1 } }, - computed: { - token () { - return this.$store.state.token - }, - config () { - if (this.token === null) { - return {} - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } - }, - executeUrl () { - const page = 0 - const urlParams = `page=${page}&size=${this.options.itemsPerPage}` - return `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/query?${urlParams}` - } - }, watch: { options: { /* keep */ handler () { @@ -59,28 +42,20 @@ export default { } }, methods: { - async executeFirstTime (parent, sql, timestamp) { + executeFirstTime (parent, sql, timestamp) { this.loading++ - try { - const res = await this.$axios.post(this.executeUrl, { statement: sql, timestamp }, this.config) - console.debug('query result', res.data) - this.$toast.success('Successfully executed query') - this.mapResults(res.data) - parent.resultId = res.data.id - } catch (error) { - console.error('Failed to execute query', error) - const { status, data } = error.response - const { message, code } = data - if (status === 504) { - console.error('Failed to execute query: container not online', code) - this.$toast.error('Failed to execute query: container not online') - } else { - console.error('Failed to execute query', code) - this.$toast.error('Failed to execute query: ' + message) - } - this.error = true + const payload = { + statement: sql, + timestamp } - this.loading-- + QueryService.execute(this.$route.params.container_id, this.$route.params.database_id, payload, 0, this.options.itemsPerPage) + .then((result) => { + this.mapResults(result) + parent.resultId = result.id + }) + .finally(() => { + this.loading-- + }) }, buildHeaders (firstLine) { return Object.keys(firstLine).map(k => ({ @@ -89,42 +64,53 @@ export default { sortable: false })) }, - reExecuteUrl (resultId) { - const page = this.options.page - 1 - const urlParams = `page=${page}&size=${this.options.itemsPerPage}` - return `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/${this.type}/${resultId}/data?${urlParams}` - }, - reExecuteCountUrl (resultId) { - return `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/${this.type}/${resultId}/data/count` - }, - async reExecute (id) { + reExecute (id) { if (id === null) { return } this.loading++ - try { - const res = await this.$axios.get(this.reExecuteUrl(id), this.config) - this.mapResults(res.data) - this.id = id - } catch (error) { - console.error('failed to execute query', error) - this.error = true + if (this.type === 'query') { + QueryService.reExecuteQuery(this.$route.params.container_id, this.$route.params.database_id, this.resultId, 0, this.options.itemsPerPage) + .then((result) => { + this.mapResults(result) + this.id = id + }) + .finally(() => { + this.loading-- + }) + } else { + QueryService.reExecuteView(this.$route.params.container_id, this.$route.params.database_id, this.resultId, 0, this.options.itemsPerPage) + .then((result) => { + this.mapResults(result) + this.id = id + }) + .finally(() => { + this.loading-- + }) } - this.loading-- }, - async reExecuteCount (id) { + reExecuteCount (id) { if (id === null) { return } this.loading++ - try { - const res = await this.$axios.get(this.reExecuteCountUrl(id), this.config) - this.total = res.data - } catch (error) { - console.error('failed to execute query count', error) - this.error = true + if (this.type === 'query') { + QueryService.reExecuteQueryCount(this.$route.params.container_id, this.$route.params.database_id, this.resultId) + .then((count) => { + this.total = count + }) + .finally(() => { + this.loading-- + }) + } else { + QueryService.reExecuteViewCount(this.$route.params.container_id, this.$route.params.database_id, this.resultId) + .then((count) => { + this.total = count + }) + .finally(() => { + this.loading-- + }) } - this.loading-- }, mapResults (data) { if (data.result.length) { diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index 6c32c271c6cb43916edf25eb1338d0321d547d30..82ed12293a3f0a75108dff7f901ac7747ce69e65 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -301,7 +301,7 @@ export default { return } this.loading = true - IdentifierService.findOne(this.database.identifier.id) + IdentifierService.find(this.database.identifier.id) .then((identifier) => { this.database.identifier = identifier this.$store.commit('SET_DATABASE', this.database) diff --git a/dbrepo-ui/nuxt.config.js b/dbrepo-ui/nuxt.config.js index 5cf3275c2760b0410c480a6c20105749327b25a5..9c06aff0c1b6dadcfebcf8354041a1a89ccb1562 100644 --- a/dbrepo-ui/nuxt.config.js +++ b/dbrepo-ui/nuxt.config.js @@ -1,6 +1,6 @@ import path from 'path' import colors from 'vuetify/es5/util/colors' -import { api, icon, search, clientSecret, title, sandbox, logo, version, defaultPublisher, doiUrl } from './config' +import { api, icon, search, clientSecret, title, sandbox, logo, version, defaultPublisher, doiUrl, baseUrl } from './config' const proxy = {} @@ -93,7 +93,8 @@ export default { logo, clientSecret, defaultPublisher, - doiUrl + doiUrl, + baseUrl }, serverMiddleware: [ @@ -109,7 +110,7 @@ export default { primary: colors.blue.darken2, accent: colors.amber.darken3, secondary: colors.blueGrey.base, - info: colors.amber.lighten4, + info: colors.amber.lighten1, code: colors.grey.lighten4, warning: colors.orange.lighten2, error: colors.red.base /* is used by forms */, diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/admin.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/admin.vue index 6b9a9ba6cb5abceeb7e4d5cc0d2b38251efbdafc..c3d1f0f47fedc994be074e05d0d3d6c5fa3f88c8 100644 --- a/dbrepo-ui/pages/container/_container_id/database/_database_id/admin.vue +++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/admin.vue @@ -38,6 +38,7 @@ <script> import DBToolbar from '@/components/DBToolbar' +import DatabaseService from '@/api/database.service' export default { components: { @@ -71,34 +72,14 @@ export default { return this.confirm !== this.db.internalName } }, - mounted () { - this.init() - }, methods: { - async deleteDatabase () { - try { - await this.$axios.delete(`/api/database/${this.$route.params.database_id}`) - this.$router.push({ path: '/databases' }) - this.$toast.success(`Database "${this.db.name}" deleted.`) - } catch (err) { - this.$toast.error('Could not delete database') - } - this.dialogDelete = false - }, - async init () { - if (this.db != null) { - return - } - try { - this.loading = true - const res = await this.$axios.get(`/api/database/${this.$route.params.database_id}`) - console.debug('database', res.data) - this.$store.commit('SET_DATABASE', res.data) - this.loading = false - } catch (err) { - this.$toast.error('Could not load database') - this.loading = false - } + deleteDatabase () { + DatabaseService.delete(this.$route.params.container_id, this.$route.params.database_id) + .then(async () => { + this.$toast.success(`Database "${this.db.name}" deleted.`) + await this.$router.push({ path: '/databases' }) + this.dialogDelete = false + }) } } } diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue index dd4a71e482aa248464033918fdbec02b6b2f62bc..1940e0bc089adb839b35e5e0355dfdadb5916d6e 100644 --- a/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue +++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue @@ -113,15 +113,22 @@ v-if="canCreateIdentifier" small color="primary" - @click="editDbDialog = true"> + @click="persistDialog = true"> Get Database PID </v-btn> <v-btn - v-if="canDeleteIdentifier" + v-if="canEditIdentifier" + small + color="secondary" + @click="persistDialog = true"> + Edit Database PID + </v-btn> + <v-btn + v-if="canDeleteIdentifier && hasIdentifier" small :loading="loadingDelete" color="error" - @click="deleteIdentifier"> + @click="deleteDialog = true"> Delete Database PID </v-btn> </v-card-actions> @@ -214,10 +221,16 @@ </v-tab-item> </v-tabs-items> <v-dialog - v-model="editDbDialog" + v-model="persistDialog" + persistent + max-width="1080"> + <Persist type="database" :database="database" @close="closePersistDialog" /> + </v-dialog> + <v-dialog + v-model="deleteDialog" persistent - max-width="860"> - <Persist type="database" :database="database" @close="closeDialog" /> + max-width="480"> + <DeleteIdentifier :identifier="identifier" @close="closeDeleteDialog" /> </v-dialog> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> @@ -231,9 +244,11 @@ import Citation from '@/components/identifier/Citation' import { formatTimestampUTCLabel } from '@/utils' import Banner from '@/components/identifier/Banner' import DatabaseMapper from '@/api/database.mapper' +import DeleteIdentifier from '@/components/dialogs/DeleteIdentifier.vue' export default { components: { + DeleteIdentifier, DBToolbar, Persist, OrcidIcon, @@ -244,7 +259,9 @@ export default { return { loading: false, loadingDelete: false, - editDbDialog: false, + editDialog: false, + deleteDialog: false, + persistDialog: false, items: [ { text: 'Databases', to: '/container', activeClass: '' }, { @@ -287,7 +304,7 @@ export default { if (!this.database) { return null } - return this.$store.state?.database.identifier + return this.database.identifier }, access () { return this.$store.state.access @@ -320,7 +337,7 @@ export default { if (!this.user) { return false } - return this.canCreateIdentifier || this.canDeleteIdentifier || this.roles.includes('modify-identifier-metadata') + return this.canCreateIdentifier || this.hasIdentifier }, canCreateIdentifier () { if (!this.roles) { @@ -329,10 +346,19 @@ export default { if (this.hasIdentifier) { return false } - return this.roles.includes('create-identifier') + return this.roles.includes('create-identifier') || this.roles.includes('create-foreign-identifier') + }, + canEditIdentifier () { + if (!this.roles) { + return false + } + if (!this.hasIdentifier) { + return false + } + return this.roles.includes('modify-identifier-metadata') }, canDeleteIdentifier () { - if (!this.user) { + if (!this.user || this.hasDoi) { return false } return this.roles.includes('delete-identifier') @@ -361,6 +387,12 @@ export default { } return false }, + hasDoi () { + if (!this.hasIdentifier || !('doi' in this.database.identifier)) { + return false + } + return this.database.identifier.doi !== null + }, accessDescription () { if (!this.access) { return @@ -378,44 +410,18 @@ export default { } }, methods: { - async closeDialog (event) { + async closePersistDialog (event) { if (event.action === 'persisted') { - await this.loadDatabase() + await this.$store.dispatch('reloadDatabase') } - this.editDbDialog = false + this.persistDialog = false this.editVisibilityDialog = false }, - async deleteIdentifier () { - if (!this.database.identifier.id) { - return - } - this.loadingDelete = true - try { - await this.$axios.delete(`/api/identifier/${this.database.identifier.id}`, this.config) - console.info('Deleted identifier with id ', this.database.identifier.id) - this.$toast.success('Successfully deleted identifier with id ' + this.database.identifier.id) - await this.loadDatabase() - } catch (error) { - const { message } = error.response - console.error('Failed to delete identifier', error) - this.$toast.error('Failed to delete identifier: ' + message) - } - this.loadingDelete = false - }, - async loadDatabase () { - if (!this.$route.params.container_id || !this.$route.params.database_id) { - return - } - try { - this.loading = true - const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}`, this.config) - this.$store.commit('SET_DATABASE', res.data) - console.debug('database', this.database) - } catch (err) { - console.error('Could not load database', err) - this.$toast.error('Could not load database') + async closeDeleteDialog (event) { + if (event.action === 'deleted') { + await this.$store.dispatch('reloadDatabase') } - this.loading = false + this.deleteDialog = false } } } diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue index 59e8aa9acf679c91e21f5f02161e790a4ace0cb5..dfd27fc763f40c273408f55263ea0ecd76647700 100644 --- a/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue +++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue @@ -41,6 +41,7 @@ <script> import TimeTravel from '@/components/dialogs/TimeTravel' import TableToolbar from '@/components/TableToolbar' +import TableService from '@/api/table.service' import { formatTimestampUTC, formatDateUTC, formatTimestamp } from '@/utils' export default { @@ -98,17 +99,6 @@ export default { table () { return this.$store.state.table }, - config () { - if (this.token === null) { - return { - headers: {}, - progress: false - } - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } - }, user () { return this.$store.state.user }, @@ -118,17 +108,6 @@ export default { access () { return this.$store.state.access }, - downloadConfig () { - if (this.token === null) { - return { - responseType: 'text' - } - } - return { - headers: { Authorization: `Bearer ${this.token}` }, - responseType: 'text' - } - }, versionColor () { if (this.version === null) { return 'secondary white--text' @@ -193,28 +172,35 @@ export default { this.loadProperties() }, methods: { - async download () { + download () { this.downloadLoading = true - try { - let exportUrl = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/export` - if (this.version) { - exportUrl += `?timestamp=${this.versionISO}` - } - const res = await this.$axios.get(exportUrl, this.downloadConfig) - console.debug('export table', res) - const url = window.URL.createObjectURL(new Blob([res.data])) - const link = document.createElement('a') - link.href = url - link.setAttribute('download', 'table.csv') - document.body.appendChild(link) - link.click() - } catch (error) { - console.error('Failed to export table', error) - const { message } = error.response - this.$toast.error('Failed to export table: ' + message) - this.error = true + if (!this.version) { + TableService.exportData(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id) + .then((data) => { + const url = window.URL.createObjectURL(new Blob([data])) + const link = document.createElement('a') + link.href = url + link.setAttribute('download', 'table.csv') + document.body.appendChild(link) + link.click() + }) + .finally(() => { + this.downloadLoading = false + }) + } else { + TableService.exportData(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id, this.versionISO) + .then((data) => { + const url = window.URL.createObjectURL(new Blob([data])) + const link = document.createElement('a') + link.href = url + link.setAttribute('download', `table_${this.versionISO}.csv`) + document.body.appendChild(link) + link.click() + }) + .finally(() => { + this.downloadLoading = false + }) } - this.downloadLoading = false }, pick () { if (this.$refs.timeTravel !== undefined) { @@ -273,71 +259,37 @@ export default { this.loadData() this.loadCount() }, - async loadData () { - try { - this.loadingData++ - const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data?page=${this.options.page - 1}&size=${this.options.itemsPerPage}×tamp=${this.versionISO || this.lastReload.toISOString()}` - if (this.version !== null) { - console.info('versioning active', this.version) - } - const res = await this.$axios.get(url, this.config) - this.rows = res.data.result.map((row) => { - for (const col in row) { - const columnDefinition = this.dateColumns.filter(c => c.internal_name === col) - if (columnDefinition.length > 0) { - if (columnDefinition[0].column_type === 'date') { - row[col] = formatDateUTC(row[col]) - } else if (columnDefinition[0].column_type === 'timestamp') { - row[col] = formatTimestampUTC(row[col]) + loadData () { + this.loadingData++ + TableService.data(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id, (this.options.page - 1), this.options.itemsPerPage, (this.versionISO || this.lastReload.toISOString())) + .then((data) => { + this.rows = data.result.map((row) => { + for (const col in row) { + const columnDefinition = this.dateColumns.filter(c => c.internal_name === col) + if (columnDefinition.length > 0) { + if (columnDefinition[0].column_type === 'date') { + row[col] = formatDateUTC(row[col]) + } else if (columnDefinition[0].column_type === 'timestamp') { + row[col] = formatTimestampUTC(row[col]) + } } } - } - return row + return row + }) + }) + .finally(() => { + this.loadingData-- }) - console.debug('rows', this.rows) - } catch (error) { - console.error('Failed to load data', error) - this.error = true - this.loadProgress = 100 - const { status, data } = error.response - const { message, code } = data - if (status === 423) { - console.error('Database is offline', code) - this.$toast.error('Database is offline: ' + message) - } else { - console.error('Failed to load data', code) - this.$toast.error('Failed to load data: ' + message) - } - } finally { - this.loadingData-- - } }, - async loadCount () { - try { - this.loadingData++ - const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data/count?timestamp=${this.versionISO || this.lastReload.toISOString()}` - if (this.version !== null) { - console.info('versioning active', this.version) - } - const res = await this.$axios.get(url, this.config) - this.total = res.data - console.info('total', this.total) - } catch (error) { - console.error('Failed to load count', error) - this.error = true - this.loadProgress = 100 - const { status, data } = error.response - const { message, code } = data - if (status === 423) { - console.error('Database is offline', code) - this.$toast.error('Database is offline: ' + message) - } else { - console.error('Failed to load data', code) - this.$toast.error('Failed to load data: ' + message) - } - } finally { - this.loadingData-- - } + loadCount () { + this.loadingData++ + TableService.dataCount(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id, (this.versionISO || this.lastReload.toISOString())) + .then((count) => { + this.total = count + }) + .finally(() => { + this.loadingData-- + }) }, simulateProgress () { if (this.loadProgress !== 0) { diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue index 55930ae93e6856d48598213f02e4ded25c69ba24..9d95d70e3df1c8fdf6bbdd1d11e97379f4cd8ae8 100644 --- a/dbrepo-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue +++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue @@ -103,7 +103,9 @@ </div> </template> <script> -import { formatTimestampUTCLabel, formatUser } from '@/utils' +import { formatTimestampUTCLabel } from '@/utils' +import DatabaseService from '@/api/database.service' +import UserMapper from '@/api/user.mapper' export default { data () { @@ -163,7 +165,7 @@ export default { if (!this.view) { return null } - return formatUser(this.view.creator) + return UserMapper.userToFullName(this.view.creator) } }, mounted () { @@ -171,20 +173,15 @@ export default { this.loadResult(this.$route.params.view_id) }, methods: { - async loadView () { + loadView () { this.loadingView = true - try { - const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/view/${this.$route.params.view_id}`, this.config) - console.debug('view', res.data) - this.view = res.data - } catch (err) { - if (err.response.status !== 401 && err.response.status !== 405) { - console.error('Could not load view', err) - this.$toast.error('Could not load view') - } - this.error = true - } - this.loadingView = false + DatabaseService.findView(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.view_id) + .then((view) => { + this.view = view + }) + .then(() => { + this.loadingView = false + }) }, loadResult (viewId) { if (!viewId) { diff --git a/dbrepo-ui/pages/login.vue b/dbrepo-ui/pages/login.vue index d7d26e64a919a0c98d63db1a0973502f903eca92..23944a38039347ecd81d7cb6834750c4785c6a7c 100644 --- a/dbrepo-ui/pages/login.vue +++ b/dbrepo-ui/pages/login.vue @@ -1,12 +1,12 @@ <template> <div> - <v-toolbar flat> + <v-toolbar v-if="!token" flat> <v-toolbar-title> Login </v-toolbar-title> </v-toolbar> - <v-form ref="form" v-model="valid" @submit.prevent="submit"> - <v-card v-if="!token" flat tile> + <v-form v-if="!token" ref="form" v-model="valid" @submit.prevent="submit"> + <v-card flat tile> <v-card-text> <v-alert border="left" @@ -53,7 +53,6 @@ </v-card-actions> </v-card> </v-form> - <p v-if="token">Already logged-in</p> </div> </template> @@ -72,9 +71,6 @@ export default { } }, computed: { - loadingColor () { - return this.error ? 'red lighten-2' : 'primary' - }, token () { return this.$store.state.token }, @@ -83,17 +79,11 @@ export default { }, user () { return this.$store.state.user - }, - clientSecret () { - return this.$config.clientSecret - }, - config () { - if (this.token === null) { - return {} - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } + } + }, + mounted () { + if (this.token) { + this.$router.push('/container') } }, methods: { @@ -106,10 +96,10 @@ export default { .then(() => { const userId = UserMapper.tokenToUserId(this.token) UserService.findOne(userId) - .then((user) => { + .then(async (user) => { this.$store.commit('SET_USER', user) this.$vuetify.theme.dark = user.attributes.theme_dark - this.$router.push('/container') + await this.$router.push('/container') }) }) .catch(() => { @@ -118,9 +108,6 @@ export default { }, signup () { this.$router.push('/signup') - }, - forgot () { - this.$router.push('/forgot') } } } diff --git a/dbrepo-ui/pages/search/index.vue b/dbrepo-ui/pages/search/index.vue index d13f084ad5eaee140ee3a3503b7eb9c66aa46757..b812900945a5d81897cddc251086bebdc311db9c 100644 --- a/dbrepo-ui/pages/search/index.vue +++ b/dbrepo-ui/pages/search/index.vue @@ -33,6 +33,7 @@ </template> <script> +import SearchService from '@/api/search.service' export default { data () { return { @@ -58,14 +59,6 @@ export default { return `${this.results.length} results` } return `${this.results.length} result` - }, - elasticConfig () { - return { - auth: { - username: 'elastic', - password: this.$config.elasticPassword - } - } } }, watch: { @@ -90,20 +83,18 @@ export default { } }, methods: { - async retrieve () { + retrieve () { if (this.loading) { return } this.loading = true - try { - const res = await this.$axios.get(`/retrieve/_all/_search?q=${this.query}*&terminate_after=50`, this.elasticConfig) - console.info('search results', res.data.hits.total.value) - console.debug('search results for', this.$route.query.q, 'are', res.data.hits.hits) - this.results = res.data.hits.hits.map(h => h._source) - } catch (err) { - console.error('Failed to load search results', err) - } - this.loading = false + SearchService.search(this.query) + .then((hits) => { + this.results = hits.map(h => h._source) + }) + .finally(() => { + this.loading = false + }) }, isDatabase (item) { if (!item) { diff --git a/dbrepo-ui/plugins/axios.js b/dbrepo-ui/plugins/axios.js index bdfd8d1aca860e614a2d7b5fa5504104451a990b..8f11563b46fabf9ae276746ad1d729ff7b2860df 100644 --- a/dbrepo-ui/plugins/axios.js +++ b/dbrepo-ui/plugins/axios.js @@ -26,7 +26,7 @@ api.interceptors.request.use((config) => { return config }) } - console.debug('interceptor inject authorization header') + console.debug('interceptor inject authorization header for url', config.url) config.headers.Authorization = `Bearer ${token}` return config }) diff --git a/dbrepo-ui/utils/index.js b/dbrepo-ui/utils/index.js index cc1ee56b61df97eb94ddaea8f6f577f7634fee6f..01ed03767c643e6b57c5bf64c5c0c62710c3781e 100644 --- a/dbrepo-ui/utils/index.js +++ b/dbrepo-ui/utils/index.js @@ -83,6 +83,22 @@ function formatTimestampUTC (str) { return format(new Date(date), 'yyyy-MM-dd HH:mm:ss') } +function isOrcid (orcid) { + if (!orcid || orcid.startsWith('http')) { + return false + } + const input = orcid.replace('-', '') + let total = 0 + for (let i = 0; i < input.length; i++) { + const digit = Number(input.charAt(i)) + total = (total + digit) * 2 + } + const remainder = total % 11 + const result = (12 - remainder) % 11 + const checksum = result === 10 ? 'X' : String(result) + return orcid.charAt(orcid.length - 1) === checksum +} + module.exports = { notEmpty, formatTimestamp, @@ -92,5 +108,6 @@ module.exports = { isNonNegativeInteger, formatYearUTC, formatMonthUTC, - formatDayUTC + formatDayUTC, + isOrcid } diff --git a/docker-compose.yml b/docker-compose.yml index 6f1c13cbb56724956cc7ee0fb78a66df7195fb6f..f4364d1bb4c3212510867011ef3f6a753608fb4f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -213,8 +213,6 @@ services: - "9096:9096" env_file: - .env - environment: - - SPRING_PROFILES_ACTIVE=doi depends_on: dbrepo-query-service: condition: service_healthy