diff --git a/dbrepo-authentication-service/dbrepo-realm.json b/dbrepo-authentication-service/dbrepo-realm.json index d6f63ede240f5fdcd3d541446be3e94f6e2ebd92..359cfccf6cdcc078e5bd6045456e0373478326b0 100644 --- a/dbrepo-authentication-service/dbrepo-realm.json +++ b/dbrepo-authentication-service/dbrepo-realm.json @@ -66,6 +66,14 @@ "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", "attributes" : { } + }, { + "id" : "143ba359-5fa2-451e-8296-43ecf20bb251", + "name" : "update-semantic-concept", + "description" : "${update-semantic-concept}", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } }, { "id" : "5136d7a3-e3f0-4585-bacd-15cb8a56095c", "name" : "escalated-container-handling", @@ -220,6 +228,14 @@ "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", "attributes" : { } + }, { + "id" : "b60a5694-4099-4f7d-a7e9-4c433e0eb9c9", + "name" : "update-semantic-unit", + "description" : "${update-semantic-unit}", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } }, { "id" : "e9854bbb-4580-4757-b1ae-305934173249", "name" : "create-database-access", @@ -258,7 +274,7 @@ "description" : "${default-data-steward-roles}", "composite" : true, "composites" : { - "realm" : [ "escalated-identifier-handling", "escalated-semantics-handling", "default-user-handling" ] + "realm" : [ "escalated-identifier-handling", "default-semantics-handling", "escalated-semantics-handling", "default-user-handling" ] }, "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", @@ -293,7 +309,7 @@ "description" : "${escalated-semantics-handling}", "composite" : true, "composites" : { - "realm" : [ "create-ontology", "update-ontology", "list-ontologies", "delete-ontology", "modify-foreign-table-column-semantics" ] + "realm" : [ "update-semantic-unit", "create-ontology", "update-ontology", "list-ontologies", "delete-ontology", "modify-foreign-table-column-semantics", "update-semantic-concept" ] }, "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", @@ -1054,7 +1070,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ], + "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -2077,7 +2093,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-address-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper" ] } }, { "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979", @@ -2086,7 +2102,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper" ] } } ], "org.keycloak.keys.KeyProvider" : [ { @@ -2138,7 +2154,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "7dddcfca-7bcd-477f-8329-50a43455fd87", + "id" : "b21432ee-a5a0-44f1-89a5-0a7649bb5e99", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -2160,7 +2176,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "8dffaa2e-8dbe-4276-8cd9-7a81eaece233", + "id" : "fe07cb6c-0ffe-4353-9184-6a7bd51940ea", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -2189,7 +2205,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "7e3d1f77-19a9-421c-b58c-d38f4d03fe63", + "id" : "ebefc325-3ee1-45ea-8837-0503a13784b1", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2211,7 +2227,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e60fa1bd-3905-4c5d-a754-396dbf02d3db", + "id" : "006c167c-32bf-41fe-80a2-0e01f9f158d5", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2233,7 +2249,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6570bc4c-7804-4a5c-87da-4ae57e9e26dd", + "id" : "b00fef58-fcdb-4e63-8efc-7f45b2d916fa", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2255,7 +2271,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6c011108-9cf4-4973-8bb4-afbf15dd44a2", + "id" : "ac9e7d88-fef2-4292-9e92-469a3b44b252", "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", @@ -2277,7 +2293,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9acc6184-a192-44b1-aed8-9cf500456c68", + "id" : "b346f859-4f7c-4383-ad6e-22ee648568f7", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -2299,7 +2315,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "78dac59d-dca1-401e-973b-083694df75df", + "id" : "44618916-6ad5-4d89-98b5-02ca56c4e94e", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -2322,7 +2338,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "fe5875af-12d3-4027-b6dc-62aa2e4f4ec9", + "id" : "cd705e48-31b2-4b44-93a1-ac64366d99e7", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -2344,7 +2360,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9a61c442-6393-4d10-bbb4-bb19a03f4560", + "id" : "1f8c5799-9c25-490d-bc81-8a75fcf88ce3", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -2380,7 +2396,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "76db50d0-143c-499a-8256-e4229c5f81d6", + "id" : "6ac53bd6-6665-42f6-8c9b-4bc0d6e39b83", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -2416,7 +2432,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "4529a603-cc30-46e4-b821-f8fe88352048", + "id" : "76b187f5-0c6c-4cb8-ac56-b54712ca290d", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -2445,7 +2461,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "dc9cda33-523a-4b76-ba21-7055c0f1aaea", + "id" : "42e03bec-71a8-4517-8fc0-1601397f8e17", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -2460,7 +2476,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "27eb9e18-c28b-43c1-8148-9b46b3ed103e", + "id" : "9ec63a08-04e2-4736-a87a-46057598466e", "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", @@ -2483,7 +2499,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9f83ad1f-cb1a-422b-802e-b4d7f2800fc5", + "id" : "715aef9a-fbc1-4a8a-969b-0df5e81ea536", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -2505,7 +2521,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "fadcf245-9bab-4b43-ae93-5a2fe685970f", + "id" : "ad7f41c0-abb5-492d-9de1-f11a9c0e3090", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -2527,7 +2543,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "3509bd76-4ae6-4f70-816c-415668d7000d", + "id" : "96d9faa7-7c92-4b34-9433-6b8198cab651", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -2543,7 +2559,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e18b1d97-8ce5-43f1-bb4e-9540c303f6da", + "id" : "6cfc2735-d8a8-41da-ab4f-5c8f2c6d3ff1", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -2579,7 +2595,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "bf867f8e-95a7-4dc3-8569-07b53874b41c", + "id" : "2bc9fcb2-2b4a-480d-a4f8-d41885ef4bf9", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -2615,7 +2631,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "74b260ac-7de8-41f0-a0ab-3b26bc6b4d75", + "id" : "4e4b9e79-7aff-4de8-869f-04dbd1a5bd1e", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -2631,13 +2647,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "e22bf5a6-9a63-425c-91f2-684a10f7f436", + "id" : "b65b03a6-9a9e-49f0-9887-7942996c8345", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "a06a0326-c8e5-4c05-bb63-545b9f4932f2", + "id" : "33d1c568-b4cd-43e1-870a-f14778834744", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java index e031cf594496b99d68c58dc44088f177ec32c091..590cd825e769d0b4ecda085bcbb9bcb51e7c2875 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java @@ -25,9 +25,10 @@ public class ConceptDto { @NotBlank private String uri; - @NotBlank private String name; + private String description; + @NotNull @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant created; diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java index e38927af8f436f517a757b17e51cff91ff597fb2..159e07823ce48327a171d976b16e40db5f2fb8fe 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java @@ -16,4 +16,10 @@ public class ConceptSaveDto { @NotBlank private String uri; + @NotBlank + private String name; + + @NotBlank + private String description; + } diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java index 65b6c2a0bdcdbeb10c47975c1a43e9523259f773..9e0fd693604556fa661ff0b8ad792dd2382f4625 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java @@ -23,9 +23,10 @@ public class UnitDto { @NotBlank private String uri; - @NotBlank private String name; + private String description; + @NotNull @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant created; diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java index 41cfd8caaf7cf20e16cd599b5de23905ac5157c6..326efc48b545a0d1ae55d6d41dfc7256ccb2f162 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java @@ -16,4 +16,10 @@ public class UnitSaveDto { @NotBlank private String uri; + @NotBlank + private String name; + + @NotBlank + private String description; + } diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/EntityDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/EntityDto.java index 728b9979dad355ba6f76cd681f67a71432593871..5c1d6cc13a05b7a2c6887139bc88db6e9b314423 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/EntityDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/EntityDto.java @@ -22,8 +22,7 @@ public class EntityDto { @Schema(example = "Apache Jena") private String label; - @NotBlank @Schema(example = "open source semantic web framework for Java") - private String comment; + private String description; } diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java index 6e314aba1c17f49d59b43014443b2d5c3b86402b..b71a0ae0be127d2cd9501f7e2c23765f5c0c949a 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java @@ -35,6 +35,6 @@ public class TableColumnEntityDto { private String label; @Schema(example = "open source semantic web framework for Java") - private String comment; + private String description; } diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java index fa2ae5a0666c3d289fadae19996f968b194b3ae0..8304898a1d836b56c6eb6ab766156cd17537b0a1 100644 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java +++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java @@ -33,6 +33,9 @@ public class TableColumnConcept { @Column(name = "name") private String name; + @Column(name = "description") + private String description; + @Column(nullable = false, updatable = false, columnDefinition = "TIMESTAMP") @CreatedDate private Instant created; diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java index aecbcd9dd354fc8d49b47a8021322dcadedf3598..922f48217f55a78e3becaa28a401da8b34461f07 100644 --- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java +++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java @@ -33,6 +33,9 @@ public class TableColumnUnit { @Column(name = "name") private String name; + @Column(name = "description") + private String description; + @Column(nullable = false, updatable = false, columnDefinition = "TIMESTAMP") @CreatedDate private Instant created; diff --git a/dbrepo-metadata-db/setup-schema.sql b/dbrepo-metadata-db/setup-schema.sql index 9642c98f6152a0e691dffdbc4fd1332badced085..92afa5c8ca4f93d3b57e414d550db32abd24c335 100644 --- a/dbrepo-metadata-db/setup-schema.sql +++ b/dbrepo-metadata-db/setup-schema.sql @@ -275,19 +275,21 @@ CREATE TABLE IF NOT EXISTS `fda`.`mdb_constraints_checks` CREATE TABLE IF NOT EXISTS `fda`.`mdb_concepts` ( - uri text not null, - name VARCHAR(255) null, - created timestamp NOT NULL DEFAULT NOW(), - created_by character varying(255), + uri text not null, + name VARCHAR(255) null, + description TEXT null, + created timestamp NOT NULL DEFAULT NOW(), + created_by character varying(255), PRIMARY KEY (uri(200)) ) WITH SYSTEM VERSIONING; CREATE TABLE IF NOT EXISTS `fda`.`mdb_units` ( - uri text not null, - name VARCHAR(255) null, - created timestamp NOT NULL DEFAULT NOW(), - created_by character varying(255), + uri text not null, + name VARCHAR(255) null, + description TEXT null, + created timestamp NOT NULL DEFAULT NOW(), + created_by character varying(255), PRIMARY KEY (uri(200)) ) WITH SYSTEM VERSIONING; @@ -499,13 +501,18 @@ VALUES (1, '%Y-%c-%d %H:%i:%S.%f', 'yyyy-MM-dd HH:mm:ss.SSSSSS', '2022-01-30 13: (1, '%Y-%c-%d %H:%i:%S', 'yyyy-MM-dd HH:mm:ss', '2022-01-30 13:44:25', true), (1, '%Y-%c-%d', 'yyyy-MM-dd', '2022-01-30', false); -INSERT INTO `fda`.`mdb_ontologies` (uri, prefix, sparql_endpoint) -VALUES ('http://www.ontology-of-units-of-measure.org/resource/om-2/', 'om2', null), - ('http://www.wikidata.org/', 'wd', 'https://query.wikidata.org/sparql'), - ('http://purl.org/ontology/mo/', 'mo', null), - ('http://purl.org/dc/elements/1.1/', 'dc', null), - ('http://www.w3.org/2001/XMLSchema#', 'xsd', null), - ('http://purl.org/NET/c4dm/timeline.owl#', 'tl', null), - ('http://xmlns.com/foaf/0.1/', 'foaf', null), - ('http://dbpedia.org', 'db', 'http://dbpedia.org/sparql'); +INSERT INTO `fda`.`mdb_ontologies` (prefix, uri, sparql_endpoint) +VALUES ('om2', 'http://www.ontology-of-units-of-measure.org/resource/om-2/', null), + ('wd', 'http://www.wikidata.org/', 'https://query.wikidata.org/sparql'), + ('mo', 'http://purl.org/ontology/mo/', null), + ('dc', 'http://purl.org/dc/elements/1.1/', null), + ('xsd', 'http://www.w3.org/2001/XMLSchema#', null), + ('tl', 'http://purl.org/NET/c4dm/timeline.owl#', null), + ('foaf', 'http://xmlns.com/foaf/0.1/', null), + ('schema', 'http://schema.org/', null), + ('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', null), + ('rdfs', 'http://www.w3.org/2000/01/rdf-schema#', null), + ('owl', 'http://www.w3.org/2002/07/owl#', null), + ('prov', 'http://www.w3.org/ns/prov#', null), + ('db', 'http://dbpedia.org', 'http://dbpedia.org/sparql'); COMMIT; diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java index 6567d5ee38424c65946292583f8ef0002ef0e993..5bcf6abdf01ab634d14b18733a4bb793ab6dd2d1 100644 --- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java +++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java @@ -1,9 +1,6 @@ package at.tuwien.endpoints; -import at.tuwien.api.database.table.columns.concepts.ConceptDto; -import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto; -import at.tuwien.api.database.table.columns.concepts.UnitDto; -import at.tuwien.api.database.table.columns.concepts.UnitSaveDto; +import at.tuwien.api.database.table.columns.concepts.*; import at.tuwien.mapper.OntologyMapper; import at.tuwien.mapper.SemanticMapper; import at.tuwien.service.SemanticService; @@ -67,39 +64,19 @@ public class SemanticsEndpoint { @PostMapping("/concept") @Transactional @PreAuthorize("hasAuthority('create-semantic-concept')") - @Timed(value = "semantics.concept.save", description = "Time needed to create or update a semantic concept") + @Timed(value = "semantics.concept.save", description = "Time needed to save a semantic concept") @Operation(summary = "Create or update a semantic concept", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", - description = "Created or updated a semantic concept", + description = "Saved a semantic concept", content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ConceptDto.class))}), }) public ResponseEntity<ConceptDto> saveConcept(@NotNull @Valid @RequestBody ConceptSaveDto data) { - log.debug("endpoint save or update concept, data={}", data); + log.debug("endpoint save concept, data={}", data); final ConceptDto dto = ontologyMapper.tableColumnConceptToConceptDto(semanticService.saveConcept(data)); - log.trace("save or update concept resulted in dto {}", dto); - return ResponseEntity.ok() - .body(dto); - } - - @PutMapping("/concept") - @Transactional - @PreAuthorize("hasAuthority('create-semantic-concept')") - @Timed(value = "semantics.concept.save", description = "Time needed to create or update a semantic concept") - @Operation(summary = "Create or update a semantic concept", security = @SecurityRequirement(name = "bearerAuth")) - @ApiResponses(value = { - @ApiResponse(responseCode = "202", - description = "Created or updated a semantic concept", - content = {@Content( - mediaType = "application/json", - schema = @Schema(implementation = ConceptDto.class))}), - }) - public ResponseEntity<ConceptDto> saveConcept(@NotNull @Valid @RequestBody ConceptSaveDto data) { - log.debug("endpoint save or update concept, data={}", data); - final ConceptDto dto = ontologyMapper.tableColumnConceptToConceptDto(semanticService.saveConcept(data)); - log.trace("save or update concept resulted in dto {}", dto); + log.trace("save concept resulted in dto {}", dto); return ResponseEntity.ok() .body(dto); } @@ -129,11 +106,11 @@ public class SemanticsEndpoint { @PostMapping("/unit") @Transactional @PreAuthorize("hasAuthority('create-semantic-unit')") - @Timed(value = "semantics.unit.save", description = "Time needed to create or update a semantic unit") - @Operation(summary = "Create or update a semantic unit", security = @SecurityRequirement(name = "bearerAuth")) + @Timed(value = "semantics.unit.save", description = "Time needed to save a semantic unit") + @Operation(summary = "Save a semantic unit", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", - description = "Created or updated a semantic unit", + description = "Saved a semantic unit", content = {@Content( mediaType = "application/json", schema = @Schema(implementation = UnitDto.class))}), @@ -141,7 +118,7 @@ public class SemanticsEndpoint { public ResponseEntity<UnitDto> saveConcept(@NotNull @Valid @RequestBody UnitSaveDto data) { log.debug("endpoint save or update unit, data={}", data); final UnitDto dto = ontologyMapper.tableColumnUnitToUnitDto(semanticService.saveUnit(data)); - log.trace("save or update unit resulted in dto {}", dto); + log.trace("save unit resulted in dto {}", dto); return ResponseEntity.accepted() .body(dto); } diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index f9581b2993600acb37d7badf8af84d03ab14b754..cc3689fdedb5e002351315bfc3d6c11b1032cf56 100644 --- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -95,7 +95,7 @@ public class TableEndpoint { @NotNull @PathVariable("tableId") Long tableId, @NotNull @PathVariable("columnId") Long columnId) throws QueryMalformedException, TableColumnNotFoundException { - log.debug("endpoint analyse table semantics, databaseId={}, tableId={}, columnId={}", databaseId, tableId, columnId); + log.debug("endpoint analyse table column semantics, databaseId={}, tableId={}, columnId={}", databaseId, tableId, columnId); final List<TableColumnEntityDto> dtos = tableService.suggestTableColumnSemantics(databaseId, tableId, columnId); log.trace("analyse table semantics resulted in dtos {}", dtos); return ResponseEntity.ok() diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java index 7268311727251df2970181e0f876239a073d87cf..aeec69284c213f542dc76005ac906df7a6fe14c9 100644 --- a/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java +++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java @@ -1,9 +1,6 @@ package at.tuwien.mapper; -import at.tuwien.api.database.table.columns.concepts.ConceptDto; -import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto; -import at.tuwien.api.database.table.columns.concepts.UnitDto; -import at.tuwien.api.database.table.columns.concepts.UnitSaveDto; +import at.tuwien.api.database.table.columns.concepts.*; import at.tuwien.api.semantics.OntologyBriefDto; import at.tuwien.api.semantics.OntologyCreateDto; import at.tuwien.api.semantics.OntologyDto; @@ -14,6 +11,8 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; +import java.util.List; + @Mapper(componentModel = "spring") public interface OntologyMapper { @@ -42,57 +41,79 @@ public interface OntologyMapper { TableColumnConcept conceptSaveDtoToTableColumnConcept(ConceptSaveDto data); - default String ontologyToFindByLabelQuery(Ontology ontology, String label, Integer limit) { + default String defaultNamespaces(List<Ontology> data) { + return String.join("\n", + data.stream() + .map(o -> "PREFIX " + o.getPrefix() + ": <" + o.getUri() + ">") + .toList()); + } + + default String ontologyToFindByLabelQuery(List<Ontology> ontologies, Ontology ontology, String label, Integer limit) { if (ontology.getSparqlEndpoint() != null) { /* prefer SPARQL endpoint over rdf */ return String.join("\n", - "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>", + defaultNamespaces(ontologies), "SELECT * {", " SERVICE <" + ontology.getSparqlEndpoint() + "> {", - " SELECT ?o ?label ?comment {", - " ?o rdfs:label \"" + label.replace("\"", "") + "\"@en .", - " ?o rdfs:label ?label .", - " OPTIONAL {?o rdfs:comment ?comment} .", - " FILTER (langMatches(lang(?label), \"EN\" ) )", - " FILTER (langMatches(lang(?comment), \"EN\" ) )", - " } LIMIT " + limit, + " SELECT ?o ?label ?description {", + " OPTIONAL {", + " ?o rdfs:label ?label", + " FILTER (LANG(?label) = 'en')", + " }", + " OPTIONAL {", + " ?o schema:description ?description", + " FILTER (LANG(?description) = 'en')", + " }", + " } LIMIT " + limit, " }", "}"); } return String.join("\n", - "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>", + defaultNamespaces(ontologies), "SELECT ?o ?label ?comment {", " ?o rdfs:label \"" + label.replace("\"", "") + "\"@en .", - " ?o rdfs:label ?label .", - " OPTIONAL {?o rdfs:comment ?comment} .", - " FILTER (langMatches(lang(?label), \"EN\" ) )", - " FILTER (langMatches(lang(?comment), \"EN\" ) )", + " OPTIONAL {", + " ?o rdfs:label ?label", + " FILTER (LANG(?label) = 'en')", + " }", + " OPTIONAL {", + " ?o schema:description ?description", + " FILTER (LANG(?description) = 'en')", + " }", "} LIMIT " + limit); } - default String ontologyToFindByUriQuery(Ontology ontology, String uri) { + default String ontologyToFindByUriQuery(List<Ontology> ontologies, Ontology ontology, String uri) { if (ontology.getSparqlEndpoint() != null) { /* prefer SPARQL endpoint over rdf */ return String.join("\n", - "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>", + defaultNamespaces(ontologies), "SELECT * {", " SERVICE <" + ontology.getSparqlEndpoint() + "> {", - " SELECT ?label ?comment {", - " <" + uri + "> rdfs:label ?label .", - " OPTIONAL {<" + uri + "> rdfs:comment ?comment} .", - " FILTER (langMatches(lang(?label), \"EN\" ) )", - " FILTER (langMatches(lang(?comment), \"EN\" ) )", + " SELECT ?label ?description {", + " OPTIONAL {", + " <" + uri + "> rdfs:label ?label", + " FILTER (LANG(?label) = 'en')", + " }", + " OPTIONAL {", + " <" + uri + "> schema:description ?description", + " FILTER (LANG(?description) = 'en')", + " }", " } LIMIT 1", " }", "}"); } return String.join("\n", - "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>", - "SELECT ?label ?comment {", - " <" + uri + "> rdfs:label ?label .", - " OPTIONAL {<" + uri + "> rdfs:comment ?comment} .", - " FILTER (langMatches(lang(?label), \"EN\" ) )", - " FILTER (langMatches(lang(?comment), \"EN\" ) )", + defaultNamespaces(ontologies), + "SELECT ?label ?description {", + " OPTIONAL {", + " <" + uri + "> rdfs:label ?label", + " FILTER (LANG(?label) = 'en')", + " }", + " OPTIONAL {", + " <" + uri + "> schema:description ?description", + " FILTER (LANG(?description) = 'en')", + " }", "} LIMIT 1"); } diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java index 68e9d93e2a32a79bcb6e9260d51e1b6fb7333402..5b51cfdf8c7457b38c748d4b955516a5e51e1740 100644 --- a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java +++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java @@ -30,10 +30,13 @@ public class QueryServiceImpl implements QueryService { private final Dataset dataset; private final OntologyMapper ontologyMapper; + private final OntologyRepository ontologyRepository; @Autowired - public QueryServiceImpl(OntologyRepository ontologyRepository, OntologyMapper ontologyMapper) { + public QueryServiceImpl(OntologyRepository ontologyRepository, OntologyMapper ontologyMapper, + OntologyRepository ontologyRepository1) { this.ontologyMapper = ontologyMapper; + this.ontologyRepository = ontologyRepository1; final Context context = ARQ.getContext().copy(); this.dataset = DatasetFactory.create(); /* registry */ @@ -62,17 +65,19 @@ public class QueryServiceImpl implements QueryService { @Override public List<EntityDto> findByLabel(Ontology ontology, String label, Integer limit) throws QueryMalformedException { - final String statement = ontologyMapper.ontologyToFindByLabelQuery(ontology, label, limit); + final List<Ontology> ontologies = ontologyRepository.findAll(); + final String statement = ontologyMapper.ontologyToFindByLabelQuery(ontologies, ontology, label, limit); + log.trace("execute sparql query:\n{}", statement); final List<EntityDto> results = new LinkedList<>(); try (QueryExecution execution = QueryExecutionFactory.create(statement, this.dataset.getDefaultModel())) { final Iterator<QuerySolution> resultSet = execution.execSelect(); while (resultSet.hasNext()) { final QuerySolution solution = resultSet.next(); - final RDFNode comment = solution.get("comment"); + final RDFNode description = solution.get("description"); final EntityDto entity = EntityDto.builder() .uri(solution.get("o").toString()) .label(label) - .comment(comment != null ? comment.asLiteral().getLexicalForm() : null) + .description(description != null ? description.asLiteral().getLexicalForm() : null) .build(); results.add(entity); } @@ -85,18 +90,20 @@ public class QueryServiceImpl implements QueryService { @Override public List<EntityDto> findByUri(Ontology ontology, String uri) throws QueryMalformedException { - final String statement = ontologyMapper.ontologyToFindByUriQuery(ontology, uri); + final List<Ontology> ontologies = ontologyRepository.findAll(); + final String statement = ontologyMapper.ontologyToFindByUriQuery(ontologies, ontology, uri); + log.trace("execute sparql query:\n{}", statement); try (QueryExecution execution = QueryExecutionFactory.create(statement, this.dataset.getDefaultModel())) { final Iterator<QuerySolution> resultSet = execution.execSelect(); final List<EntityDto> results = new LinkedList<>(); while (resultSet.hasNext()) { final QuerySolution solution = resultSet.next(); final RDFNode label = solution.get("label"); - final RDFNode comment = solution.get("comment"); + final RDFNode description = solution.get("description"); final EntityDto entity = EntityDto.builder() .uri(uri) .label(label != null ? label.asLiteral().getLexicalForm() : null) - .comment(comment != null ? comment.asLiteral().getLexicalForm() : null) + .description(description != null ? description.asLiteral().getLexicalForm() : null) .build(); results.add(entity); } diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java index 8c8cab046939d9c9a90a5b24b7dde8b1748ea537..94a55ee511b4edb2cdfb2d36a5a2cf25a1e1eba8 100644 --- a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java +++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java @@ -34,15 +34,13 @@ public class SemanticServiceImpl implements SemanticService { @Override @Transactional(readOnly = true) public List<TableColumnConcept> findAllConcepts() { - final List<TableColumnConcept> concepts = tableColumnConceptRepository.findAll(); - return concepts; + return tableColumnConceptRepository.findAll(); } @Override @Transactional(readOnly = true) public List<TableColumnUnit> findAllUnits() { - final List<TableColumnUnit> units = tableColumnUnitRepository.findAll(); - return units; + return tableColumnUnitRepository.findAll(); } @Override diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index d51df7fcf15ce9bbd01782c4060dc778732fe40f..8f586250943b420093aa2a0c8e6f73baa537c4a4 100644 --- a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -88,7 +88,7 @@ public class TableServiceImpl implements TableService { .columnId(optional.get().getId()) .label(e.getLabel()) .uri(e.getUri()) - .comment(e.getComment()) + .description(e.getDescription()) .build()) .toList()); } diff --git a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java index 82d41b6d8662ff7689136c10e6678eeebc109523..5d025939ba6a1ab45725474ad375a0070df26f68 100644 --- a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java +++ b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java @@ -85,8 +85,8 @@ public class TableColumnEndpoint { @NotNull Principal principal, @NotNull @RequestHeader("Authorization") String authorization) throws TableNotFoundException, TableMalformedException, DatabaseNotFoundException, - ContainerNotFoundException, UnitNotFoundException, ConceptNotFoundException, NotAllowedException, - SemanticEntityPersistException { + ContainerNotFoundException, NotAllowedException, SemanticEntityPersistException, + SemanticEntityNotFoundException { log.debug("endpoint update table, containerId={}, databaseId={}, tableId={}, principal={}", containerId, databaseId, tableId, principal); if (!User.hasRole(principal, "modify-foreign-table-column-semantics")) { diff --git a/dbrepo-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableColumnEndpointUnitTest.java b/dbrepo-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableColumnEndpointUnitTest.java index 38f3e9547fd08e38873c0572a386ab25a90f2439..2dd1a03a5affd3ba8a02eb9ca289aae1c0ba6fc9 100644 --- a/dbrepo-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableColumnEndpointUnitTest.java +++ b/dbrepo-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableColumnEndpointUnitTest.java @@ -101,7 +101,9 @@ public class TableColumnEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-table-column-semantics"}) - public void update_publicHasRoleHasOwnWriteAccess_succeeds() throws TableNotFoundException, NotAllowedException, TableMalformedException, UnitNotFoundException, DatabaseNotFoundException, ConceptNotFoundException, ContainerNotFoundException, SemanticEntityPersistException { + public void update_publicHasRoleHasOwnWriteAccess_succeeds() throws TableNotFoundException, NotAllowedException, + TableMalformedException, DatabaseNotFoundException, ContainerNotFoundException, + SemanticEntityPersistException, SemanticEntityNotFoundException { /* test */ generic_update(CONTAINER_3_ID, DATABASE_3_ID, TABLE_8_ID, COLUMN_1_1_ID, DATABASE_3, TABLE_8, COLUMN_8_2_WITH_SEMANTICS, COLUMN_8_2_SEMANTICS_UPDATE_DTO, USER_1_USERNAME, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS); @@ -140,8 +142,8 @@ public class TableColumnEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_2_USERNAME, authorities = {"modify-table-column-semantics"}) public void update_publicHasRoleForeignHasAllWriteAccess_succeeds() throws TableNotFoundException, - NotAllowedException, TableMalformedException, UnitNotFoundException, DatabaseNotFoundException, - ConceptNotFoundException, ContainerNotFoundException, SemanticEntityPersistException { + NotAllowedException, TableMalformedException, DatabaseNotFoundException, + ContainerNotFoundException, SemanticEntityPersistException, SemanticEntityNotFoundException { /* test */ generic_update(CONTAINER_3_ID, DATABASE_3_ID, TABLE_8_ID, COLUMN_1_1_ID, DATABASE_3, TABLE_8, COLUMN_8_2_WITH_SEMANTICS, COLUMN_8_2_SEMANTICS_UPDATE_DTO, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_3_USER_2_WRITE_ALL_ACCESS); @@ -183,7 +185,9 @@ public class TableColumnEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-table-column-semantics"}) - public void update_privateHasRoleHasOwnWriteAccess_succeeds() throws TableNotFoundException, NotAllowedException, TableMalformedException, UnitNotFoundException, DatabaseNotFoundException, ConceptNotFoundException, ContainerNotFoundException, SemanticEntityPersistException { + public void update_privateHasRoleHasOwnWriteAccess_succeeds() throws TableNotFoundException, NotAllowedException, + TableMalformedException, DatabaseNotFoundException, ContainerNotFoundException, + SemanticEntityPersistException, SemanticEntityNotFoundException { /* test */ generic_update(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, COLUMN_1_1_ID, DATABASE_1, TABLE_1, COLUMN_1_4_WITH_SEMANTICS, COLUMN_1_4_SEMANTICS_UPDATE_DTO, USER_1_USERNAME, USER_1_PRINCIPAL, DATABASE_1_USER_1_WRITE_OWN_ACCESS); @@ -222,8 +226,8 @@ public class TableColumnEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_2_USERNAME, authorities = {"modify-table-column-semantics"}) public void update_privateHasRoleForeignHasAllWriteAccess_succeeds() throws TableNotFoundException, - NotAllowedException, TableMalformedException, UnitNotFoundException, DatabaseNotFoundException, - ConceptNotFoundException, ContainerNotFoundException, SemanticEntityPersistException { + NotAllowedException, TableMalformedException, DatabaseNotFoundException, + ContainerNotFoundException, SemanticEntityPersistException, SemanticEntityNotFoundException { /* test */ generic_update(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, COLUMN_1_1_ID, DATABASE_1, TABLE_1, COLUMN_1_4_WITH_SEMANTICS, COLUMN_1_4_SEMANTICS_UPDATE_DTO, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_1_USER_2_WRITE_ALL_ACCESS); @@ -238,7 +242,7 @@ public class TableColumnEndpointUnitTest extends BaseUnitTest { ColumnSemanticsUpdateDto data, String username, Principal principal, DatabaseAccess access) throws DatabaseNotFoundException, NotAllowedException, TableNotFoundException, TableMalformedException, - UnitNotFoundException, ConceptNotFoundException, ContainerNotFoundException, SemanticEntityPersistException { + ContainerNotFoundException, SemanticEntityPersistException, SemanticEntityNotFoundException { /* mock */ if (database != null) { diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/exception/SemanticEntityNotFoundException.java b/dbrepo-table-service/services/src/main/java/at/tuwien/exception/SemanticEntityNotFoundException.java new file mode 100644 index 0000000000000000000000000000000000000000..70098f61dacc02ff101b4c99724eb83d4b1c636a --- /dev/null +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/exception/SemanticEntityNotFoundException.java @@ -0,0 +1,21 @@ +package at.tuwien.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.NOT_FOUND) +public class SemanticEntityNotFoundException extends Exception { + + public SemanticEntityNotFoundException(String msg) { + super(msg); + } + + public SemanticEntityNotFoundException(String msg, Throwable thr) { + super(msg, thr); + } + + public SemanticEntityNotFoundException(Throwable thr) { + super(thr); + } + +} diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/SemanticServiceGateway.java b/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/SemanticServiceGateway.java new file mode 100644 index 0000000000000000000000000000000000000000..df54e70226bb1519c46e5b7b074e5cda424098e3 --- /dev/null +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/SemanticServiceGateway.java @@ -0,0 +1,8 @@ +package at.tuwien.gateway; + +import at.tuwien.api.semantics.EntityDto; +import at.tuwien.exception.SemanticEntityNotFoundException; + +public interface SemanticServiceGateway { + EntityDto getEntity(Long ontologyId, String uri, String authorization) throws SemanticEntityNotFoundException; +} diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/impl/SemanticServiceGatewayImpl.java b/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/impl/SemanticServiceGatewayImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..7b4f839984dd0f106758aefc320d110eff4c84f8 --- /dev/null +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/impl/SemanticServiceGatewayImpl.java @@ -0,0 +1,49 @@ +package at.tuwien.gateway.impl; + +import at.tuwien.api.semantics.EntityDto; +import at.tuwien.exception.SemanticEntityNotFoundException; +import at.tuwien.gateway.SemanticServiceGateway; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Service +public class SemanticServiceGatewayImpl implements SemanticServiceGateway { + + private final RestTemplate restTemplate; + + @Autowired + public SemanticServiceGatewayImpl(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + @Override + public EntityDto getEntity(Long ontologyId, String uri, String authorization) throws SemanticEntityNotFoundException { + final String url = "/api/semantic/ontology/" + ontologyId + "/entity?uri=" + uri; + final HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", authorization); + final ResponseEntity<EntityDto[]> response = restTemplate.exchange(url, HttpMethod.GET, + new HttpEntity<>(null, headers), EntityDto[].class); + if (!response.getStatusCode().equals(HttpStatus.OK) || response.getBody() == null) { + log.error("Failed to get semantic entity for uri {}", uri); + throw new SemanticEntityNotFoundException("Failed to get concept for uri " + uri); + } + if (response.getBody().length != 1) { + log.error("None or multiple entities found for uri {}", uri); + throw new SemanticEntityNotFoundException("None or multiple entities found for uri " + uri); + } + return response.getBody()[0]; + } + + private String urlEncode(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + +} 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 5dd576ce0fb3d26a1590c419a1869643ba1064d9..688e44c832e1e998f16018b995ab30ec542684bd 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 @@ -14,6 +14,7 @@ import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; import at.tuwien.api.database.table.constraints.foreignKey.ForeignKeyCreateDto; import at.tuwien.api.database.table.constraints.foreignKey.ForeignKeyDto; import at.tuwien.api.database.table.constraints.foreignKey.ReferenceTypeDto; +import at.tuwien.api.semantics.EntityDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.columns.TableColumn; @@ -85,14 +86,14 @@ public interface TableMapper { Table tableCreateDtoToTable(TableCreateDto data); @Mappings({ - @Mapping(source = "conceptUri", target = "uri") + @Mapping(source = "label", target = "name") }) - TableColumnConcept columnSemanticsUpdateDtoToTableColumnConcept(ColumnSemanticsUpdateDto data); + TableColumnConcept entityDtoToTableColumnConcept(EntityDto data); @Mappings({ - @Mapping(source = "unitUri", target = "uri") + @Mapping(source = "label", target = "name") }) - TableColumnUnit columnSemanticsUpdateDtoToTableColumnUnit(ColumnSemanticsUpdateDto data); + TableColumnUnit entityDtoToTableColumnUnit(EntityDto data); default TableColumn columnNameToTableColumn(Table table, String name) throws TableMalformedException { String internalName = nameToInternalName(name); diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/repository/jpa/OntologyRepository.java b/dbrepo-table-service/services/src/main/java/at/tuwien/repository/jpa/OntologyRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..75c296f527e5a1b04d8a326673ca35f88519e842 --- /dev/null +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/repository/jpa/OntologyRepository.java @@ -0,0 +1,10 @@ +package at.tuwien.repository.jpa; + +import at.tuwien.entities.semantics.Ontology; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface OntologyRepository extends JpaRepository<Ontology, Long> { + +} diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/service/SemanticService.java b/dbrepo-table-service/services/src/main/java/at/tuwien/service/SemanticService.java new file mode 100644 index 0000000000000000000000000000000000000000..343a27347da322611980796a78734893d9442382 --- /dev/null +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/service/SemanticService.java @@ -0,0 +1,32 @@ +package at.tuwien.service; + +import at.tuwien.entities.database.table.columns.TableColumnConcept; +import at.tuwien.entities.database.table.columns.TableColumnUnit; +import at.tuwien.exception.ConceptNotFoundException; +import at.tuwien.exception.SemanticEntityNotFoundException; +import at.tuwien.exception.UnitNotFoundException; + +public interface SemanticService { + + /** + * Finds a ColumnConcept with given uri + * + * @param uri The uri. + * @return The concept, if successful. + * @throws ConceptNotFoundException The ColumnConcept was not found in the metadata database. + */ + TableColumnConcept findConcept(String uri) throws ConceptNotFoundException; + + /** + * Finds a unit with given uri + * + * @param uri The uri. + * @return The unit, if successful. + * @throws UnitNotFoundException The unit was not found in the metadata database. + */ + TableColumnUnit findUnit(String uri) throws UnitNotFoundException; + + TableColumnConcept saveConcept(String uri, String authorization) throws SemanticEntityNotFoundException; + + TableColumnUnit saveUnit(String uri, String authorization) throws SemanticEntityNotFoundException; +} diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/service/TableService.java b/dbrepo-table-service/services/src/main/java/at/tuwien/service/TableService.java index dae9cb07f6161d1c1462edb8ace8f73ec6729b23..3bc0a73af55fa381d5b035cf214f943c155f766b 100644 --- a/dbrepo-table-service/services/src/main/java/at/tuwien/service/TableService.java +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/service/TableService.java @@ -78,16 +78,14 @@ public interface TableService { * @param tableId The table id. * @param columnId The column id. * @param updateDto The update data containing unit and concept uris. - * @return The updated table column. - * @throws TableNotFoundException + * @return The updated table column, if successful. + * @throws TableNotFoundException The table was not found in the metadata database. * @throws DatabaseNotFoundException The database was not found in the metadata database. * @throws ContainerNotFoundException The container was not found. * @throws TableMalformedException The table seems malformed by the mapper. - * @throws UnitNotFoundException - * @throws ConceptNotFoundException */ TableColumn update(Long containerId, Long databaseId, Long tableId, Long columnId, ColumnSemanticsUpdateDto updateDto, String authorization) throws TableNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, - TableMalformedException, UnitNotFoundException, ConceptNotFoundException, SemanticEntityPersistException; + TableMalformedException, SemanticEntityPersistException, SemanticEntityNotFoundException; } diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java b/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..94cd509ae86317051719b0c6e8027ef7d8940d44 --- /dev/null +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java @@ -0,0 +1,107 @@ +package at.tuwien.service.impl; + +import at.tuwien.entities.database.table.columns.TableColumnConcept; +import at.tuwien.entities.database.table.columns.TableColumnUnit; +import at.tuwien.entities.semantics.Ontology; +import at.tuwien.exception.ConceptNotFoundException; +import at.tuwien.exception.SemanticEntityNotFoundException; +import at.tuwien.exception.UnitNotFoundException; +import at.tuwien.gateway.SemanticServiceGateway; +import at.tuwien.mapper.TableMapper; +import at.tuwien.repository.jpa.ConceptRepository; +import at.tuwien.repository.jpa.OntologyRepository; +import at.tuwien.repository.jpa.UnitRepository; +import at.tuwien.service.SemanticService; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Log4j2 +@Service +public class SemanticServiceImpl implements SemanticService { + + private final TableMapper tableMapper; + private final UnitRepository unitRepository; + private final ConceptRepository conceptRepository; + private final OntologyRepository ontologyRepository; + private final SemanticServiceGateway semanticServiceGateway; + + @Autowired + public SemanticServiceImpl(TableMapper tableMapper, UnitRepository unitRepository, ConceptRepository conceptRepository, + OntologyRepository ontologyRepository, SemanticServiceGateway semanticServiceGateway) { + this.tableMapper = tableMapper; + this.unitRepository = unitRepository; + this.conceptRepository = conceptRepository; + this.ontologyRepository = ontologyRepository; + this.semanticServiceGateway = semanticServiceGateway; + } + + @Override + public TableColumnConcept findConcept(String uri) throws ConceptNotFoundException { + final Optional<TableColumnConcept> optional = conceptRepository.findById(uri); + if (optional.isEmpty()) { + log.error("Failed to find column concept with uri {}", uri); + throw new ConceptNotFoundException("Failed to find concept with uri " + uri); + } + return optional.get(); + } + + @Override + public TableColumnUnit findUnit(String uri) throws UnitNotFoundException { + final Optional<TableColumnUnit> optional = unitRepository.findById(uri); + if (optional.isEmpty()) { + log.error("Failed to find unit with uri {}", uri); + throw new UnitNotFoundException("Failed to find unit"); + } + return optional.get(); + } + + @Override + public TableColumnConcept saveConcept(String uri, String authorization) throws SemanticEntityNotFoundException { + /* check compatible ontologies */ + final Ontology ontology = getCompatibleOntology(uri); + if (ontology == null) { + return TableColumnConcept.builder() + .uri(uri) + .build(); + } + final TableColumnConcept entity = tableMapper.entityDtoToTableColumnConcept(semanticServiceGateway.getEntity(ontology.getId(), uri, authorization)); + final TableColumnConcept concept = conceptRepository.save(entity); + log.debug("saved concept with uri {}", concept.getUri()); + log.trace("saved concept {}", concept.getUri()); + return concept; + } + + @Override + public TableColumnUnit saveUnit(String uri, String authorization) throws SemanticEntityNotFoundException { + final Ontology ontology = getCompatibleOntology(uri); + if (ontology == null) { + return TableColumnUnit.builder() + .uri(uri) + .build(); + } + final TableColumnUnit entity = tableMapper.entityDtoToTableColumnUnit(semanticServiceGateway.getEntity(ontology.getId(), uri, authorization)); + final TableColumnUnit unit = unitRepository.save(entity); + log.debug("saved unit with uri {}", unit.getUri()); + log.trace("saved unit {}", unit.getUri()); + return unit; + } + + private Ontology getCompatibleOntology(String uri) { + final List<Ontology> ontologies = ontologyRepository.findAll() + .stream() + .filter(o -> uri.startsWith(o.getUri())) + .toList(); + if (ontologies.size() != 1) { + log.warn("Failed to find registered ontology for entity with uri {}", uri); + return null; + } + final Ontology ontology = ontologies.get(0); + log.debug("found available compatible ontology with id {}", ontology.getId()); + return ontology; + } + +} 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 9f46855a19dd069a71e28bf25d05f7060df204d9..c0b1681e8b6da198b7e3fadcadd1c47ca6ab32ce 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 @@ -7,21 +7,14 @@ import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.columns.TableColumn; -import at.tuwien.entities.database.table.columns.TableColumnConcept; -import at.tuwien.entities.database.table.columns.TableColumnUnit; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.TableMapper; import at.tuwien.repository.elastic.TableColumnIdxRepository; import at.tuwien.repository.elastic.TableIdxRepository; -import at.tuwien.repository.jpa.ConceptRepository; import at.tuwien.repository.jpa.TableColumnRepository; import at.tuwien.repository.jpa.TableRepository; -import at.tuwien.repository.jpa.UnitRepository; -import at.tuwien.service.ContainerService; -import at.tuwien.service.DatabaseService; -import at.tuwien.service.TableService; -import at.tuwien.service.UserService; +import at.tuwien.service.*; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; @@ -41,28 +34,26 @@ public class TableServiceImpl extends HibernateConnector implements TableService private final TableMapper tableMapper; private final UserService userService; - private final UnitRepository unitRepository; - private final TableRepository tableRepository; private final DatabaseService databaseService; + private final SemanticService semanticService; + private final TableRepository tableRepository; private final ContainerService containerService; - private final ConceptRepository conceptRepository; private final TableIdxRepository tableIdxRepository; private final TableColumnRepository tableColumnRepository; private final TableColumnIdxRepository tableColumnIdxRepository; @Autowired - public TableServiceImpl(TableMapper tableMapper, UserService userService, UnitRepository unitRepository, + public TableServiceImpl(TableMapper tableMapper, UserService userService, SemanticService semanticService, TableRepository tableRepository, DatabaseService databaseService, - ContainerService containerService, ConceptRepository conceptRepository, - TableIdxRepository tableIdxRepository, TableColumnRepository tableColumnRepository, + ContainerService containerService, TableIdxRepository tableIdxRepository, + TableColumnRepository tableColumnRepository, TableColumnIdxRepository tableColumnIdxRepository) { this.tableMapper = tableMapper; this.userService = userService; - this.unitRepository = unitRepository; + this.semanticService = semanticService; this.tableRepository = tableRepository; this.databaseService = databaseService; this.containerService = containerService; - this.conceptRepository = conceptRepository; this.tableIdxRepository = tableIdxRepository; this.tableColumnRepository = tableColumnRepository; this.tableColumnIdxRepository = tableColumnIdxRepository; @@ -214,24 +205,28 @@ public class TableServiceImpl extends HibernateConnector implements TableService public TableColumn update(Long containerId, Long databaseId, Long tableId, Long columnId, ColumnSemanticsUpdateDto updateDto, String authorization) throws TableNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, - TableMalformedException, UnitNotFoundException, ConceptNotFoundException { + TableMalformedException, SemanticEntityNotFoundException { final Table table = findById(containerId, databaseId, tableId); final TableColumn column = findColumn(table, columnId); /* assign */ if (updateDto.getUnitUri() != null) { - unitRepository.save(tableMapper.columnSemanticsUpdateDtoToTableColumnUnit(updateDto)); - final TableColumnUnit unit = findUnit(updateDto.getUnitUri()); - column.setUnit(unit); - log.debug("update unit of column, unit={}, column={}", unit, column); + try { + column.setUnit(semanticService.findUnit(updateDto.getUnitUri())); + } catch (UnitNotFoundException e) { + log.warn("Unit with uri {} not found in metadata database", updateDto.getUnitUri()); + column.setUnit(semanticService.saveUnit(updateDto.getUnitUri(), authorization)); + } } else { column.setUnit(null); log.debug("remove unit of column, column={}", column); } if (updateDto.getConceptUri() != null) { - conceptRepository.save(tableMapper.columnSemanticsUpdateDtoToTableColumnConcept(updateDto)); - final TableColumnConcept concept = findConcept(updateDto.getConceptUri()); - column.setConcept(concept); - log.debug("update ColumnConcept of column, concept={}, column={}", concept, column); + try { + column.setConcept(semanticService.findConcept(updateDto.getConceptUri())); + } catch (ConceptNotFoundException e) { + log.warn("Concept with uri {} not found in metadata database", updateDto.getConceptUri()); + column.setConcept(semanticService.saveConcept(updateDto.getConceptUri(), authorization)); + } } else { column.setConcept(null); log.debug("remove ColumnConcept of column, column={}", column); @@ -267,36 +262,4 @@ public class TableServiceImpl extends HibernateConnector implements TableService return optional.get(); } - /** - * Finds a ColumnConcept with given uri - * - * @param uri The uri. - * @return The concept, if successful. - * @throws ConceptNotFoundException The ColumnConcept was not found in the metadata database. - */ - protected TableColumnConcept findConcept(String uri) throws ConceptNotFoundException { - final Optional<TableColumnConcept> optional = conceptRepository.findById(uri); - if (optional.isEmpty()) { - log.error("Failed to find column concept with uri {}", uri); - throw new ConceptNotFoundException("Failed to find concept with uri " + uri); - } - return optional.get(); - } - - /** - * Finds a unit with given uri - * - * @param uri The uri. - * @return The unit, if successful. - * @throws UnitNotFoundException The unit was not found in the metadata database. - */ - protected TableColumnUnit findUnit(String uri) throws UnitNotFoundException { - final Optional<TableColumnUnit> optional = unitRepository.findById(uri); - if (optional.isEmpty()) { - log.error("Failed to find unit with uri {}", uri); - throw new UnitNotFoundException("Failed to find unit"); - } - return optional.get(); - } - } diff --git a/dbrepo-ui/.env.example b/dbrepo-ui/.env.example index 53a7f375f56dcfcd53e0d399c4600bc699ecaf27..c6d247fe1847baa4328456c02f75578325fbb24e 100644 --- a/dbrepo-ui/.env.example +++ b/dbrepo-ui/.env.example @@ -13,3 +13,4 @@ SANDBOX=false SHARED_FILESYSTEM="/tmp" TITLE="Database Repository" VERSION="latest" +GIT_HASH="deadbeef" diff --git a/dbrepo-ui/Dockerfile b/dbrepo-ui/Dockerfile index 582126fc3e19d3822e440c9675083c3047bc1f20..f3b107c8d845d63d085fc96dcf04e85be47e8dfe 100644 --- a/dbrepo-ui/Dockerfile +++ b/dbrepo-ui/Dockerfile @@ -57,6 +57,7 @@ ENV VERSION="${TAG}" ENV TITLE="Database Repository" ENV ICON="/favicon.ico" ENV DBREPO_CLIENT_SECRET="client-secret" +ENV GIT_HASH="deadbeef" WORKDIR /app diff --git a/dbrepo-ui/api/semantic.service.js b/dbrepo-ui/api/semantic.service.js index 0c9f01b7db6973e64ebe4f7c65f170deea8c7bb8..01b8b021a3fc86bde81b7ad9a92d383591d35591 100644 --- a/dbrepo-ui/api/semantic.service.js +++ b/dbrepo-ui/api/semantic.service.js @@ -36,6 +36,23 @@ class SemanticService { }) } + updateConcept (data) { + return new Promise((resolve, reject) => { + api.put('/api/semantic/concept', data, { headers: { Accept: 'application/json' } }) + .then((response) => { + const concept = response.data + console.debug('response concept', concept) + resolve(concept) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to update concept', error) + Vue.$toast.error(`[${code}] Failed to update concept: ${message}`) + reject(error) + }) + }) + } + findAllUnits () { return new Promise((resolve, reject) => { api.get('/api/semantic/unit', { headers: { Accept: 'application/json' } }) @@ -53,6 +70,23 @@ class SemanticService { }) } + updateUnit (data) { + return new Promise((resolve, reject) => { + api.put('/api/semantic/unit', data, { headers: { Accept: 'application/json' } }) + .then((response) => { + const unit = response.data + console.debug('response unit', unit) + resolve(unit) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to update unit', error) + Vue.$toast.error(`[${code}] Failed to update unit: ${message}`) + reject(error) + }) + }) + } + findOntology (id) { return new Promise((resolve, reject) => { api.get(`/api/semantic/ontology/${id}`, { headers: { Accept: 'application/json' } }) diff --git a/dbrepo-ui/components/dialogs/EditSemantics.vue b/dbrepo-ui/components/dialogs/EditSemantics.vue deleted file mode 100644 index 5debbc5840e681825688d2de715b9a0f7e943cb1..0000000000000000000000000000000000000000 --- a/dbrepo-ui/components/dialogs/EditSemantics.vue +++ /dev/null @@ -1,96 +0,0 @@ -<template> - <div> - <v-card> - <v-card-title>Edit</v-card-title> - <v-card-text> - <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit"> - <v-row> - <v-col> - <v-text-field - v-model="semanticDto.name" - clearable - label="Name" /> - </v-col> - </v-row> - <v-row> - <v-col> - <v-text-field - v-model="semanticDto.uri" - clearable - label="URI" - :rules="[v => !!v || $t('Required')]" - required /> - </v-col> - </v-row> - </v-form> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn - class="mb-2" - @click="cancel"> - Cancel - </v-btn> - <v-btn - color="primary" - class="mb-2 mr-2" - :disabled="!valid" - :loading="loadingSave" - @click="save"> - Save - </v-btn> - </v-card-actions> - </v-card> - </div> -</template> - -<script> -import SemanticService from '@/api/semantic.service' - -export default { - props: { - entity: { - type: Object, - default () { - return {} - } - } - }, - data () { - return { - valid: false, - localEntity: null, - loadingSave: false, - semanticDto: { - name: null, - uri: null - } - } - }, - computed: { - }, - watch: { - entity () { - this.semanticDto.name = this.entity.name - this.semanticDto.uri = this.entity.uri - } - }, - mounted () { - this.semanticDto.name = this.entity.name - this.semanticDto.uri = this.entity.uri - }, - methods: { - cancel () { - this.$emit('close', { success: false, action: 'cancel' }) - }, - save () { - SemanticService.u - }, - submit () { - this.$refs.form.validate() - } - } -} -</script> -<style scoped> -</style> diff --git a/dbrepo-ui/components/dialogs/Semantics.vue b/dbrepo-ui/components/dialogs/Semantics.vue index 97910f2690ca31bf18d409a0c1d12e67715f7e7b..caf59080020368b8c0989eaa9874ed42169d901c 100644 --- a/dbrepo-ui/components/dialogs/Semantics.vue +++ b/dbrepo-ui/components/dialogs/Semantics.vue @@ -1,37 +1,61 @@ <template> <div> <v-card> - <v-card-title>Assign Semantic Information</v-card-title> - <v-card-subtitle v-if="!loadingSemantics"> - Recommend <strong v-text="recommendations.length" /> {{ recommendations.length === 1 ? 'entity' : 'entities' }} that matches the column name - </v-card-subtitle> + <v-progress-linear + v-if="loadingSemantics" + color="primary" + indeterminate /> + <v-card-title v-text="column.name" /> + <v-card-subtitle v-if="loadingSemantics && !semanticEntity">Loading semantic recommendations ...</v-card-subtitle> + <v-card-text> + <v-alert + v-if="!entity" + border="left" + color="info" + dark + icon="mdi-share-variant" + class="pl-6"> + <p> + The following ontologies automatically will query the fields <code>rdfs:label</code> and + <code>schema:description</code> and store it for this column. You can still use other URIs that are not + matching these ontologies, the URI will be displayed instead. + </p> + <div v-for="(item,idx) in ontologies" :key="idx"> + <a :href="item.uri" target="_blank" v-text="item.uri" /> + </div> + </v-alert> + <v-alert + v-if="entity" + border="left" + color="primary" + dark + icon="mdi-share-variant" + class="pl-6"> + <div> + <a :href="entity.uri" class="white--text" target="_blank" v-text="entity.name ? entity.name : entity.uri" /> + </div> + <div v-text="entity.description" /> + </v-alert> + </v-card-text> <v-card-text> <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit"> - <v-row> - <v-col> - <v-progress-linear - v-if="loadingSemantics" - color="secondary" - indeterminate /> - <v-list-item-group v-if="!loadingSemantics" v-model="recommendation"> - <v-list-item v-for="(item,idx) in recommendations" :key="idx" three-line> - <template v-slot:default="{ active, }"> - <v-list-item-action> - <v-checkbox - :input-value="active" - color="primary" /> - </v-list-item-action> - <v-list-item-content> - <v-list-item-title v-text="item.label" /> - <v-list-item-subtitle v-text="item.uri" /> - <v-list-item-subtitle class="mt-1" v-text="item.comment" /> - </v-list-item-content> - </template> - </v-list-item> - </v-list-item-group> - </v-col> - </v-row> - <v-row> + <v-list-item-group v-if="!loadingSemantics" v-model="recommendation"> + <v-list-item v-for="(item,idx) in recommendations" :key="idx" three-line> + <template v-slot:default="{ active, }"> + <v-list-item-action> + <v-checkbox + :input-value="active" + color="primary" /> + </v-list-item-action> + <v-list-item-content> + <v-list-item-title v-text="item.label" /> + <v-list-item-subtitle v-text="item.uri" /> + <v-list-item-subtitle class="mt-1" v-text="item.description" /> + </v-list-item-content> + </template> + </v-list-item> + </v-list-item-group> + <v-row dense> <v-col> <v-text-field v-model="uri" @@ -99,24 +123,26 @@ export default { valid: false, loading: false, loadingOntologies: false, - loadingSemantics: false, - ontologies: [] + loadingSemantics: false } }, computed: { + ontologies () { + return this.$store.state.ontologies + }, + entity () { + if (!this.column[this.mode]) { + return null + } + return this.column[this.mode] + } }, watch: { column () { - this.loadSemantics() - if (this.column.unit && this.mode === 'unit') { - this.uri = this.column.unit.uri - return - } - if (this.column.concept && this.mode === 'concept') { - this.uri = this.column.concept.uri - return + if (!this.column[this.mode]) { + this.recommendSemantics() } - this.uri = null + this.init() }, recommendation (index) { if (!this.recommendations[index] || !('uri' in this.recommendations[index])) { @@ -127,8 +153,10 @@ export default { } }, mounted () { - this.loadOntologies() - this.loadSemantics() + this.init() + if (!this.column[this.mode]) { + this.recommendSemantics() + } }, methods: { cancel () { @@ -153,17 +181,7 @@ export default { this.loadingSave = false }) }, - loadOntologies () { - this.loadingOntologies = true - SemanticService.findAllOntologies() - .then((ontologies) => { - this.ontologies = ontologies - }) - .finally(() => { - this.loadingOntologies = false - }) - }, - loadSemantics () { + recommendSemantics () { this.loadingSemantics = true SemanticService.suggestTableColumn(this.database.id, this.tableId, this.column.id) .then((recommendations) => { @@ -179,6 +197,17 @@ export default { } return str.match(/https?:\/\//g) }, + init () { + if (this.column.unit && this.mode === 'unit') { + this.uri = this.column.unit.uri + return + } + if (this.column.concept && this.mode === 'concept') { + this.uri = this.column.concept.uri + return + } + this.uri = null + }, submit () { this.$refs.form.validate() } diff --git a/dbrepo-ui/components/dialogs/ViewSemanticEntity.vue b/dbrepo-ui/components/dialogs/ViewSemanticEntity.vue new file mode 100644 index 0000000000000000000000000000000000000000..6830ae76f44e5319b021b6afc9118fbcba99d252 --- /dev/null +++ b/dbrepo-ui/components/dialogs/ViewSemanticEntity.vue @@ -0,0 +1,72 @@ +<template> + <div> + <v-card> + <v-card-title v-text="entity.name" /> + <v-card-subtitle> + <a :href="entity.uri" target="_blank" v-text="entity.uri" /> + </v-card-subtitle> + <v-card-text> + <p v-text="description" /> + </v-card-text> + <div v-for="(item,idx) in entity.columns" :key="idx"> + <v-list-item-group> + <v-list-item two-line :to="link(item)"> + <v-list-item-content> + <v-list-item-title v-text="item.name" /> + <v-list-item-subtitle class="mt-2" v-text="link(item)" /> + </v-list-item-content> + </v-list-item> + </v-list-item-group> + </div> + <v-card-actions class="mt-2"> + <v-spacer /> + <v-btn + class="mb-2" + @click="cancel"> + Cancel + </v-btn> + </v-card-actions> + </v-card> + </div> +</template> + +<script> +export default { + props: { + mode: { + type: String, + default () { + return 'unit' + } + }, + entity: { + type: Object, + default () { + return {} + } + } + }, + data () { + return { + } + }, + computed: { + description () { + if (!this.entity.description) { + return '(no description)' + } + return this.entity.description + } + }, + methods: { + cancel () { + this.$emit('close', { success: false, action: 'cancel' }) + }, + link (item) { + return `/container/${item.database_id}/database/${item.database_id}/table/${item.table_id}/schema` + } + } +} +</script> +<style scoped> +</style> diff --git a/dbrepo-ui/config.js b/dbrepo-ui/config.js index d0222abf070e693cb8c884e89b1cb28484abd18a..bc199503b7207296f1a4828012d5cf9417329da6 100644 --- a/dbrepo-ui/config.js +++ b/dbrepo-ui/config.js @@ -17,5 +17,6 @@ config.elasticPassword = process.env.ELASTIC_PASSWORD || 'elastic' config.clientSecret = process.env.DBREPO_CLIENT_SECRET || 'MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG' config.defaultPublisher = process.env.DEFAULT_PID_PUBLISHER || 'Technische Universität Wien' config.doiUrl = process.env.DOI_URL || 'https://doi.org' +config.gitHash = process.env.GIT_HASH || 'deadbeef' module.exports = config diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index 75eaccafae909ac1b042761d3c825d927fe18a75..30596e71b7a544decbfacd93c71eb9d1cb10587a 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -10,7 +10,7 @@ <v-list-item class="mt-2"> <v-list-item-content> <v-list-item-subtitle> - {{ version }} + {{ version }} ({{ gitHash }}) </v-list-item-subtitle> <v-list-item-title class="text-h6"> Database Repository @@ -183,6 +183,9 @@ export default { version () { return this.$config.version }, + gitHash () { + return this.$config.gitHash + }, canListOntologies () { if (!this.roles) { return false diff --git a/dbrepo-ui/nuxt.config.js b/dbrepo-ui/nuxt.config.js index 9db2e6dc80c7f97dd3a17dca49d279c9bfc241a8..fee9bcc80148bcbb1f04974a4875c88d56762c6f 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, baseUrl } from './config' +import { api, icon, search, clientSecret, title, sandbox, logo, version, defaultPublisher, doiUrl, baseUrl, gitHash } from './config' const proxy = {} @@ -94,7 +94,8 @@ export default { clientSecret, defaultPublisher, doiUrl, - baseUrl + baseUrl, + gitHash }, serverMiddleware: [ diff --git a/dbrepo-ui/pages/semantic/index.vue b/dbrepo-ui/pages/semantic/index.vue index a9362fb6d3fd8dc25184604e3da5934f6d9dd117..63353bb5abbb58474570da5d79d6326b153bdcee 100644 --- a/dbrepo-ui/pages/semantic/index.vue +++ b/dbrepo-ui/pages/semantic/index.vue @@ -25,39 +25,39 @@ <a :href="item.uri" target="_blank" v-text="item.uri" /> </template> <template v-slot:item.action="{ item }"> - <v-btn small @click="edit(item)"> - Edit + <v-btn small :disabled="disabled(item)" @click="view(item)"> + Usages </v-btn> </template> </v-data-table> </v-card-text> </v-card> <v-dialog - v-model="editSemanticDialog" - persistent + v-model="viewSemanticEntityDialog" max-width="640"> - <EditSemantics :entity="entity" @close="close" /> + <ViewSemanticEntity :mode="mode" :entity="entity" @close="close" /> </v-dialog> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> </template> <script> import SemanticService from '@/api/semantic.service' -import EditSemantics from '@/components/dialogs/EditSemantics.vue' +import ViewSemanticEntity from '@/components/dialogs/ViewSemanticEntity.vue' export default { components: { - EditSemantics + ViewSemanticEntity }, data () { return { loadingConcepts: false, loadingUnits: false, entity: null, - editSemanticDialog: false, + viewSemanticEntityDialog: false, headers: [ - { text: 'Name', value: 'name' }, { text: 'URI', value: 'uri' }, + { text: 'Name', value: 'name' }, + { text: 'Description', value: 'description' }, { text: 'Usages', value: 'usages' }, { text: null, value: 'action' } ], @@ -97,6 +97,9 @@ export default { rows () { return this.tab === 0 ? this.concepts : this.units }, + mode () { + return this.tab === 0 ? 'concept' : 'unit' + }, canListOntologies () { if (!this.roles) { return false @@ -137,12 +140,20 @@ export default { this.loadingUnits = false }) }, - edit (entity) { + disabled (item) { + return !item.usages || this.usages === 0 + }, + view (entity) { this.entity = entity - this.editSemanticDialog = true + this.viewSemanticEntityDialog = true }, close (event) { - this.editSemanticDialog = false + if (this.mode === 'unit') { + this.loadUnits() + } else if (this.mode === 'concept') { + this.loadConcepts() + } + this.viewSemanticEntityDialog = false } } } diff --git a/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue b/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue index f8e486420b21bd4872a217dad78c22f022770982..a9b4422d3a2867b9da47e3e8f80160dacd96576f 100644 --- a/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue +++ b/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue @@ -1,9 +1,16 @@ <template> <div v-if="canListOntologies"> <v-toolbar flat> + <v-toolbar-title> + <v-btn id="back-btn" plain class="mr-2" to="/semantic/ontology"> + <v-icon left>mdi-arrow-left</v-icon> + </v-btn> + </v-toolbar-title> <v-toolbar-title> <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> - <span v-if="!loading" v-text="title" /> + <span v-if="!loading"> + Ontology <a v-if="ontology" :href="ontology.uri" target="_blank" v-text="ontology.uri" /> + </span> </v-toolbar-title> <v-spacer /> <v-toolbar-title> @@ -117,12 +124,6 @@ export default { ontologies () { return this.$store.state.ontologies }, - title () { - if (!this.ontology) { - return 'Ontology' - } - return `Ontology ${this.ontology.prefix}` - }, canListOntologies () { if (!this.roles) { return false @@ -156,7 +157,7 @@ export default { SemanticService.unregisterOntology(this.$route.params.ontology_id) .then(async () => { await this.$store.dispatch('reloadOntologies') - await this.$router.push('/ontology') + await this.$router.push('/semantic/ontology') }) .finally(() => { this.loadingDelete = false diff --git a/dbrepo-ui/pages/semantic/ontology/index.vue b/dbrepo-ui/pages/semantic/ontology/index.vue index dcb79ccc929815d55a6e30ea4f1b21b4c46d5ab6..b39800ec4150a80944c9ff438582ecb2a368e993 100644 --- a/dbrepo-ui/pages/semantic/ontology/index.vue +++ b/dbrepo-ui/pages/semantic/ontology/index.vue @@ -1,6 +1,11 @@ <template> <div v-if="canListOntologies"> <v-toolbar flat> + <v-toolbar-title> + <v-btn id="back-btn" plain class="mr-2" to="/semantic"> + <v-icon left>mdi-arrow-left</v-icon> + </v-btn> + </v-toolbar-title> <v-toolbar-title>{{ ontologies.length }} {{ $t('layout.ontologies', { name: 'vue-i18n' }) }}</v-toolbar-title> <v-spacer /> <v-toolbar-title>