diff --git a/.env.unix.example b/.env.unix.example index b352e9a482a5f582dd36e11aa32700e81ebcd824..930e4f7ba4640f056e13aceb2d02dac45261cb7c 100644 --- a/.env.unix.example +++ b/.env.unix.example @@ -20,4 +20,9 @@ SHARED_FILESYSTEM=/tmp LOG_LEVEL=trace # error, warning, info, debug, trace DEFAULT_ROLES=ROLE_RESEARCHER SUPERUSERS=user1,user2 -ELASTIC_PASSWORD=elastic \ No newline at end of file +ELASTIC_PASSWORD=elastic +DOI_URL=https://doi.org +DATACITE_URL=https://api.datacite.org +DATACITE_PREFIX=<prefix> +DATACITE_USERNAME=<username> +DATACITE_PASSWORD=<password> \ No newline at end of file diff --git a/.env.win.example b/.env.win.example index cf27aca493364a3665bd177230f9028384a2413a..3c26a4505b85400196878fb5c2e7ccb8b750c1df 100644 --- a/.env.win.example +++ b/.env.win.example @@ -20,4 +20,9 @@ SHARED_FILESYSTEM=C:\tmp LOG_LEVEL=trace # error, warning, info, debug, trace DEFAULT_ROLES=ROLE_RESEARCHER SUPERUSERS=user1,user2 -ELASTIC_PASSWORD=elastic \ No newline at end of file +ELASTIC_PASSWORD=elastic +DOI_URL=https://doi.org +DATACITE_URL=https://api.datacite.org +DATACITE_PREFIX=<prefix> +DATACITE_USERNAME=<username> +DATACITE_PASSWORD=<password> \ No newline at end of file diff --git a/.gitignore b/.gitignore index cebd8bbc63ddab78c25b3db0b3d3e34f3519f545..dcecc1a35265d9e5d9c94e65edc5edd348986114 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ -dbrepo-* +# dbrepo-* # Notebooks .jupyter/ diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteBody.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteBody.java new file mode 100644 index 0000000000000000000000000000000000000000..f1c434179a00b9a1383e38a5c900f2894cb64249 --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteBody.java @@ -0,0 +1,15 @@ +package at.tuwien.datacite; + +import lombok.*; + +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteBody<T> implements Serializable { + + private DataCiteData<T> data; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteData.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteData.java new file mode 100644 index 0000000000000000000000000000000000000000..84350e4ab9680e255590b9c9ec4b76705a3eeec9 --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteData.java @@ -0,0 +1,20 @@ +package at.tuwien.datacite; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; + +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class DataCiteData<T> implements Serializable { + + private String id; + private String type; + private T attributes; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteCreateDoi.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteCreateDoi.java new file mode 100644 index 0000000000000000000000000000000000000000..8a9d1c1b9f4801160debb9b64390c916b5708af8 --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteCreateDoi.java @@ -0,0 +1,44 @@ +package at.tuwien.datacite.doi; + +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteCreateDoi implements Serializable { + + private String url; + + private String prefix; + + private DataCiteDoiTypes types; + + private DataCiteDoiEvent event; + + private List<DataCiteDoiTitle> titles; + + @NotBlank + private String publisher; + + @NotNull + private Integer publicationYear; + + private Integer publicationMonth; + + private Integer publicationDay; + + private String language; + + private List<DataCiteDoiRights> rightsList; + + private List<DataCiteDoiCreator> creators; + + private List<DataCiteDoiRelatedIdentifier> relatedIdentifiers; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoi.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoi.java new file mode 100644 index 0000000000000000000000000000000000000000..1890aae2fd324e075d4141e99f17c06f28b23ecc --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoi.java @@ -0,0 +1,17 @@ +package at.tuwien.datacite.doi; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class DataCiteDoi implements Serializable { + + private String doi; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreator.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..d42fdec34746836859fa18af7dfbf43f94b23a1a --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreator.java @@ -0,0 +1,26 @@ +package at.tuwien.datacite.doi; + +import lombok.*; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; +import java.util.List; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteDoiCreator implements Serializable { + + @NotBlank + private String name; + + private String givenName; + + private String familyName; + + private List<DataCiteDoiCreatorAffiliation> affiliation; + + private List<DataCiteDoiCreatorNameIdentifier> nameIdentifiers; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorAffiliation.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorAffiliation.java new file mode 100644 index 0000000000000000000000000000000000000000..4b598cb33bca49a287ac0918eee671a184c61532 --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorAffiliation.java @@ -0,0 +1,21 @@ +package at.tuwien.datacite.doi; + +import lombok.*; + +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteDoiCreatorAffiliation implements Serializable { + + private String affiliationIdentifier; + + private String affiliationScheme; + + private String name; + + private String schemeUri; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorNameIdentifier.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorNameIdentifier.java new file mode 100644 index 0000000000000000000000000000000000000000..fe12496fc159244c20259685f7b7f4796fd0565c --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorNameIdentifier.java @@ -0,0 +1,19 @@ +package at.tuwien.datacite.doi; + +import lombok.*; + +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteDoiCreatorNameIdentifier implements Serializable { + + private String schemeUri; + + private String nameIdentifier; + + private String nameIdentifierScheme; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiEvent.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..24925a4f08286c9c59533628cc21e7c0e26375bb --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiEvent.java @@ -0,0 +1,33 @@ +package at.tuwien.datacite.doi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + + +@Getter +public enum DataCiteDoiEvent implements Serializable { + + @JsonProperty("publish") + PUBLISH("publish"), + + @JsonProperty("register") + REGISTER("register"), + + @JsonProperty("hide") + HIDE("hide"); + + private final String name; + + DataCiteDoiEvent(String name) { + this.name = name; + } + + @Override + public String toString() { + return this.name; + } +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRelatedIdentifier.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRelatedIdentifier.java new file mode 100644 index 0000000000000000000000000000000000000000..3f4a4211d9428cad2c9bf165d4e2793c6950cdef --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRelatedIdentifier.java @@ -0,0 +1,21 @@ +package at.tuwien.datacite.doi; + +import lombok.*; + +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteDoiRelatedIdentifier implements Serializable { + + private String relatedIdentifier; + + private String relatedIdentifierType; + + private String relationType; + + private String resourceTypeGeneral; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRights.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRights.java new file mode 100644 index 0000000000000000000000000000000000000000..b48826fa13cc5be41eb8a0c0672a9a2d09acb220 --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRights.java @@ -0,0 +1,19 @@ +package at.tuwien.datacite.doi; + +import lombok.*; + +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteDoiRights implements Serializable { + + private String rights; + + private String rightsUri; + + private String lang; +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTitle.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTitle.java new file mode 100644 index 0000000000000000000000000000000000000000..c19a37036e1c07ce4dc7c6197b7939d183c6f97a --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTitle.java @@ -0,0 +1,48 @@ +package at.tuwien.datacite.doi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteDoiTitle implements Serializable { + + @NotBlank + private String title; + + private Type titleType; + + private String lang; + + public enum Type { + + @JsonProperty("AlternativeTitle") + ALTERNATIVE_TITLE("AlternativeTitle"), + + @JsonProperty("Subtitle") + SUBTITLE("Subtitle"), + + @JsonProperty("TranslatedTitle") + TRANSLATED_TITLE("TranslatedTitle"), + + @JsonProperty("Other") + OTHER("Other"); + + private final String name; + + Type(String name) { + this.name = name; + } + + @Override + public String toString() { + return this.name; + } + } +} diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTypes.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTypes.java new file mode 100644 index 0000000000000000000000000000000000000000..deb7d155f4df1c206fc6cbbfa4fc86d043541165 --- /dev/null +++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTypes.java @@ -0,0 +1,29 @@ +package at.tuwien.datacite.doi; + +import lombok.*; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DataCiteDoiTypes implements Serializable { + + public static final DataCiteDoiTypes DATASET = DataCiteDoiTypes.builder().resourceTypeGeneral("Dataset").build(); + + @NotNull + private String resourceTypeGeneral; + + private String resourceType; + + private String schemaOrg; + + private String bibtex; + + private String citeproc; + + private String ris; +} 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 8826a455e6903339ac54f7cde0b9326feaf3b94e..4d9a5defbf76ef93e0b84b7737b8eca4d4b83f97 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 @@ -172,7 +172,7 @@ public class IdentifierEndpoint extends AbstractEndpoint { }) public ResponseEntity<IdentifierDto> update(@NotNull @PathVariable("id") Long id, @NotNull @Valid @RequestBody IdentifierDto data) - throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException { + throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException, IdentifierRequestException { log.debug("endpoint update identifier, id={}, data={}", id, data); final Identifier identifier = identifierService.update(id, data); return ResponseEntity.accepted() @@ -195,7 +195,7 @@ public class IdentifierEndpoint extends AbstractEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<?> delete(@NotNull @PathVariable("id") Long id) - throws IdentifierNotFoundException { + throws IdentifierNotFoundException, NotAllowedException { log.debug("endpoint delete identifier, id={}", id); identifierService.delete(id); return ResponseEntity.accepted() diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/application-doi.yml b/dbrepo-identifier-service/rest-service/src/main/resources/application-doi.yml new file mode 100644 index 0000000000000000000000000000000000000000..4a6687e0e807f7fbb2243078053bc4641a900368 --- /dev/null +++ b/dbrepo-identifier-service/rest-service/src/main/resources/application-doi.yml @@ -0,0 +1,6 @@ +fda: + datacite: + url: "${DATACITE_URL}" + prefix: "${DATACITE_PREFIX}" + username: "${DATACITE_USERNAME}" + password: "${DATACITE_PASSWORD}" \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt index 13cf643dae05b0e93e8c77d68a280b4d0f5260ad..8af282e645262dd7f8dfa8c17826e1d4c4fd43a6 100644 --- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt +++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt @@ -1 +1 @@ -[# th:each="creator,idx: ${creators}"][# th:if="${idx.index} > 0 and ${idx.index} != ${idx.size} - 1 and ${idx.index} != 0"], [/][# th:if="${idx.index} == ${idx.size} - 1 and ${idx.size} != 1"] & [/][[${creator.firstname.substring(0,1)}]]., [[${creator.lastname}]][/][# th:if="${creators.size()} > 0"]. [/]([[${publicationYear}]]). [[${title}]]. [[${publisher}]]. [[${doi}]] \ No newline at end of file +[# th:each="creator,idx: ${creators}"][# th:if="${idx.index} > 0 and ${idx.index} != ${idx.size} - 1 and ${idx.index} != 0"], [/][# th:if="${idx.index} == ${idx.size} - 1 and ${idx.size} != 1"] & [/][[${creator.firstname.substring(0,1)}]]., [[${creator.lastname}]][/][# th:if="${creators.size()} > 0"]. [/]([[${publicationYear}]]). [[${title}]]. [[${publisher}]]. [[${identifierType} == 'doi' ? 'https://doi.org/' : '']][[${identifier}]] \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt index 3ffe19fef7193059855155a43c65df28de45059b..ad48a2bd59fb10d5e25acc9323288c6c16e0df49 100644 --- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt +++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt @@ -1,7 +1,7 @@ @misc{dbrepo[[${publicationYear}]], author = {[# th:each="creator,idx: ${creators}"][# th:if="${idx.index} > 0"] and [/][[${creator.lastname}]], [[${creator.firstname}]][/]}, title = {[[${title}]]}, - howpublished = {\url{[[${doi}]]}}, + [[${identifierType}]] = {[[${identifierType} == 'url' ? '\url{' : '']][[${identifier}]][[${identifierType} == 'url' ? '}' : '']]}, month = {[[${publicationMonth}]]}, year = {[[${publicationYear}]]} } \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt index 67f9e13035097bb4587d2f3ff1a5199ff4d65996..602b73deed44278af106e3a946a1b1aa55996c36 100644 --- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt +++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt @@ -1 +1 @@ -[1] [# th:each="creator,idx: ${creators}"][# th:if="${idx.index} > 0"] and [/][[${creator.firstname.substring(0,1)}]]. [[${creator.lastname}]][/][# th:if="${creators.size()} > 0"], [/]“[[${title}]]“, [[${publisher}]], [[${publicationYear}]], doi: [[${doi}]]. \ No newline at end of file +[1] [# th:each="creator,idx: ${creators}"][# th:if="${idx.index} > 0"] and [/][[${creator.firstname.substring(0,1)}]]. [[${creator.lastname}]][/][# th:if="${creators.size()} > 0"], [/]“[[${title}]]“, [[${publisher}]], [[${publicationYear}]], [[${identifierType} == 'url' ? 'Available: ' : '']][[${identifier}]]. \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml b/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml index 48a23436cb5faa1d71d4ec6e2ceb8b9f05a6948d..3d52208f994c8d145a2d2bd734e10f6b0089de47 100644 --- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml +++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml @@ -1,11 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> <resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://datacite.org/schema/kernel-4" xsi:schemaLocation="http://datacite.org/schema/kernel-4 https://schema.datacite.org/meta/kernel-4.4/metadata.xsd"> - <identifier identifierType="PID">[[${doi}]]</identifier><creators th:if="${not #lists.isEmpty(creators)}"> + <identifier th:attr="identifierType=${identifierType}">[[${identifier}]]</identifier> + <creators th:if="${not #lists.isEmpty(creators)}"> <creator th:each="creator: ${creators}"> <creatorName nameType="Personal">[[${creator.lastname}]], [[${creator.firstname}]]</creatorName> <givenName>[[${creator.firstname}]]</givenName> - <familyName>[[${creator.lastname}]]</familyName><affiliation th:if="${creator.affiliation != null}">[[${creator.affiliation}]]</affiliation><nameIdentifier th:if="${creator.orcid != null}" schemeURI="https://orcid.org" nameIdentifierScheme="ORCID">[[${creator.orcid}]]</nameIdentifier> + <familyName>[[${creator.lastname}]]</familyName> + <nameIdentifier th:if="${creator.orcid != null}" schemeURI="https://orcid.org" nameIdentifierScheme="ORCID"> + [[${creator.orcid}]] + </nameIdentifier> + <affiliation th:if="${creator.affiliation != null}">[[${creator.affiliation}]]</affiliation> </creator> </creators> <titles> @@ -17,9 +22,12 @@ <date dateType="Issued">[[${created}]]</date> <date dateType="Available">[[${created}]]</date> </dates> - <resourceType resourceTypeGeneral="Dataset">Dataset</resourceType><relatedIdentifiers th:if="${not #lists.isEmpty(relatedIdentifiers)}"> + <resourceType resourceTypeGeneral="Dataset">Dataset</resourceType> + <relatedIdentifiers th:if="${not #lists.isEmpty(relatedIdentifiers)}"> <relatedIdentifier th:each="relatedIdentifier: ${relatedIdentifiers}" - th:attr="relatedIdentifierType=${relatedIdentifier.type},relationType=${relatedIdentifier.relation}">[[${relatedIdentifier.value}]]</relatedIdentifier> + th:attr="relatedIdentifierType=${relatedIdentifier.type},relationType=${relatedIdentifier.relation}"> + [[${relatedIdentifier.value}]] + </relatedIdentifier> </relatedIdentifiers> <descriptions th:if="${description != null}"> <description descriptionType="Abstract">[[${description}]]</description> 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 22fb7cf7ca355c2f60654d64a36d0d18581cfad0..8ad52e70ba3bd5fe30900d7a635e4fd0ee0f7c09 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 @@ -209,8 +209,6 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { .dbid(IDENTIFIER_1_DATABASE_ID) .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) - .doi(IDENTIFIER_1_DOI) - .visibility(IDENTIFIER_1_VISIBILITY_DTO) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) @@ -234,8 +232,6 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { .dbid(IDENTIFIER_1_DATABASE_ID) .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) - .doi(IDENTIFIER_1_DOI) - .visibility(IDENTIFIER_1_VISIBILITY_DTO) .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_2_CREATE_DTO)) .publicationDay(IDENTIFIER_2_PUBLICATION_DAY) .publicationMonth(IDENTIFIER_2_PUBLICATION_MONTH) @@ -297,7 +293,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) public void update_dataSteward_succeeds() throws IdentifierPublishingNotAllowedException, - IdentifierNotFoundException { + IdentifierNotFoundException, IdentifierRequestException { /* test */ generic_update(); @@ -328,7 +324,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void delete_dataSteward_succeeds() throws IdentifierNotFoundException { + public void delete_dataSteward_succeeds() throws IdentifierNotFoundException, NotAllowedException { /* test */ generic_delete(); @@ -397,7 +393,8 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { return persistenceEndpoint.find(IDENTIFIER_1_ID, accept); } - protected void generic_update() throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException { + protected void generic_update() + throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException, IdentifierRequestException { /* mock */ when(identifierService.update(IDENTIFIER_3_ID, IDENTIFIER_3_DTO)) @@ -419,7 +416,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest { assertEquals(IDENTIFIER_3_RESULT_HASH, body.getResultHash()); } - protected void generic_delete() throws IdentifierNotFoundException { + protected void generic_delete() throws IdentifierNotFoundException, NotAllowedException { /* mock */ doNothing() 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 50213b8fdaf27b010fc691608fb0e1af67d8fadf..491b3eb7c574a0222ec07b5c56ecaf182dd5cbe4 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 @@ -307,6 +307,25 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { assertEquals(compare, body); } + @Test + public void find_bibliographyApa4_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, + RemoteUnavailableException, IdentifierRequestException, IOException { + final String accept = "text/bibliography; style=apa"; + final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa4.txt"), + StandardCharsets.UTF_8); + + /* mock */ + when(identifierRepository.findById(IDENTIFIER_1_ID)) + .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); + + /* test */ + final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); + assertEquals(HttpStatus.OK, response.getStatusCode()); + final String body = (String) response.getBody(); + assertNotNull(body); + assertEquals(compare, body); + } + @Test public void find_bibliographyIeee0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { @@ -364,6 +383,25 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { assertEquals(compare, body); } + @Test + public void find_bibliographyIeee3_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, + RemoteUnavailableException, IdentifierRequestException, IOException { + final String accept = "text/bibliography; style=ieee"; + final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee3.txt"), + StandardCharsets.UTF_8); + + /* mock */ + when(identifierRepository.findById(IDENTIFIER_1_ID)) + .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); + + /* test */ + final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); + assertEquals(HttpStatus.OK, response.getStatusCode()); + final String body = (String) response.getBody(); + assertNotNull(body); + assertEquals(compare, body); + } + @Test public void find_bibliographyBibtex0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException, IOException { @@ -421,6 +459,25 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { assertEquals(compare, body); } + @Test + public void find_bibliographyBibtex3_succeeds() throws IdentifierNotFoundException, QueryNotFoundException, + RemoteUnavailableException, IdentifierRequestException, IOException { + final String accept = "text/bibliography; style=bibtex"; + final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex3.txt"), + StandardCharsets.UTF_8); + + /* mock */ + when(identifierRepository.findById(IDENTIFIER_1_ID)) + .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI)); + + /* test */ + final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept); + assertEquals(HttpStatus.OK, response.getStatusCode()); + final String body = (String) response.getBody(); + assertNotNull(body); + assertEquals(compare, body); + } + protected static String inputStreamToString(InputStream inputStream) throws IOException { return IOUtils.toString(inputStream, StandardCharsets.UTF_8); } 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 new file mode 100644 index 0000000000000000000000000000000000000000..21505d50dd165dc4353d2dd219b20b5ea17a3640 --- /dev/null +++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java @@ -0,0 +1,216 @@ +package at.tuwien.service; + +import at.tuwien.BaseUnitTest; +import at.tuwien.api.identifier.IdentifierCreateDto; +import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.config.DataCiteConfig; +import at.tuwien.config.EndpointConfig; +import at.tuwien.config.IndexInitializer; +import at.tuwien.datacite.DataCiteBody; +import at.tuwien.datacite.DataCiteData; +import at.tuwien.datacite.doi.DataCiteDoi; +import at.tuwien.entities.identifier.Identifier; +import at.tuwien.exception.*; +import at.tuwien.repository.jpa.ContainerRepository; +import at.tuwien.repository.jpa.DatabaseRepository; +import at.tuwien.repository.jpa.IdentifierRepository; +import at.tuwien.repository.jpa.ImageRepository; +import at.tuwien.service.impl.IdentifierServiceImpl; +import org.apache.http.auth.BasicUserPrincipal; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.security.Principal; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; + +@ExtendWith(SpringExtension.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +@SpringBootTest +@ActiveProfiles("doi") +public class DataCiteIdentifierServiceUnitTest extends BaseUnitTest { + + @MockBean + private IndexInitializer indexInitializer; + + @MockBean(answer = Answers.RETURNS_MOCKS) + private DataCiteConfig dataCiteConfig; + + @MockBean(answer = Answers.RETURNS_MOCKS) + private EndpointConfig endpointConfig; + + @Autowired + private ImageRepository imageRepository; + + @Autowired + private ContainerRepository containerRepository; + + @Autowired + private DatabaseRepository databaseRepository; + + @Autowired + private IdentifierRepository identifierRepository; + + @MockBean + private RestTemplate restTemplate; + + @MockBean(answer = Answers.RETURNS_SELF) + private RestTemplateBuilder restTemplateBuilder; + + @MockBean + private IdentifierServiceImpl identifierService; + + @Autowired + private IdentifierService dataCiteIdentifierService; + + @BeforeEach + public void beforeEach() { + imageRepository.save(IMAGE_1); + containerRepository.save(CONTAINER_1); + databaseRepository.save(DATABASE_1); + when(restTemplateBuilder.build()).thenReturn(restTemplate); + IDENTIFIER_1.setCreators(null); + } + + @Test + public void create_database_succeeds() + throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException, + QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException, + IdentifierRequestException { + final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); + final String bearer = "Bearer abcxyz"; + final DataCiteBody<DataCiteDoi> response = + new DataCiteBody<>(new DataCiteData<>(null, "dois", new DataCiteDoi(IDENTIFIER_1_DOI_NOT_NULL))); + + /* mock */ + when(identifierService.create(any(IdentifierCreateDto.class), eq(principal), eq(bearer))) + .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1)); + when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), + any(ParameterizedTypeReference.class))) + .thenReturn(ResponseEntity.status(HttpStatus.CREATED).body(response)); + + /* test */ + Identifier result = dataCiteIdentifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer); + assertTrue(identifierRepository.existsById(result.getId())); + assertEquals(IDENTIFIER_1_DOI_NOT_NULL, result.getDoi()); + } + + @Test + public void create_invalidMetadata_fails() + throws IdentifierAlreadyExistsException, UserNotFoundException, QueryNotFoundException, + DatabaseNotFoundException, RemoteUnavailableException, IdentifierPublishingNotAllowedException, + IdentifierRequestException { + final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); + final String bearer = "Bearer abcxyz"; + + /* mock */ + when(identifierService.create(any(IdentifierCreateDto.class), eq(principal), eq(bearer))) + .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1)); + when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), + any(ParameterizedTypeReference.class))) + .thenThrow(HttpClientErrorException.BadRequest.class); + + /* test */ + assertThrows(IdentifierRequestException.class, () -> { + dataCiteIdentifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer); + }); + assertEquals(0, identifierRepository.count()); + } + + @Test + public void create_restClientException_fails() + throws IdentifierAlreadyExistsException, UserNotFoundException, QueryNotFoundException, + DatabaseNotFoundException, RemoteUnavailableException, IdentifierPublishingNotAllowedException, + IdentifierRequestException { + final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); + final String bearer = "Bearer abcxyz"; + + /* mock */ + when(identifierService.create(any(IdentifierCreateDto.class), eq(principal), eq(bearer))) + .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1)); + when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), + any(ParameterizedTypeReference.class))) + .thenThrow(RestClientException.class); + + /* test */ + assertThrows(InternalError.class, () -> { + dataCiteIdentifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer); + }); + assertEquals(0, identifierRepository.count()); + } + + @Test + public void update_existing_succeeds() + throws IdentifierRequestException, IdentifierNotFoundException { + 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))) + .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); + assertTrue(identifierRepository.existsById(IDENTIFIER_1_ID)); + assertEquals(IDENTIFIER_1_DOI_NOT_NULL, result.getDoi()); + } + + @Test + public void update_invalidMetadata_fails() + throws IdentifierRequestException, IdentifierNotFoundException { + + /* mock */ + when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.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))) + .thenThrow(HttpClientErrorException.BadRequest.class); + + /* test */ + assertThrows(IdentifierRequestException.class, () -> { + dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO); + }); + assertEquals(0, identifierRepository.count()); + } + + @Test + public void update_restClientException_fails() + throws IdentifierRequestException, IdentifierNotFoundException { + + /* mock */ + when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.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))) + .thenThrow(RestClientException.class); + + /* test */ + assertThrows(InternalError.class, () -> { + dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO); + }); + 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 7a7663650241f6919bd18d741a487c61b98470c4..4133a24409469526e74a2a87bfca541f73dd4e07 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 @@ -3,18 +3,15 @@ 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.config.IndexInitializer; import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.identifier.RelatedIdentifier; -import at.tuwien.entities.identifier.VisibilityType; import at.tuwien.exception.*; import at.tuwien.gateway.QueryServiceGateway; import at.tuwien.repository.elastic.IdentifierIdxRepository; import at.tuwien.repository.jpa.*; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -86,7 +83,8 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { @Test public void create_subsetRelatedIdentifiers_succeeds() throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException, - QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException { + QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException, + IdentifierRequestException { final String bearer = "Bearer abcxyz"; /* mock */ @@ -139,7 +137,8 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { @Test @Transactional(readOnly = true) - public void update_succeeds() throws IdentifierNotFoundException, IdentifierPublishingNotAllowedException { + public void update_succeeds() + throws IdentifierNotFoundException, IdentifierPublishingNotAllowedException, IdentifierRequestException { /* mock */ when(identifierIdxRepository.save(any(IdentifierDto.class))) @@ -155,46 +154,7 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { } @Test - @Disabled("Multiple representation of the same entity") - public void publish_everyone_succeeds() throws IdentifierAlreadyPublishedException, IdentifierNotFoundException { - - /* mock */ - identifierRepository.save(IDENTIFIER_1); - identifierRepository.save(IDENTIFIER_2); - - /* mock */ - when(identifierIdxRepository.save(any(IdentifierDto.class))) - .thenReturn(IDENTIFIER_2_DTO); - - /* test */ - final Identifier response = identifierService.publish(IDENTIFIER_2_ID, VisibilityTypeDto.EVERYONE); - assertEquals(IDENTIFIER_2_ID, response.getId()); - assertEquals(IDENTIFIER_2_TITLE, response.getTitle()); - assertEquals(IDENTIFIER_2_DESCRIPTION, response.getDescription()); - assertEquals(IDENTIFIER_2_DOI, response.getDoi()); - assertEquals(IDENTIFIER_2_PUBLISHER, response.getPublisher()); - assertEquals(IDENTIFIER_2_CONTAINER_ID, response.getContainerId()); - assertEquals(IDENTIFIER_2_DATABASE_ID, response.getDatabaseId()); - assertEquals(IDENTIFIER_2_PUBLICATION_YEAR, response.getPublicationYear()); - assertEquals(IDENTIFIER_2_PUBLICATION_MONTH, response.getPublicationMonth()); - assertEquals(IDENTIFIER_2_PUBLICATION_DAY, response.getPublicationDay()); - assertEquals(VisibilityType.EVERYONE, response.getVisibility()); - } - - @Test - @Disabled("Constraint identifier") - public void publish_trusted_succeeds() throws IdentifierAlreadyPublishedException, IdentifierNotFoundException { - - /* mock */ - when(identifierIdxRepository.save(any(IdentifierDto.class))) - .thenReturn(IDENTIFIER_2_DTO); - - /* test */ - identifierService.publish(IDENTIFIER_2_ID, VisibilityTypeDto.TRUSTED); - } - - @Test - public void delete_succeeds() throws IdentifierNotFoundException { + public void delete_succeeds() throws IdentifierNotFoundException, NotAllowedException { /* mock */ doNothing() @@ -210,7 +170,7 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { /* test */ assertThrows(IdentifierNotFoundException.class, () -> { - identifierService.publish(IDENTIFIER_2_ID, VisibilityTypeDto.EVERYONE); + identifierService.delete(IDENTIFIER_2_ID); }); } 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 0ac6b1b7196b67bfe4941e9e77837190f9373b06..9301bd73535d5491fa766f23e728118abff59f22 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 @@ -188,65 +188,60 @@ public class IdentifierServiceUnitTest extends BaseUnitTest { } @Test - public void create_database_succeeds() - throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException, - QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException { - final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); - final String bearer = "Bearer abcxyz"; + public void update_doiChange_fails() { - /* mock */ - when(databaseService.find(DATABASE_1_ID)) - .thenReturn(DATABASE_1); - when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(QueryDto.class))) - .thenReturn(ResponseEntity.ok(QUERY_1_DTO)); - when(userService.findByUsername(USER_1_USERNAME)) - .thenReturn(USER_1); - when(identifierRepository.save(any(Identifier.class))) - .thenReturn(IDENTIFIER_1); - when(identifierIdxRepository.save(any(IdentifierDto.class))) - .thenReturn(IDENTIFIER_1_DTO); + 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 */ - identifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer); + assertThrows(IdentifierRequestException.class, () -> { + identifierService.update(IDENTIFIER_1_ID, identifierWithNewDoiDto); + }); } @Test - public void create_publicDatabaseTrustedDataset_fails() - throws DatabaseNotFoundException { - final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); - final String bearer = "Bearer abcxyz"; + public void update_notVisibleByEveryone_fails() { - /* mock */ - when(databaseService.find(DATABASE_3_ID)) - .thenReturn(DATABASE_3); - when(identifierRepository.save(any(Identifier.class))) - .thenReturn(IDENTIFIER_3); + 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(IdentifierPublishingNotAllowedException.class, () -> { - identifierService.create(IDENTIFIER_3_DTO_TRUSTED_REQUEST, principal, bearer); + assertThrows(IdentifierRequestException.class, () -> { + identifierService.update(IDENTIFIER_1_ID, identifierDto); }); } @Test - public void create_publicDatabaseSelfDataset_fails() - throws DatabaseNotFoundException { + public void create_database_succeeds() + throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException, + QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException, + IdentifierRequestException { final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); final String bearer = "Bearer abcxyz"; /* mock */ - when(databaseService.find(DATABASE_3_ID)) - .thenReturn(DATABASE_3); + when(databaseService.find(DATABASE_1_ID)) + .thenReturn(DATABASE_1); + when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(QueryDto.class))) + .thenReturn(ResponseEntity.ok(QUERY_1_DTO)); + when(userService.findByUsername(USER_1_USERNAME)) + .thenReturn(USER_1); when(identifierRepository.save(any(Identifier.class))) - .thenReturn(IDENTIFIER_3); + .thenReturn(IDENTIFIER_1); + when(identifierIdxRepository.save(any(IdentifierDto.class))) + .thenReturn(IDENTIFIER_1_DTO); /* test */ - assertThrows(IdentifierPublishingNotAllowedException.class, () -> { - identifierService.create(IDENTIFIER_3_DTO_SELF_REQUEST, principal, bearer); - }); + identifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer); } @Test @@ -288,7 +283,7 @@ public class IdentifierServiceUnitTest extends BaseUnitTest { } @Test - public void delete_succeeds() throws IdentifierNotFoundException { + public void delete_succeeds() throws IdentifierNotFoundException, NotAllowedException { /* mock */ when(identifierRepository.findById(IDENTIFIER_1_ID)) @@ -320,6 +315,22 @@ 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 { @@ -358,30 +369,4 @@ public class IdentifierServiceUnitTest extends BaseUnitTest { }); } - @Test - public void publish_alreadyEveryone_fails() { - - /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); - - /* test */ - assertThrows(IdentifierAlreadyPublishedException.class, () -> { - identifierService.publish(IDENTIFIER_1_ID, VisibilityTypeDto.SELF); - }); - } - - @Test - public void publish_alreadyEveryone2_fails() { - - /* mock */ - when(identifierRepository.findById(IDENTIFIER_1_ID)) - .thenReturn(Optional.of(IDENTIFIER_1)); - - /* test */ - assertThrows(IdentifierAlreadyPublishedException.class, () -> { - identifierService.publish(IDENTIFIER_1_ID, VisibilityTypeDto.TRUSTED); - }); - } - } diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_apa4.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_apa4.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce6a65da6b9e838043cf89acd5febd86ae3b6d08 --- /dev/null +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_apa4.txt @@ -0,0 +1 @@ +M., Mustermann. (2022). Austrian weather data. Austrian Government. https://doi.org/10.1000/183 \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt index b59c85b51e49426f8a22404fc0fd9456a301669c..0f797e3861818f4a17cc6487e97f6ce08d2efa48 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt @@ -1,7 +1,7 @@ @misc{dbrepo2022, author = {}, title = {Sweden weather data}, - howpublished = {\url{http://localhost:3000/pid/4}}, + url = {\url{http://localhost:3000/pid/4}}, month = {7}, year = {2022} } \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt index 4feb1be0d63cb77464a6f9ea0ee7a710b8547913..bab1cdadb13192dbf2c16f9dbfebb9fcc8385a36 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt @@ -1,7 +1,7 @@ @misc{dbrepo2022, author = {Mustermann, Max}, title = {Austrian weather data}, - howpublished = {\url{http://localhost:3000/pid/1}}, + url = {\url{http://localhost:3000/pid/1}}, month = {5}, year = {2022} } \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt index 0d7b5e3d7aa746d08084c85600f06e9fb4178ed0..9df96bed21348ede73192f16686e79aecc9c02dc 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt @@ -1,7 +1,7 @@ @misc{dbrepo2022, author = {Mustermann, Max and Mustermann, Martina}, title = {Australian weather data}, - howpublished = {\url{http://localhost:3000/pid/2}}, + url = {\url{http://localhost:3000/pid/2}}, month = {7}, year = {2022} } \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex3.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex3.txt new file mode 100644 index 0000000000000000000000000000000000000000..44190f2752de38baf1aa1856981729e433a1a4af --- /dev/null +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex3.txt @@ -0,0 +1,7 @@ +@misc{dbrepo2022, + author = {Mustermann, Max}, + title = {Austrian weather data}, + doi = {10.1000/183}, + month = {5}, + year = {2022} +} \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt index 1d79dc4dfbaeddd7ead4a97cf1a2dd687567fec9..35a5374403f5de61697a0eaab9910352b4aed9a2 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt @@ -1 +1 @@ -[1] “Sweden weather data“, Swedish Government, 2022, doi: http://localhost:3000/pid/4. \ No newline at end of file +[1] “Sweden weather data“, Swedish Government, 2022, Available: http://localhost:3000/pid/4. \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt index c1376a5ba53376bdba9154900d367a2908a98ea9..ac28d6fdeea46ae4c57e39c4dd6166b26e49703e 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt @@ -1 +1 @@ -[1] M. Mustermann, “Austrian weather data“, Austrian Government, 2022, doi: http://localhost:3000/pid/1. \ No newline at end of file +[1] M. Mustermann, “Austrian weather data“, Austrian Government, 2022, Available: http://localhost:3000/pid/1. \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt index 5ae90052d725298c58895ff917990a652665bc07..909031b2db36b3f9696af84ae423e33f1ffde199 100644 --- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt @@ -1 +1 @@ -[1] M. Mustermann and M. Mustermann, “Australian weather data“, Australian Government, 2022, doi: http://localhost:3000/pid/2. \ No newline at end of file +[1] M. Mustermann and M. Mustermann, “Australian weather data“, Australian Government, 2022, Available: http://localhost:3000/pid/2. \ No newline at end of file diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee3.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee3.txt new file mode 100644 index 0000000000000000000000000000000000000000..2576174cf69acc6f7f545b8b6ccfd415d1efd083 --- /dev/null +++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee3.txt @@ -0,0 +1 @@ +[1] M. Mustermann, “Austrian weather data“, Austrian Government, 2022, 10.1000/183. \ No newline at end of file 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 c9546518e28cd55888740e8248bd5f433168d8c1..ab803627d4a39d33b65d777a480709d4d18cff9d 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 @@ -5,7 +5,7 @@ "type": "database", "title": "Sweden weather data", "description": "Selecting all from the weather Sweden table", - "doi": "10.1000/184", + "doi": null, "visibility": "everyone", "publisher": "Swedish Government", "publication_day": 14, 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 e0eb80bffa0082995c1cf33afb82d3233ad226d4..be2009147bae92bae99101194a405f5ad6d337e0 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 @@ -8,7 +8,7 @@ "description": "Selecting all from the weather Austrian table", "query": "SELECT `id` FROM `foobar`", "query_normalized": "SELECT `id` FROM `foobar`", - "doi": "10.1000/182", + "doi": null, "query_hash": "abc", "execution": "2018-11-07T10:59:12.000+00:00", "result_hash": "def", diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/DataCiteConfig.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/DataCiteConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..ec84c3f4ff382ac41e0f7134217f527ebad87c81 --- /dev/null +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/DataCiteConfig.java @@ -0,0 +1,24 @@ +package at.tuwien.config; + +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Getter +@Profile("doi") +@Configuration +public class DataCiteConfig { + + @Value("${fda.datacite.url}") + private String url; + + @Value("${fda.datacite.prefix}") + private String prefix; + + @Value("${fda.datacite.username}") + private String username; + + @Value("${fda.datacite.password}") + private String password; +} diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/DataCiteMapper.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/DataCiteMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..5f68ba498c630c9e25998c9af093347fd9a05321 --- /dev/null +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/DataCiteMapper.java @@ -0,0 +1,98 @@ +package at.tuwien.mapper; + +import at.tuwien.datacite.doi.*; +import at.tuwien.entities.database.License; +import at.tuwien.entities.identifier.Creator; +import at.tuwien.entities.identifier.Identifier; +import at.tuwien.entities.identifier.RelatedIdentifier; +import at.tuwien.utils.EnumToStringConverter; +import org.mapstruct.*; +import org.springframework.context.annotation.Profile; + +import java.util.List; + +@Profile("doi") +@Mapper(componentModel = "spring", uses = EnumToStringConverter.class) +public interface DataCiteMapper { + + default <T> List<T> list(T t) { + if (t == null) return null; + return List.of(t); + } + + @Mappings({ + @Mapping(target = "titles", source = "."), + @Mapping(target = "publisher", source = "publisher"), + @Mapping(target = "publicationYear", source = "publicationYear"), + @Mapping(target = "publicationMonth", source = "publicationMonth"), + @Mapping(target = "publicationDay", source = "publicationDay"), + @Mapping(target = "language", source = "language"), + @Mapping(target = "rightsList", + expression = "java(list(licenseToDoiRights(identifier.getLicense())))"), + @Mapping(target = "creators", source = "creators"), + @Mapping(target = "relatedIdentifiers", source = "related"), + }) + DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier); + + default DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier, String url, String prefix) { + return addParametersToCreateDoi( + identifierToDataCiteCreateDoi(identifier), + url, + prefix, + DataCiteDoiTypes.DATASET, + DataCiteDoiEvent.PUBLISH + ); + } + + DataCiteCreateDoi addParametersToCreateDoi(@MappingTarget DataCiteCreateDoi target, + String url, + String prefix, + DataCiteDoiTypes types, + DataCiteDoiEvent event); + + default List<DataCiteDoiTitle> identifierToDoiTitles(Identifier identifier) { + return List.of( + DataCiteDoiTitle.builder().title(identifier.getTitle()).build(), + DataCiteDoiTitle.builder() + .title(identifier.getDescription()) + .titleType(DataCiteDoiTitle.Type.SUBTITLE) + .build() + ); + } + + @Mappings({ + @Mapping(target = "rights", source = "identifier"), + @Mapping(target = "rightsUri", source = "uri"), + }) + DataCiteDoiRights licenseToDoiRights(License license); + + @Mappings({ + @Mapping(target = "name", expression = "java(license.getLastname() + \", \" + license.getFirstname())"), + @Mapping(target = "givenName", source = "firstname"), + @Mapping(target = "familyName", source = "lastname"), + @Mapping(target = "affiliation", + expression = "java(list(affiliationStringToDoiCreatorAffiliation(license.getAffiliation())))"), + @Mapping(target = "nameIdentifiers", + expression = "java(list(orcidStringToDoiCreatorNameIdentifier(license.getOrcid())))"), + }) + DataCiteDoiCreator creatorToDoiCreator(Creator license); + + @Mappings({ + @Mapping(target = "name", constant = "affiliation"), + }) + DataCiteDoiCreatorAffiliation affiliationStringToDoiCreatorAffiliation(String affiliation); + + @Mappings({ + @Mapping(target = "schemeUri", constant = "https://orcid.org"), + @Mapping(target = "nameIdentifier", expression = "java(\"https://orcid.org/\" + orcid)"), + @Mapping(target = "nameIdentifierScheme", constant = "ORCID"), + }) + DataCiteDoiCreatorNameIdentifier orcidStringToDoiCreatorNameIdentifier(String orcid); + + @Mappings({ + @Mapping(target = "relatedIdentifier", source = "value"), + @Mapping(target = "relatedIdentifierType", source = "type"), + @Mapping(target = "relationType", source = "relation"), + }) + DataCiteDoiRelatedIdentifier relatedIdentifierToDoiRelatedIdentifier(RelatedIdentifier relatedIdentifier); +} 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 4c6dd57ac46ee6af851a7ed561db89c3c1b320ad..08fa62a8ec066cf57a1304a13e70e12ae27b9f68 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 @@ -1,8 +1,10 @@ package at.tuwien.mapper; import at.tuwien.api.identifier.*; -import at.tuwien.entities.database.Database; -import at.tuwien.entities.identifier.*; +import at.tuwien.entities.identifier.Creator; +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; @@ -31,9 +33,6 @@ public interface IdentifierMapper { @Transactional RelatedIdentifier relatedIdentifierCreateDtoToRelatedIdentifier(RelatedIdentifierCreateDto data); - @Transactional - VisibilityType visibilityTypeDtoToVisibilityType(VisibilityTypeDto data); - IdentifierType identifierTypeDtoToIdentifierType(IdentifierTypeDto data); default String identifierToLocationUrl(String baseUrl, Identifier data) { @@ -46,11 +45,4 @@ public interface IdentifierMapper { } } - default VisibilityType databaseToVisibilityType(Database data) { - if (data.getIsPublic()) { - return VisibilityType.EVERYONE; - } - return VisibilityType.SELF; - } - } 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 f052f49c15bdf9ebe2e7557f57eee59dd810f409..95071320600402cf97b75013593a429c237489a7 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 @@ -1,12 +1,12 @@ package at.tuwien.service; -import at.tuwien.api.identifier.*; -import at.tuwien.ExportResource; +import at.tuwien.api.identifier.BibliographyTypeDto; +import at.tuwien.api.identifier.IdentifierCreateDto; +import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.entities.identifier.Identifier; import at.tuwien.exception.*; import org.springframework.core.io.InputStreamResource; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.security.Principal; import java.util.List; @@ -58,7 +58,7 @@ public interface IdentifierService { Identifier create(IdentifierCreateDto data, Principal principal, String authorization) throws IdentifierPublishingNotAllowedException, QueryNotFoundException, RemoteUnavailableException, IdentifierAlreadyExistsException, UserNotFoundException, - DatabaseNotFoundException; + DatabaseNotFoundException, IdentifierRequestException; /** * Finds an identifier by given id in the metadata database. @@ -109,28 +109,15 @@ public interface IdentifierService { * @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 IdentifierPublishingNotAllowedException The identifier contained a visibility change which is not allowed here. */ - Identifier update(Long identifierId, IdentifierDto data) throws IdentifierNotFoundException, - IdentifierPublishingNotAllowedException; - - /** - * Publishes the identifier for a given identifier id in the metadata database. - * - * @param identifierId The identifier id. - * @param visibility The new visibility. - * @return The updated identifier from the metadata database. - * @throws IdentifierNotFoundException The identifier was not found in the metadata database or was deleted. - * @throws IdentifierAlreadyPublishedException The identifier is already published (=EVERYONE) and cannot be un-published. - */ - Identifier publish(Long identifierId, VisibilityTypeDto visibility) throws IdentifierNotFoundException, - IdentifierAlreadyPublishedException; + Identifier update(Long identifierId, IdentifierDto 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; + void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException; } 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 new file mode 100644 index 0000000000000000000000000000000000000000..37a02ceb6f4f592a063c943b8637f41fffc7b37e --- /dev/null +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java @@ -0,0 +1,212 @@ +package at.tuwien.service.impl; + +import at.tuwien.api.identifier.BibliographyTypeDto; +import at.tuwien.api.identifier.IdentifierCreateDto; +import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.config.DataCiteConfig; +import at.tuwien.config.EndpointConfig; +import at.tuwien.datacite.DataCiteBody; +import at.tuwien.datacite.DataCiteData; +import at.tuwien.datacite.doi.DataCiteCreateDoi; +import at.tuwien.datacite.doi.DataCiteDoi; +import at.tuwien.entities.identifier.Identifier; +import at.tuwien.exception.*; +import at.tuwien.gateway.QueryServiceGateway; +import at.tuwien.mapper.DataCiteMapper; +import at.tuwien.mapper.IdentifierMapper; +import at.tuwien.repository.elastic.IdentifierIdxRepository; +import at.tuwien.repository.jpa.IdentifierRepository; +import at.tuwien.repository.jpa.RelatedIdentifierRepository; +import at.tuwien.service.DatabaseService; +import at.tuwien.service.IdentifierService; +import at.tuwien.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.thymeleaf.TemplateEngine; + +import javax.transaction.Transactional; +import java.security.Principal; +import java.util.List; +import java.util.Optional; + +@Slf4j +@Primary +@Profile("doi") +@Service +public class DataCiteIdentifierServiceImpl implements IdentifierService { + + private final DataCiteConfig dataCiteConfig; + private final EndpointConfig endpointConfig; + private final DataCiteMapper dataCiteMapper; + private final RestTemplateBuilder restTemplateBuilder; + private final IdentifierRepository identifierRepository; + private final IdentifierService identifierService; + + public DataCiteIdentifierServiceImpl(DataCiteConfig dataCiteConfig, DataCiteMapper dataCiteMapper, + RestTemplateBuilder restTemplateBuilder, EndpointConfig endpointConfig, + IdentifierRepository identifierRepository, IdentifierServiceImpl identifierService) { + this.dataCiteConfig = dataCiteConfig; + this.dataCiteMapper = dataCiteMapper; + this.restTemplateBuilder = + restTemplateBuilder.basicAuthentication(dataCiteConfig.getUsername(), dataCiteConfig.getPassword()) + .uriTemplateHandler(new DefaultUriBuilderFactory(dataCiteConfig.getUrl())); + this.endpointConfig = endpointConfig; + this.identifierRepository = identifierRepository; + this.identifierService = identifierService; + } + + @Override + public List<Identifier> findAll(Long databaseId, Long queryId) throws IdentifierNotFoundException { + return identifierService.findAll(databaseId, queryId); + } + + @Override + public Identifier find(Long databaseId, Long queryId) throws IdentifierNotFoundException { + return identifierService.find(databaseId, queryId); + } + + @Override + public List<Identifier> findAll() { + return identifierService.findAll(); + } + + @Override + @Transactional(rollbackOn = {Exception.class}) + public Identifier create(IdentifierCreateDto data, Principal principal, String authorization) + throws IdentifierPublishingNotAllowedException, QueryNotFoundException, RemoteUnavailableException, + IdentifierAlreadyExistsException, UserNotFoundException, DatabaseNotFoundException, + IdentifierRequestException { + Identifier identifier = identifierService.create(data, principal, authorization); + RestTemplate restTemplate = restTemplateBuilder.build(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBasicAuth(dataCiteConfig.getUsername(), dataCiteConfig.getPassword()); + HttpEntity<DataCiteBody<DataCiteCreateDoi>> request = new HttpEntity<>( + DataCiteBody.<DataCiteCreateDoi>builder() + .data(DataCiteData.<DataCiteCreateDoi>builder() + .type("dois") + .attributes(dataCiteMapper.identifierToDataCiteCreateDoi(identifier, + endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId(), + dataCiteConfig.getPrefix())) + .build()) + .build(), + headers + ); + + try { + ResponseEntity<DataCiteBody<DataCiteDoi>> response = restTemplate.exchange("dois", HttpMethod.POST, + request, + new ParameterizedTypeReference<>() { + } + ); + + 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) { + log.error("Invalid DOI metadata.", e); + throw new IdentifierRequestException("Invalid DOI metadata.", 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); + } + + return identifier; + } + + @Override + public Identifier find(Long identifierId) throws IdentifierNotFoundException { + return identifierService.find(identifierId); + } + + @Override + public InputStreamResource exportMetadata(Long id) throws IdentifierNotFoundException { + return identifierService.exportMetadata(id); + } + + @Override + public String exportBibliography(Long id, BibliographyTypeDto style) + throws IdentifierNotFoundException, IdentifierRequestException { + return identifierService.exportBibliography(id, style); + } + + @Override + public InputStreamResource exportResource(Long identifierId) + throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, + IdentifierRequestException { + return identifierService.exportResource(identifierId); + } + + @Override + @Transactional(rollbackOn = {Exception.class}) + public Identifier update(Long identifierId, IdentifierDto data) + throws IdentifierNotFoundException, IdentifierRequestException { + Identifier identifier = identifierService.update(identifierId, data); + if(identifier.getDoi() == null) { + return identifier; + } + + RestTemplate restTemplate = restTemplateBuilder.build(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBasicAuth(dataCiteConfig.getUsername(), dataCiteConfig.getPassword()); + HttpEntity<DataCiteBody<DataCiteCreateDoi>> request = new HttpEntity<>( + DataCiteBody.<DataCiteCreateDoi>builder() + .data(DataCiteData.<DataCiteCreateDoi>builder() + .type("dois") + .attributes(dataCiteMapper.identifierToDataCiteCreateDoi(identifier, + endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId(), + dataCiteConfig.getPrefix())) + .build()) + .build(), + headers + ); + + try { + ResponseEntity<DataCiteBody<DataCiteDoi>> response = restTemplate.exchange("dois/{doi}", HttpMethod.PUT, + request, + new ParameterizedTypeReference<>() { + }, + identifier.getDoi() + ); + + 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) { + log.error("Invalid DOI metadata.", e); + throw new IdentifierRequestException("Invalid DOI metadata.", 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); + } + + return identifier; + } + + @Override + public void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException { + 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 3c0a8f0cecb4b1400ad1cb6274353514e889e876..f33ff8911b976e33338e6af8f6f8428c00fb1084 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 @@ -4,7 +4,10 @@ import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.identifier.*; import at.tuwien.config.EndpointConfig; import at.tuwien.entities.database.Database; -import at.tuwien.entities.identifier.*; +import at.tuwien.entities.identifier.Creator; +import at.tuwien.entities.identifier.Identifier; +import at.tuwien.entities.identifier.IdentifierType; +import at.tuwien.entities.identifier.RelatedIdentifier; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.QueryServiceGateway; @@ -17,6 +20,7 @@ import at.tuwien.service.IdentifierService; import at.tuwien.service.UserService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; +import org.springframework.context.annotation.Profile; import org.springframework.core.io.InputStreamResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -96,13 +100,10 @@ public class IdentifierServiceImpl implements IdentifierService { @Transactional public Identifier create(IdentifierCreateDto data, Principal principal, String authorization) throws QueryNotFoundException, RemoteUnavailableException, IdentifierAlreadyExistsException, - UserNotFoundException, DatabaseNotFoundException, IdentifierPublishingNotAllowedException { + UserNotFoundException, DatabaseNotFoundException, IdentifierPublishingNotAllowedException, + IdentifierRequestException { /* check */ final Database database = databaseService.find(data.getDbid()); - if (database.getIsPublic() && !data.getVisibility().equals(VisibilityTypeDto.EVERYONE)) { - log.error("Identifier cannot restrict the result set"); - throw new IdentifierPublishingNotAllowedException("Identifier cannot restrict the result set"); - } if (data.getType().equals(IdentifierTypeDto.DATABASE) && identifierRepository.existsByDatabaseIdAndType(data.getDbid(), IdentifierType.DATABASE)) { log.error("Identifier already issued for database with id {}", data.getDbid()); throw new IdentifierAlreadyExistsException("Database identifier already exists"); @@ -120,7 +121,6 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getType().equals(IdentifierTypeDto.SUBSET)) { log.debug("identifier describes a subset"); final QueryDto query = queryServiceGateway.find(data.getCid(), data.getDbid(), data, authorization); - tmp.setVisibility(identifierMapper.visibilityTypeDtoToVisibilityType(data.getVisibility())); tmp.setQuery(query.getQuery()); tmp.setQueryId(query.getId()); tmp.setQueryNormalized(query.getQueryNormalized()); @@ -128,9 +128,6 @@ public class IdentifierServiceImpl implements IdentifierService { tmp.setExecution(query.getExecution()); tmp.setResultNumber(query.getResultNumber()); tmp.setResultHash(query.getResultHash()); - } else if (data.getType().equals(IdentifierTypeDto.DATABASE)) { - log.debug("identifier describes a database"); - tmp.setVisibility(identifierMapper.databaseToVisibilityType(database)); } /* create in metadata database */ final Identifier entity = identifierRepository.save(tmp); @@ -181,7 +178,13 @@ public class IdentifierServiceImpl implements IdentifierService { final Identifier identifier = find(id); /* context */ final Context context = new Context(); - context.setVariable("doi", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId()); + if(identifier.getDoi() != null) { + context.setVariable("identifierType", "DOI"); + context.setVariable("identifier", identifier.getDoi()); + } else { + context.setVariable("identifierType", "PID"); + context.setVariable("identifier", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId()); + } context.setVariable("creators", identifier.getCreators()); context.setVariable("title", identifier.getTitle()); context.setVariable("publisher", identifier.getPublisher()); @@ -205,7 +208,13 @@ public class IdentifierServiceImpl implements IdentifierService { final Identifier identifier = find(id); /* context */ final Context context = new Context(); - context.setVariable("doi", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId()); + if(identifier.getDoi() != null) { + context.setVariable("identifierType", "doi"); + context.setVariable("identifier", identifier.getDoi()); + } else { + context.setVariable("identifierType", "url"); + context.setVariable("identifier", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId()); + } context.setVariable("creator", identifier.getCreator()); context.setVariable("creators", identifier.getCreators()); context.setVariable("title", identifier.getTitle()); @@ -245,9 +254,15 @@ public class IdentifierServiceImpl implements IdentifierService { @Override @Transactional public Identifier update(Long identifierId, IdentifierDto data) - throws IdentifierNotFoundException { + throws IdentifierNotFoundException, IdentifierRequestException { /* check */ - find(identifierId); + 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."); + } /* map */ final Identifier entity = identifierMapper.identifierDtoToIdentifier(data); entity.getCreators() @@ -264,29 +279,12 @@ public class IdentifierServiceImpl implements IdentifierService { @Override @Transactional - public Identifier publish(Long identifierId, VisibilityTypeDto visibility) - throws IdentifierNotFoundException, IdentifierAlreadyPublishedException { - final Identifier identifier = find(identifierId); - if (identifier.getVisibility().equals(VisibilityType.EVERYONE)) { - /* once published, the identifier cannot be reverted, it is persistent! */ - log.error("Identifier with id {} is already published for everyone", identifier.getId()); - throw new IdentifierAlreadyPublishedException("Identifier with id " + identifier.getId() + " is already published for everyone"); - } - identifier.setVisibility(identifierMapper.visibilityTypeDtoToVisibilityType(visibility)); - final Identifier entity = identifierRepository.save(identifier); - log.info("Published identifier with id {}", identifierId); - log.trace("published identifier {}", entity); - /* elastic search */ - identifierIdxRepository.save(identifierMapper.identifierToIdentifierDto(entity)); - log.info("Published identifier with id {} in elastic search", identifierId); - return entity; - } - - @Override - @Transactional - public void delete(Long identifierId) throws IdentifierNotFoundException { + 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."); + } /* delete */ identifierRepository.delete(identifier); log.info("Deleted identifier with id {}", identifierId); diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/utils/EnumToStringConverter.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/utils/EnumToStringConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..2e53ae6cdc17dcf9b915bc9aef3793701cba98e8 --- /dev/null +++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/utils/EnumToStringConverter.java @@ -0,0 +1,15 @@ +package at.tuwien.utils; + +import org.mapstruct.TargetType; + +public class EnumToStringConverter { + + public static String convert(Enum<?> source) { + return source == null ? null : source.toString(); + } + + public static <E extends Enum<E>> E convert(String source, @TargetType Class<E> enumType) { + // You probably need something else here as the methods are not symmetrical + return source == null ? null : Enum.valueOf( enumType, source ); + } +} diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java index eeb7c449a8098080f8abebd9da7dc76507c1ff1a..67a9e858e95b8dc72b9074870de4e697346e72f8 100644 --- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java +++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java @@ -11,6 +11,7 @@ import lombok.Setter; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotEmpty; import java.util.List; @Data @@ -37,13 +38,6 @@ public class IdentifierCreateDto { @Schema(example = "Air quality reports at Stephansplatz, Vienna") private String description; - @NotNull - @Schema(example = "everyone") - private VisibilityTypeDto visibility; - - @Schema(example = "10.1038/nphys1170") - private String doi; - @JsonProperty("publication_day") @Schema(example = "15") private Integer publicationDay; @@ -65,6 +59,7 @@ public class IdentifierCreateDto { private Integer publicationYear; @NotNull + @NotEmpty private List<CreatorCreateDto> creators; @JsonProperty("related_identifiers") 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 95ffd29e5907c9585f1f64402e396dc5d24065d1..5cdd7a5aeda30d8eda045d4f9551eb259cd37edd 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 @@ -109,7 +109,8 @@ public class Identifier implements Serializable { @Column(nullable = false, columnDefinition = "enum('EVERYONE', 'TRUSTED', 'SELF')") @Enumerated(EnumType.STRING) - private VisibilityType visibility = VisibilityType.SELF; + @Builder.Default + private VisibilityType visibility = VisibilityType.EVERYONE; @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) @JoinColumns({ 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 f856227bf321a72134343b1a10d10c4db2b38261..4397e90f4ff82391485d0f18bd0a9d33ca35dda5 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 @@ -1875,7 +1875,7 @@ public abstract class BaseTest { public final static Long QUERY_1_ID = 1L; public final static String QUERY_1_STATEMENT = "SELECT `id`, `date`, `location`, `mintemp`, `rainfall` FROM " + "`weather_aus`"; - public final static String QUERY_1_DOI = "1111/1"; + public final static String QUERY_1_DOI = null; public final static Long QUERY_1_CONTAINER_ID = CONTAINER_1_ID; public final static Long QUERY_1_DATABASE_ID = DATABASE_1_ID; public final static Long QUERY_1_RESULT_NUMBER = 2L; @@ -3634,7 +3634,8 @@ public abstract class BaseTest { public final static String IDENTIFIER_1_DESCRIPTION_MODIFY = "Selecting some from the weather Austrian table"; public final static String IDENTIFIER_1_TITLE = "Austrian weather data"; public final static String IDENTIFIER_1_TITLE_MODIFY = "Austrian weather some data"; - public final static String IDENTIFIER_1_DOI = "10.1000/182"; + 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 */; @@ -3693,6 +3694,30 @@ public abstract class BaseTest { .creators(List.of(IDENTIFIER_1_CREATOR_1)) .build(); + public final static Identifier IDENTIFIER_1_WITH_DOI = Identifier.builder() + .id(IDENTIFIER_1_ID) + .containerId(IDENTIFIER_1_CONTAINER_ID) + .databaseId(IDENTIFIER_1_DATABASE_ID) + .queryId(IDENTIFIER_1_QUERY_ID) + .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) + .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) + .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) + .queryHash(IDENTIFIER_1_QUERY_HASH) + .resultHash(IDENTIFIER_1_RESULT_HASH) + .query(IDENTIFIER_1_QUERY) + .queryNormalized(IDENTIFIER_1_NORMALIZED) + .resultNumber(IDENTIFIER_1_RESULT_NUMBER) + .publisher(IDENTIFIER_1_PUBLISHER) + .type(IDENTIFIER_1_TYPE) + .creators(List.of(IDENTIFIER_1_CREATOR_1)) + .build(); + public final static IdentifierDto IDENTIFIER_1_DTO = IdentifierDto.builder() .id(IDENTIFIER_1_ID) .containerId(IDENTIFIER_1_CONTAINER_ID) @@ -3718,13 +3743,38 @@ public abstract class BaseTest { .creators(List.of(IDENTIFIER_1_CREATOR_1_DTO)) .build(); + public final static IdentifierDto IDENTIFIER_1_WITH_DOI_DTO = IdentifierDto.builder() + .id(IDENTIFIER_1_ID) + .containerId(IDENTIFIER_1_CONTAINER_ID) + .databaseId(IDENTIFIER_1_DATABASE_ID) + .queryId(IDENTIFIER_1_QUERY_ID) + .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) + .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) + .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) + .queryHash(IDENTIFIER_1_QUERY_HASH) + .resultHash(IDENTIFIER_1_RESULT_HASH) + .query(IDENTIFIER_1_QUERY) + .queryNormalized(IDENTIFIER_1_NORMALIZED) + .resultNumber(IDENTIFIER_1_RESULT_NUMBER) + .publisher(IDENTIFIER_1_PUBLISHER) + .type(IDENTIFIER_1_TYPE_DTO) + .creator(USER_1_DTO) + .creators(List.of(IDENTIFIER_1_CREATOR_1_DTO)) + .build(); + public final static Long IDENTIFIER_2_ID = 2L; public final static Long IDENTIFIER_2_QUERY_ID = QUERY_2_ID; public final static Long IDENTIFIER_2_CONTAINER_ID = CONTAINER_2_ID; public final static Long IDENTIFIER_2_DATABASE_ID = DATABASE_2_ID; 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 = "10.1000/183"; + 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); @@ -3917,8 +3967,6 @@ public abstract class BaseTest { .dbid(IDENTIFIER_1_DATABASE_ID) .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) - .doi(IDENTIFIER_1_DOI) - .visibility(IDENTIFIER_1_VISIBILITY_DTO) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) @@ -3932,8 +3980,6 @@ public abstract class BaseTest { .dbid(IDENTIFIER_1_DATABASE_ID) .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) - .doi(IDENTIFIER_1_DOI) - .visibility(VisibilityTypeDto.TRUSTED) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) @@ -3947,8 +3993,6 @@ public abstract class BaseTest { .dbid(IDENTIFIER_1_DATABASE_ID) .description(IDENTIFIER_1_DESCRIPTION) .title(IDENTIFIER_1_TITLE) - .doi(IDENTIFIER_1_DOI) - .visibility(VisibilityTypeDto.SELF) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) @@ -3985,8 +4029,6 @@ public abstract class BaseTest { .dbid(IDENTIFIER_2_DATABASE_ID) .description(IDENTIFIER_2_DESCRIPTION) .title(IDENTIFIER_2_TITLE) - .doi(IDENTIFIER_2_DOI) - .visibility(IDENTIFIER_2_VISIBILITY_DTO) .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_2_CREATE_DTO)) .publicationDay(IDENTIFIER_2_PUBLICATION_DAY) .publicationMonth(IDENTIFIER_2_PUBLICATION_MONTH) @@ -4002,7 +4044,7 @@ public abstract class BaseTest { public final static Long IDENTIFIER_3_DATABASE_ID = DATABASE_3_ID; 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 = "10.1000/183"; + 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); @@ -4128,8 +4170,6 @@ public abstract class BaseTest { .dbid(IDENTIFIER_3_DATABASE_ID) .description(IDENTIFIER_3_DESCRIPTION) .title(IDENTIFIER_3_TITLE) - .doi(IDENTIFIER_3_DOI) - .visibility(IDENTIFIER_3_VISIBILITY_DTO) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR) @@ -4143,8 +4183,6 @@ public abstract class BaseTest { .dbid(IDENTIFIER_3_DATABASE_ID) .description(IDENTIFIER_3_DESCRIPTION) .title(IDENTIFIER_3_TITLE) - .doi(IDENTIFIER_3_DOI) - .visibility(VisibilityTypeDto.TRUSTED) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR) @@ -4158,8 +4196,6 @@ public abstract class BaseTest { .dbid(IDENTIFIER_3_DATABASE_ID) .description(IDENTIFIER_3_DESCRIPTION) .title(IDENTIFIER_3_TITLE) - .doi(IDENTIFIER_3_DOI) - .visibility(VisibilityTypeDto.SELF) .relatedIdentifiers(List.of()) .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH) .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR) @@ -4173,7 +4209,7 @@ public abstract class BaseTest { public final static Long IDENTIFIER_4_DATABASE_ID = DATABASE_4_ID; 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 = "10.1000/184"; + 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); diff --git a/dbrepo-ui/.env.example b/dbrepo-ui/.env.example index 6d67c5eca979378377bc21dd27644ba038a41d86..28ec04e8937e8362c56a46c890de7a142ddc285c 100644 --- a/dbrepo-ui/.env.example +++ b/dbrepo-ui/.env.example @@ -6,3 +6,4 @@ BROKER_USERNAME=fda BROKER_PASSWORD=fda SANDBOX=false SHARED_FILESYSTEM=/tmp +DOI_URL="https://doi.org" diff --git a/dbrepo-ui/components/dialogs/Persist.vue b/dbrepo-ui/components/dialogs/Persist.vue index a8595e18fd1151270c670c256f2509997c799b63..b8ed2529476f21729aa44d8cc25f28a35965bad4 100644 --- a/dbrepo-ui/components/dialogs/Persist.vue +++ b/dbrepo-ui/components/dialogs/Persist.vue @@ -64,19 +64,6 @@ required /> </v-col> </v-row> - <v-row dense> - <v-col> - <v-select - id="visibility" - v-model="visibility" - name="visibility" - label="Visibility *" - :items="['Public']" - disabled - :rules="[v => !!v || $t('Required')]" - required /> - </v-col> - </v-row> <v-row v-for="(creator,i) in identifier.creators" :key="`c-${i}`" dense> <v-col cols="3"> <v-text-field @@ -109,7 +96,7 @@ label="ORCID" /> </v-col> <v-col cols="1" class="mt-5"> - <v-btn icon x-small @click="deleteCreator(i)"> + <v-btn v-if="!i" icon x-small @click="deleteCreator(i)"> <v-icon>mdi-close</v-icon> </v-btn> </v-col> @@ -225,10 +212,15 @@ export default { publication_month: formatMonthUTC(Date.now()), publication_day: formatDayUTC(Date.now()), license: null, - visibility: 'everyone', type: this.type, - doi: null, - creators: [], + creators: [ + { + firstname: null, + lastname: null, + affiliation: null, + orcid: null + } + ], related_identifiers: [] }, relatedTypes: [ diff --git a/dbrepo-ui/components/identifier/Banner.vue b/dbrepo-ui/components/identifier/Banner.vue new file mode 100644 index 0000000000000000000000000000000000000000..8756962c9e9b4e3106078e603e1db4209b272fab --- /dev/null +++ b/dbrepo-ui/components/identifier/Banner.vue @@ -0,0 +1,33 @@ +<template> + <p v-if="identifier?.doi">DOI: <a :href="doiUrl(identifier.doi)">{{ identifier.doi }}</a></p> + <p v-else-if="identifier?.id"><a :href="pidUrl(identifier.id)">{{ pidUrl(identifier.id) }}</a></p> +</template> +yarn dev +<script> + +export default { + props: { + identifier: { + type: Object, + default () { + return null + } + } + }, + computed: { + baseUrl () { + return `${location.protocol}//${location.host}` + } + }, + methods: { + pidUrl (pid) { + return `${this.baseUrl}/pid/${pid}` + }, + doiUrl (doi) { + return `${this.$config.doiUrl}/${doi}` + } + } +} +</script> + +<style scoped></style> diff --git a/dbrepo-ui/nuxt.config.js b/dbrepo-ui/nuxt.config.js index 66d11e53c6c5a59abc1f2f729acb9895844603d6..e87dd45befc8457dccec4029180428f0547abdba 100644 --- a/dbrepo-ui/nuxt.config.js +++ b/dbrepo-ui/nuxt.config.js @@ -81,7 +81,8 @@ export default { logo: process.env.LOGO || '/logo.png', mailVerify: process.env.MAIL_VERIFY || false, tokenMax: process.env.TOKEN_MAX || 5, - elasticPassword: process.env.ELASTIC_PASSWORD || 'elastic' + elasticPassword: process.env.ELASTIC_PASSWORD || 'elastic', + doiUrl: process.env.DOI_URL || 'https://doi.org' }, proxy: { 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 b9224362e62047fb60aa8b81f72f49b67b949b53..99edcf8c78809f354db5227eca7a382e44ab6fa9 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 @@ -15,7 +15,7 @@ </v-list-item-title> <v-list-item-content v-if="publisher"> <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> - <a v-if="!loading" :href="pid">{{ pid }}</a> + <Banner v-if="!loading" :identifier="database.identifier" /> </v-list-item-content> <v-list-item-title v-if="publisher" class="mt-2"> Database Publisher @@ -222,6 +222,7 @@ import DBToolbar from '@/components/DBToolbar' import Persist from '@/components/dialogs/Persist' import OrcidIcon from '@/components/icons/OrcidIcon' import Citation from '@/components/identifier/Citation' +import Banner from '@/components/identifier/Banner' import { formatTimestampUTCLabel, formatUser, isDataSteward, isResearcher } from '@/utils' export default { @@ -229,7 +230,8 @@ export default { DBToolbar, Persist, OrcidIcon, - Citation + Citation, + Banner }, data () { return { @@ -300,9 +302,6 @@ export default { isDataSteward () { return isDataSteward(this.user) }, - pid () { - return `${this.baseUrl}/pid/${this.database.identifier.id}` - }, createdUTC () { return formatTimestampUTCLabel(this.database.created) }, diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue index 4ca589d1527db7c2bd3eea1a845348c3f3d2d9c9..71d72baab90497b3f667e041d59972aa6aecef22 100644 --- a/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue +++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue @@ -84,7 +84,7 @@ Persistent Identifier </v-list-item-title> <v-list-item-content> - <a :href="`${baseUrl}/pid/${query.identifier.id}`">{{ baseUrl }}/pid/{{ query.identifier.id }}</a> + <Banner :identifier="query.identifier" /> </v-list-item-content> <v-list-item-title class="mt-2"> Title @@ -220,13 +220,15 @@ <script> import Persist from '@/components/dialogs/Persist' import Citation from '@/components/identifier/Citation' +import Banner from '@/components/identifier/Banner' import { formatTimestampUTCLabel, formatDateUTC } from '@/utils' export default { name: 'QueryShow', components: { Persist, - Citation + Citation, + Banner }, data () { return { diff --git a/docker-compose.dbrepo1.yml b/docker-compose.dbrepo1.yml index 5d89457c84f64126c67ed4a4b1180e0274af13e1..b85af3cd4fdd74be65d4e20685fcba9d642ef986 100644 --- a/docker-compose.dbrepo1.yml +++ b/docker-compose.dbrepo1.yml @@ -187,6 +187,8 @@ services: core: env_file: - .env + environment: + - SPRING_PROFILES_ACTIVE=doi depends_on: dbrepo-query-service: condition: service_healthy diff --git a/docker-compose.dbrepo2.yml b/docker-compose.dbrepo2.yml index b403413a70034ba328ccc2fa60b061192e1aadf6..d02ee089f0211b09c11f5d446a60fc4690683489 100644 --- a/docker-compose.dbrepo2.yml +++ b/docker-compose.dbrepo2.yml @@ -187,6 +187,8 @@ services: core: env_file: - .env + environment: + - SPRING_PROFILES_ACTIVE=doi depends_on: dbrepo-query-service: condition: service_healthy