diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/AbstractAssembler.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/AbstractAssembler.java index 58d6ea649..16d6ef411 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/AbstractAssembler.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/AbstractAssembler.java @@ -233,7 +233,7 @@ protected List resolveCustodialResources(List } else if (Objects.nonNull(dfr.getDepositFile().getPassFileId())) { String passFileId = dfr.getDepositFile().getPassFileId(); LOG.trace("Returning PassFileResource for Pass File {}", passFileId); - delegateResource = new PassFileResource(passClient, passFileId); + delegateResource = new PassFileResource(passClient, passFileId, dfr.getDepositFile().getName()); } else if (location.startsWith(HTTP_PREFIX) || location.startsWith(HTTPS_PREFIX) || location.startsWith(JAR_PREFIX)) { try { diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/PassFileResource.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/PassFileResource.java index 15b81e3d8..170f2744e 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/PassFileResource.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/assembler/PassFileResource.java @@ -29,10 +29,12 @@ public class PassFileResource extends AbstractResource { private final PassClient passClient; private final String passFileId; + private final String filename; - public PassFileResource(PassClient passClient, String passFileId) { + public PassFileResource(PassClient passClient, String passFileId, String filename) { this.passClient = passClient; this.passFileId = passFileId; + this.filename = filename; } @Override @@ -54,4 +56,9 @@ public String getContentAsString(Charset charset) throws IOException { public String getDescription() { return "PassFileResource File ID: " + passFileId; } + + @Override + public String getFilename() { + return filename; + } } diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java index 549c7def6..5f7b84f1f 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java @@ -20,10 +20,10 @@ import java.net.URI; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -73,8 +73,8 @@ public class DepositSubmissionMapper { private static final String AUTHORS_KEY = "authors"; private static final String AUTHOR_KEY = "author"; private static final String PUB_TYPE_KEY = "pubType"; - private static final String EMBARGO_END_DATE_PATTERN = "yyyy-MM-dd"; private static final String NLMTA_KEY = "journal-NLMTA-ID"; + private static final String DEFAULT_DATE_TIME_ZONE = "America/New_York"; /** * Creates a DepositSubmission by walking the tree of PassEntity objects, starting with the Submission entity, @@ -267,7 +267,7 @@ private void processCommonMetadata(DepositMetadata metadata, JsonObject submissi .ifPresent(pName -> metadata.getJournalMetadata().setPublisherName(pName)); getStringProperty(submissionData, PUBLICATION_DATE_KEY) - .ifPresent(pName -> metadata.getJournalMetadata().setPublicationDate(pName)); + .ifPresent(date -> metadata.getJournalMetadata().setPublicationDate(parseDate(date))); getArrayProperty(submissionData, ISSNS).ifPresent(issns -> { issns.forEach(issnObjAsStr -> { @@ -290,19 +290,29 @@ private void processCommonMetadata(DepositMetadata metadata, JsonObject submissi getStringProperty(submissionData, EMBARGO_END_DATE_KEY).ifPresent(endDate -> { try { - // TODO - Resolve inconsistent date/date-time formats in metadata and deposit data model - // TODO - Fix assumption of local timezone - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(EMBARGO_END_DATE_PATTERN); - LocalDateTime localEndDate = LocalDate.parse(endDate, formatter).atStartOfDay(); - ZonedDateTime zonedEndDate = localEndDate.atZone(ZoneId.of("America/New_York")); - metadata.getArticleMetadata().setEmbargoLiftDate(zonedEndDate); - } catch (Exception e) { + metadata.getArticleMetadata().setEmbargoLiftDate(parseDate(endDate)); + } catch (DateTimeParseException e) { throw new DepositServiceRuntimeException( - String.format("Data file contained an invalid Date: '%s'.", endDate), e); + String.format("Data file contained an invalid Date: '%s'.", endDate), e); } }); } + // Parse an ISO 8601 date with or without a time zone + public ZonedDateTime parseDate(String date) { + try { + return ZonedDateTime.parse(date, DateTimeFormatter.ISO_DATE); + } catch (DateTimeParseException e) { + try { + return LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay() + .atZone(ZoneId.of(DEFAULT_DATE_TIME_ZONE)); + } catch (DateTimeParseException e2) { + throw new DepositServiceRuntimeException( + String.format("Metadata contained an invalid Date: '%s'.", date), e2); + } + } + } + private void processCrossrefMetadata(DepositMetadata metadata, JsonObject submissionData) { getStringProperty(submissionData, DOI_KEY).ifPresent(doi -> { try { diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/DSpaceBinding.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/DSpaceBinding.java new file mode 100644 index 000000000..3e046bf62 --- /dev/null +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/DSpaceBinding.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.config.repository; + +import static org.eclipse.pass.deposit.transport.Transport.TRANSPORT_PROTOCOL; + +import java.util.Map; + +import org.eclipse.pass.deposit.transport.Transport; + +/** + * @author Russ Poetker (rpoetke1@jh.edu) + */ +public class DSpaceBinding extends ProtocolBinding { + static final String PROTO = "DSpace"; + + public DSpaceBinding() { + this.setProtocol(PROTO); + } + + @Override + public Map asPropertiesMap() { + return Map.of(TRANSPORT_PROTOCOL, Transport.PROTOCOL.DSpace.name()); + } +} diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/ProtocolBinding.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/ProtocolBinding.java index 0a67cae5e..773da4d88 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/ProtocolBinding.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/ProtocolBinding.java @@ -30,7 +30,8 @@ @JsonSubTypes.Type(value = SftpBinding.class, name = SftpBinding.PROTO), @JsonSubTypes.Type(value = SwordV2Binding.class, name = SwordV2Binding.PROTO), @JsonSubTypes.Type(value = FilesystemBinding.class, name = FilesystemBinding.PROTO), - @JsonSubTypes.Type(value = InvenioRdmBinding.class, name = InvenioRdmBinding.PROTO) + @JsonSubTypes.Type(value = InvenioRdmBinding.class, name = InvenioRdmBinding.PROTO), + @JsonSubTypes.Type(value = DSpaceBinding.class, name = DSpaceBinding.PROTO) }) public abstract class ProtocolBinding { diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/dspace/DSpaceAssembler.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/dspace/DSpaceAssembler.java new file mode 100644 index 000000000..bf64ceee3 --- /dev/null +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/dspace/DSpaceAssembler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.provider.dspace; + +import static org.eclipse.pass.deposit.assembler.AssemblerSupport.buildMetadata; + +import java.util.List; +import java.util.Map; + +import org.eclipse.pass.deposit.assembler.AbstractAssembler; +import org.eclipse.pass.deposit.assembler.DepositFileResource; +import org.eclipse.pass.deposit.assembler.MetadataBuilder; +import org.eclipse.pass.deposit.assembler.MetadataBuilderFactory; +import org.eclipse.pass.deposit.assembler.PackageStream; +import org.eclipse.pass.deposit.assembler.ResourceBuilderFactory; +import org.eclipse.pass.deposit.assembler.SimplePackageStream; +import org.eclipse.pass.deposit.model.DepositSubmission; +import org.eclipse.pass.support.client.PassClient; +import org.springframework.stereotype.Component; + +@Component +public class DSpaceAssembler extends AbstractAssembler { + public DSpaceAssembler(MetadataBuilderFactory mbf, + ResourceBuilderFactory rbf, + PassClient passClient) { + super(mbf, rbf, passClient); + } + + @Override + protected PackageStream createPackageStream(DepositSubmission depositSubmission, + List custodialResources, + MetadataBuilder mb, ResourceBuilderFactory rbf, + Map options) { + buildMetadata(mb, options); + return new SimplePackageStream(depositSubmission, custodialResources, mb); + } +} diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/dspace/DSpaceMetadataMapper.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/dspace/DSpaceMetadataMapper.java new file mode 100644 index 000000000..68b6e3739 --- /dev/null +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/dspace/DSpaceMetadataMapper.java @@ -0,0 +1,148 @@ +/* + * Copyright 2025 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.provider.dspace; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import org.eclipse.pass.deposit.model.DepositMetadata; +import org.eclipse.pass.deposit.model.DepositMetadata.Article; +import org.eclipse.pass.deposit.model.DepositMetadata.Journal; +import org.eclipse.pass.deposit.model.DepositMetadata.Manuscript; +import org.eclipse.pass.deposit.model.DepositMetadata.Person; +import org.eclipse.pass.deposit.model.DepositSubmission; +import org.eclipse.pass.deposit.provider.util.CitationUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * DSpace metadata fields set: + *
    + *
  • dc.title for the Manuscript + *
  • dc.publisher for the publisher name + *
  • dc.identifier.citation for the Manuscript + *
  • dc.identifier.doi for the DOI + *
  • dc.contributor for each non-submitter associated with the Manuscript + *
  • dc.description.abstract for the Manuscript + *
  • dc.date.issued for the publication date + *
  • DSPACE_FIELD_EMBARGO_LIFT Date that the embargo is lifted + *
  • DSPACE_FIELD_EMBARGO_TERMS Date that the embargo is lifted + *
+ */ +@Component +public class DSpaceMetadataMapper { + // Section of workspace item form to add metadata + static final String SECTION_ONE = "traditionalpageone"; + static final String SECTION_TWO = "traditionalpagetwo"; + + private final String dspaceFieldEmbargoLift; + private final String dspaceFieldEmbargoTerms; + + public DSpaceMetadataMapper(@Value("${dspace.field.embargo.lift}") String dspaceFieldEmbargoLift, + @Value("${dspace.field.embargo.terms}") String dspaceFieldEmbargoTerms) { + this.dspaceFieldEmbargoLift = dspaceFieldEmbargoLift; + this.dspaceFieldEmbargoTerms = dspaceFieldEmbargoTerms; + } + + public String patchWorkspaceItem(DepositSubmission submission) { + DepositMetadata depositMd = submission.getMetadata(); + Journal journalMd = depositMd.getJournalMetadata(); + Article articleMd = depositMd.getArticleMetadata(); + Manuscript manuscriptMd = depositMd.getManuscriptMetadata(); + + JSONArray metadata = new JSONArray(); + + // Required by DSpace + metadata.add(add_array(SECTION_ONE, "dc.title", manuscriptMd.getTitle())); + + if (journalMd != null && journalMd.getPublisherName() != null) { + metadata.add(add_array(SECTION_ONE, "dc.publisher", journalMd.getPublisherName())); + } + + if (articleMd.getDoi() != null) { + metadata.add(add_array(SECTION_ONE, "dc.identifier.doi", articleMd.getDoi().toString())); + } + + if (manuscriptMd.getMsAbstract() != null) { + metadata.add(add_array(SECTION_TWO, "dc.description.abstract", manuscriptMd.getMsAbstract())); + } + + String citation = CitationUtil.createCitation(submission); + + if (!citation.isEmpty()) { + metadata.add(add_array(SECTION_ONE, "dc.identifier.citation", citation)); + } + + // Required by DSpace as ISO 8601 local date + metadata.add(add_array(SECTION_ONE, "dc.date.issued", journalMd.getPublicationDate(). + format(DateTimeFormatter.ISO_LOCAL_DATE))); + + // Add non-submitters as authors + String[] authors = depositMd.getPersons().stream().filter( + p -> p.getType() != DepositMetadata.PERSON_TYPE.submitter). + map(Person::getName).toArray(String[]::new); + + metadata.add(add_array(SECTION_ONE, "dc.contributor.author", authors)); + + ZonedDateTime embargoLiftDate = articleMd.getEmbargoLiftDate(); + + if (embargoLiftDate != null) { + String liftDate = embargoLiftDate.format(DateTimeFormatter.ISO_LOCAL_DATE); + + metadata.add(add_array(SECTION_ONE, dspaceFieldEmbargoLift, liftDate)); + metadata.add(add_array(SECTION_ONE, dspaceFieldEmbargoTerms, liftDate)); + } + + // Required by DSpace + metadata.add(add("license", "granted", "true")); + + return metadata.toString(); + } + + private JSONObject add(String section, String key, String value) { + JSONObject op = new JSONObject(); + + op.put("op", "add"); + op.put("path", "/sections/" + section + "/" + key); + op.put("value", value); + + return op; + } + + private JSONObject add_array(String section, String key, String... values) { + JSONObject op = new JSONObject(); + + op.put("op", "add"); + op.put("path", "/sections/" + section + "/" + key); + + JSONArray op_value = new JSONArray(); + for (String value : values) { + op_value.add(array_value(value)); + } + + op.put("value", op_value); + + return op; + } + + private JSONObject array_value(String value) { + JSONObject obj = new JSONObject(); + obj.put("value", value); + return obj; + } +} diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/inveniordm/InvenioRdmMetadataMapper.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/inveniordm/InvenioRdmMetadataMapper.java index f73235a56..1b4885016 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/inveniordm/InvenioRdmMetadataMapper.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/inveniordm/InvenioRdmMetadataMapper.java @@ -15,6 +15,7 @@ */ package org.eclipse.pass.deposit.provider.inveniordm; +import java.time.format.DateTimeFormatter; import java.util.Objects; import net.minidev.json.JSONArray; @@ -49,8 +50,9 @@ public JSONObject toInvenioMetadata(DepositSubmission depositSubmission) { invenioMetadata.put("creators", creators); String title = depositSubmission.getSubmissionMeta().get("title").getAsString(); invenioMetadata.put("title", title); - String publicationDate = depositMetadata.getJournalMetadata().getPublicationDate(); - invenioMetadata.put("publication_date", publicationDate); + + invenioMetadata.put("publication_date", depositMetadata.getJournalMetadata(). + getPublicationDate().format(DateTimeFormatter.ISO_LOCAL_DATE)); JSONObject resourceType = new JSONObject(); resourceType.put("id", "publication-article"); invenioMetadata.put("resource_type", resourceType); diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriter.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriter.java index ee7a3853e..fb41ade8d 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriter.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriter.java @@ -49,14 +49,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import au.edu.apsr.mtk.base.AmdSec; import au.edu.apsr.mtk.base.Constants; import au.edu.apsr.mtk.base.Div; import au.edu.apsr.mtk.base.DmdSec; @@ -70,11 +68,11 @@ import au.edu.apsr.mtk.base.METSWrapper; import au.edu.apsr.mtk.base.MdSec; import au.edu.apsr.mtk.base.MdWrap; -import au.edu.apsr.mtk.base.SourceMD; import au.edu.apsr.mtk.base.StructMap; import org.eclipse.pass.deposit.assembler.PackageStream; import org.eclipse.pass.deposit.model.DepositMetadata; import org.eclipse.pass.deposit.model.DepositSubmission; +import org.eclipse.pass.deposit.provider.util.CitationUtil; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -105,8 +103,6 @@ public class DspaceMetadataDomWriter { private METS mets; - private int authorIndex; - DspaceMetadataDomWriter(DocumentBuilderFactory dbf) { try { this.dbf = dbf; @@ -409,7 +405,6 @@ Element createDublinCoreMetadataDCMES(DepositSubmission submission) { // Attach a for each Person associated with the submission to the Manuscript metadata DepositMetadata nimsMd = submission.getMetadata(); DepositMetadata.Manuscript manuscriptMd = nimsMd.getManuscriptMetadata(); - DepositMetadata.Article articleMd = nimsMd.getArticleMetadata(); DepositMetadata.Journal journalMd = nimsMd.getJournalMetadata(); // Attach a for the Manuscript title @@ -435,11 +430,7 @@ Element createDublinCoreMetadataDCMES(DepositSubmission submission) { record.appendChild(publisher); } - // Begin building citation string - StringBuilder citationBldr = new StringBuilder(); - - // Attach a for each author of the manuscript and add authorIndex to citation - authorIndex = 0; + // Attach a for each author of the manuscript nimsMd.getPersons().forEach(p -> { // Only include authorIndex, PIs and CoPIs as contributors if (p.getType() != DepositMetadata.PERSON_TYPE.submitter) { @@ -447,56 +438,18 @@ Element createDublinCoreMetadataDCMES(DepositSubmission submission) { contributor.setTextContent(p.getName()); record.appendChild(contributor); } - - if (p.getType() == DepositMetadata.PERSON_TYPE.author) { - // Citation: For author 0, add name. For authorIndex 1 and 2, add comma then name. - // For author 3, add comma and "et al". For later authorIndex, do nothing. - if (authorIndex == 0) { - citationBldr.append(p.getReversedName()); - } else if (authorIndex <= 2) { - citationBldr.append(", " + p.getReversedName()); - } else if (authorIndex == 3) { - citationBldr.append(", et al"); - } - authorIndex++; - } }); - if (authorIndex == 0) { + + if (nimsMd.getPersons().stream().filter(p -> p.getType() == DepositMetadata.PERSON_TYPE.author).count() == 0) { throw new RuntimeException("No authors found in the manuscript metadata!"); } - // Add period at end of author list in citation - citationBldr.append("."); - // Attach a if not empty - // publication date - after a single space, in parens, followed by "." - if (journalMd != null && journalMd.getPublicationDate() != null && !journalMd.getPublicationDate().isEmpty()) { - citationBldr.append(" (" + journalMd.getPublicationDate() + ")."); - } - // article title - after single space, in double quotes with "." inside - if (articleMd != null && articleMd.getTitle() != null && !articleMd.getTitle().isEmpty()) { - citationBldr.append(" \"" + articleMd.getTitle() + ".\""); - } - // journal title - after single space, followed by "." - if (journalMd != null && journalMd.getJournalTitle() != null && !journalMd.getJournalTitle().isEmpty()) { - citationBldr.append(" " + journalMd.getJournalTitle() + "."); - } - // volume - after single space - if (articleMd != null && articleMd.getVolume() != null && !articleMd.getVolume().isEmpty()) { - citationBldr.append(" " + articleMd.getVolume()); - } - // issue - after single space, inside parens, followed by "." - if (articleMd != null && articleMd.getIssue() != null && !articleMd.getIssue().isEmpty()) { - citationBldr.append(" (" + articleMd.getIssue() + ")."); - } - // DOI - after single space, followed by "." - if (articleMd != null && articleMd.getDoi() != null) { - citationBldr.append(" " + articleMd.getDoi().toString() + "."); - } + String citation = CitationUtil.createCitation(submission); - if (!citationBldr.toString().isEmpty()) { - Element citation = dcDocument.createElementNS(DCTERMS_NS, asQname(DCTERMS_NS, DCT_BIBLIOCITATION)); - citation.setTextContent(citationBldr.toString()); - record.appendChild(citation); + if (!citation.isEmpty()) { + Element el = dcDocument.createElementNS(DCTERMS_NS, asQname(DCTERMS_NS, DCT_BIBLIOCITATION)); + el.setTextContent(citation); + record.appendChild(el); } return record; @@ -656,47 +609,6 @@ private FileSec createFileSec() throws METSException { return fs; } - private SourceMD getSourceMd(String id) throws METSException { - if (id == null) { - return createSourceMd(); - } - - Optional amdSec = mets - .getAmdSecs() - .stream() - .filter(candidateAmdSec -> candidateAmdSec.getSourceMD(id) != null) - .findAny(); - - if (amdSec.isPresent()) { - return amdSec.get().getSourceMD(id); - } - - throw new RuntimeException("SourceMD with id '" + id + "' not found."); - } - - private SourceMD createSourceMd() throws METSException { - AmdSec amdSec = getAmdSec(); - SourceMD sourceMD = amdSec.newSourceMD(); - sourceMD.setID(mintId()); - amdSec.addSourceMD(sourceMD); - return sourceMD; - } - - private AmdSec getAmdSec() throws METSException { - if (mets.getAmdSecs() == null || mets.getAmdSecs().isEmpty()) { - return createAmdSec(); - } - - return mets.getAmdSecs().get(0); - } - - private AmdSec createAmdSec() throws METSException { - AmdSec as = mets.newAmdSec(); - mets.addAmdSec(as); - as.setID(mintId()); - return as; - } - /** * Obtains the specified {@code }, or creates a new {@code } if {@code id} is {@code null}. * diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/util/CitationUtil.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/util/CitationUtil.java new file mode 100644 index 000000000..ae00ff136 --- /dev/null +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/util/CitationUtil.java @@ -0,0 +1,86 @@ +/* + * Copyright 2025 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.provider.util; + +import java.time.format.DateTimeFormatter; + +import org.eclipse.pass.deposit.model.DepositMetadata; +import org.eclipse.pass.deposit.model.DepositMetadata.Person; +import org.eclipse.pass.deposit.model.DepositSubmission; + +public class CitationUtil { + private CitationUtil() {} + + // TODO Could we use citation from CrossRef metadata? + public static String createCitation(DepositSubmission submission) { + DepositMetadata submissionMd = submission.getMetadata(); + DepositMetadata.Article articleMd = submissionMd.getArticleMetadata(); + DepositMetadata.Journal journalMd = submissionMd.getJournalMetadata(); + + StringBuilder citationBldr = new StringBuilder(); + + int authorIndex = 0; + for (Person p: submissionMd.getPersons()) { + if (p.getType() == DepositMetadata.PERSON_TYPE.author) { + // Citation: For author 0, add name. For authorIndex 1 and 2, add comma then name. + // For author 3, add comma and "et al". For later authorIndex, do nothing. + if (authorIndex == 0) { + citationBldr.append(p.getReversedName()); + } else if (authorIndex <= 2) { + citationBldr.append(", " + p.getReversedName()); + } else if (authorIndex == 3) { + citationBldr.append(", et al"); + } + authorIndex++; + } + } + + // Add period at end of author list in citation + + if (citationBldr.length() > 0) { + citationBldr.append("."); + } + + // Attach a if not empty + // publication date - after a single space, in parens, followed by "." + if (journalMd != null && journalMd.getPublicationDate() != null) { + citationBldr.append(" (" + journalMd.getPublicationDate(). + format(DateTimeFormatter.ISO_LOCAL_DATE) + ")."); + } + // article title - after single space, in double quotes with "." inside + if (articleMd != null && articleMd.getTitle() != null && !articleMd.getTitle().isEmpty()) { + citationBldr.append(" \"" + articleMd.getTitle() + ".\""); + } + // journal title - after single space, followed by "." + if (journalMd != null && journalMd.getJournalTitle() != null && !journalMd.getJournalTitle().isEmpty()) { + citationBldr.append(" " + journalMd.getJournalTitle() + "."); + } + // volume - after single space + if (articleMd != null && articleMd.getVolume() != null && !articleMd.getVolume().isEmpty()) { + citationBldr.append(" " + articleMd.getVolume()); + } + // issue - after single space, inside parens, followed by "." + if (articleMd != null && articleMd.getIssue() != null && !articleMd.getIssue().isEmpty()) { + citationBldr.append(" (" + articleMd.getIssue() + ")."); + } + // DOI - after single space, followed by "." + if (articleMd != null && articleMd.getDoi() != null) { + citationBldr.append(" " + articleMd.getDoi().toString() + "."); + } + + return citationBldr.toString(); + } +} diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/SubmissionProcessor.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/SubmissionProcessor.java index 3f9e03b26..d4f878cac 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/SubmissionProcessor.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/SubmissionProcessor.java @@ -291,8 +291,8 @@ private static boolean isSubmittedByUser(Submission submission) { static Collection getLookupKeys(Repository repo) { final List keys = new ArrayList<>(); - ofNullable(repo.getName()).ifPresent(keys::add); ofNullable(repo.getRepositoryKey()).ifPresent(keys::add); + ofNullable(repo.getName()).ifPresent(keys::add); ofNullable(repo.getId()).map(Object::toString).ifPresent(keys::add); return keys; diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataService.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataService.java index 771527579..476e21be0 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataService.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataService.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Objects; +import org.eclipse.pass.deposit.support.dspace.DSpaceDepositService; import org.eclipse.pass.support.client.ModelUtil; import org.eclipse.pass.support.client.PassClient; import org.eclipse.pass.support.client.PassClientSelector; @@ -51,7 +52,7 @@ public class DeploymentTestDataService { public static final String SUBMISSION_ID = "submission.id"; private final PassClient passClient; - private final DspaceDepositService dspaceDepositService; + private final DSpaceDepositService dspaceDepositService; @Value("${pass.test.data.policy.title}") private String testPolicyTitle; @@ -66,7 +67,7 @@ public class DeploymentTestDataService { private String dspaceKey; @Autowired - public DeploymentTestDataService(PassClient passClient, DspaceDepositService dspaceDepositService) { + public DeploymentTestDataService(PassClient passClient, DSpaceDepositService dspaceDepositService) { this.passClient = passClient; this.dspaceDepositService = dspaceDepositService; } @@ -131,7 +132,7 @@ private void deleteDepositsInRepoIfNeeded(Grant testGrant) throws IOException { )); List testSubmissions = passClient.streamObjects(testSubmissionSelector).toList(); if (!testSubmissions.isEmpty()) { - DspaceDepositService.AuthContext authContext = dspaceDepositService.authenticate(); + DSpaceDepositService.AuthContext authContext = dspaceDepositService.authenticate(); testSubmissions.forEach(testSubmission -> { try { PassClientSelector testDepositSelector = new PassClientSelector<>(Deposit.class); @@ -149,7 +150,7 @@ private void deleteDepositsInRepoIfNeeded(Grant testGrant) throws IOException { } } - private void deleteDepositInRepoIfNeeded(Deposit deposit, DspaceDepositService.AuthContext authContext) { + private void deleteDepositInRepoIfNeeded(Deposit deposit, DSpaceDepositService.AuthContext authContext) { if (isDspaceDeposit(deposit)) { dspaceDepositService.deleteDeposit(deposit, authContext); } diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/deploymenttest/DspaceDepositService.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/dspace/DSpaceDepositService.java similarity index 64% rename from pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/deploymenttest/DspaceDepositService.java rename to pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/dspace/DSpaceDepositService.java index d8cd787d1..336825382 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/deploymenttest/DspaceDepositService.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/support/dspace/DSpaceDepositService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.eclipse.pass.deposit.support.deploymenttest; +package org.eclipse.pass.deposit.support.dspace; import java.net.URI; import java.util.List; @@ -29,6 +29,8 @@ import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; +import org.eclipse.pass.deposit.assembler.DepositFileResource; +import org.eclipse.pass.deposit.transport.RepositoryConnectivityService; import org.eclipse.pass.support.client.model.Deposit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,35 +48,40 @@ /** * @author Russ Poetker (rpoetke1@jh.edu) */ -@ConditionalOnProperty(name = "pass.test.data.job.enabled") +@ConditionalOnProperty(name = "dspace.api.url") @Service -public class DspaceDepositService { - private static final Logger LOG = LoggerFactory.getLogger(DspaceDepositService.class); - - public static final String X_XSRF_TOKEN = "X-XSRF-TOKEN"; - public static final String COOKIE = "Cookie"; - public static final String DSPACE_XSRF_COOKIE = "DSPACE-XSRF-COOKIE="; - public static final String AUTHORIZATION = "Authorization"; +public class DSpaceDepositService { + private static final Logger LOG = LoggerFactory.getLogger(DSpaceDepositService.class); + private static final String X_XSRF_TOKEN = "X-XSRF-TOKEN"; + private static final String COOKIE = "Cookie"; + private static final String DSPACE_XSRF_COOKIE = "DSPACE-XSRF-COOKIE="; + private static final String AUTHORIZATION = "Authorization"; private final RestClient restClient; + @Value("${dspace.api.url}") + private String dspaceApiUrl; + + @Value("${dspace.website.url}") + private String dspaceWebsiteUrl; + @Value("${dspace.user}") private String dspaceUsername; @Value("${dspace.password}") private String dspacePassword; - @Value("${dspace.server}") - private String dspaceServer; + @Value("${dspace.collection.handle}") + private String dspaceCollectionHandle; + + private final RepositoryConnectivityService repositoryConnectivityService; - @Value("${dspace.server.api.protocol}") - private String dspaceApiProtocol; + public record AuthContext(String xsrfToken, String authToken){} - protected record AuthContext(String xsrfToken, String authToken){} + public DSpaceDepositService(@Value("${dspace.api.url}") String dspaceApiUrl, + RepositoryConnectivityService repositoryConnectivityService) { + this.repositoryConnectivityService = repositoryConnectivityService; - public DspaceDepositService(@Value("${dspace.server.api.protocol}") String dspaceApiProtocol, - @Value("${dspace.server}") String dspaceServer, - @Value("${dspace.server.api.path}") String dspaceApiPath) { PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() .setDefaultSocketConfig(SocketConfig.custom() .setSoTimeout(Timeout.ofMinutes(1)) @@ -89,9 +96,10 @@ public DspaceDepositService(@Value("${dspace.server.api.protocol}") String dspac .setConnectionManager(connectionManager) .disableCookieManagement() .build(); + this.restClient = RestClient.builder() .requestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)) - .baseUrl(dspaceApiProtocol + "://" + dspaceServer + dspaceApiPath) + .baseUrl(dspaceApiUrl) .build(); } @@ -100,7 +108,7 @@ public DspaceDepositService(@Value("${dspace.server.api.protocol}") String dspac * authentication docs. * @return an AuthContext containing authToken and xsrfToken */ - AuthContext authenticate() { + public AuthContext authenticate() { // Using exchange is needed for this call because dspace returns 404, but the response headers has the // csrf token header DSPACE-XSRF-TOKEN ResponseEntity csrfResponse = restClient.get() @@ -128,11 +136,11 @@ AuthContext authenticate() { * Deletes the deposit in the remote repository. * @param deposit contains deposit info to do the delete */ - void deleteDeposit(Deposit deposit, AuthContext authContext) { + public void deleteDeposit(Deposit deposit, AuthContext authContext) { LOG.warn("Deleting Test Deposit In Dspace (PASS Deposit ID={})", deposit.getId()); URI accessUrl = Objects.nonNull(deposit.getRepositoryCopy()) - ? deposit.getRepositoryCopy().getAccessUrl() - : null; + ? deposit.getRepositoryCopy().getAccessUrl() + : null; LOG.warn("Deposit accessUrl={}", accessUrl); if (Objects.nonNull(accessUrl)) { String handleValue = parseHandleFilter(accessUrl); @@ -162,12 +170,20 @@ private String getAuthHeaderValue(ResponseEntity response, String header) } private String parseHandleFilter(URI accessUrl) { - String handleDelim = dspaceApiProtocol + "://" + dspaceServer + "/handle/"; - String[] handleTokens = accessUrl.toString().split(handleDelim); - if (handleTokens.length != 2) { + String path = accessUrl.getPath(); + + String mark = "/handle/"; + int start = path.indexOf(mark); + + if (start == -1) { throw new RuntimeException("Unable to determine dspace item handle for " + accessUrl); } - return handleTokens[1]; + + return path.substring(start + mark.length()); + } + + public String createAccessUrlFromItemUuid(String itemUuid) { + return dspaceWebsiteUrl + "/items/" + itemUuid; } private String findItemUuid(String handleValue, AuthContext authContext, String submissionTitle) { @@ -233,4 +249,88 @@ private void deleteItem(String itemUuid, AuthContext authContext) { .toBodilessEntity(); LOG.warn("Deleted item UUID={}", itemUuid); } + + public String getUuidForHandle(String handle, AuthContext authContext) { + LOG.debug("Search Dspace for object with handle={}", handle); + + String searchResponse = restClient.get() + .uri("/discover/search/objects?query=handle:{handleValue}", handle) + .accept(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION, authContext.authToken()) + .retrieve() + .body(String.class); + + List> searchArray = JsonPath.parse(searchResponse).read("$..indexableObject[?(@.handle)]"); + + if (searchArray.size() == 1) { + Map itemMap = searchArray.get(0); + String uuid = itemMap.get("uuid").toString(); + + LOG.debug("Found object UUID={} with handle={}", uuid, handle); + + return uuid; + } + + throw new RuntimeException("Unable to find object with handle: " + handle); + } + + public String createWorkspaceItem(List files, AuthContext authContext) { + MultiValueMap body = new LinkedMultiValueMap<>(); + + for (DepositFileResource res: files) { + body.add("file", res.getResource()); + } + + String uuid = getUuidForHandle(dspaceCollectionHandle, authContext); + + String json = restClient.post() + .uri("/submission/workspaceitems?owningCollection={collectionUuid}", uuid) + .header(AUTHORIZATION, authContext.authToken()) + .header(X_XSRF_TOKEN, authContext.xsrfToken()) + .header(COOKIE, DSPACE_XSRF_COOKIE + authContext.xsrfToken()) + .body(body) + .retrieve().body(String.class); + + return json; + } + + public void patchWorkspaceItem(int workspaceItemId, String patch, AuthContext authContext) { + restClient.patch() + .uri("/submission/workspaceitems/{workspaceItemId}", workspaceItemId) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION, authContext.authToken()) + .header(X_XSRF_TOKEN, authContext.xsrfToken()) + .header(COOKIE, DSPACE_XSRF_COOKIE + authContext.xsrfToken()) + .body(patch) + .retrieve().toBodilessEntity(); + } + + public void createWorkflowItem(int workspaceItemId, AuthContext authContext) { + String workspaceItemUrl = dspaceApiUrl + "/submission/workspaceitems/" + workspaceItemId; + + restClient.post() + .uri("/workflow/workflowitems") + .header("Content-Type", "text/uri-list") + .header(AUTHORIZATION, authContext.authToken()) + .header(X_XSRF_TOKEN, authContext.xsrfToken()) + .header(COOKIE, DSPACE_XSRF_COOKIE + authContext.xsrfToken()) + .body(workspaceItemUrl) + .retrieve().toBodilessEntity(); + } + + public String getWorkspaceItem(int workspaceItemId, AuthContext authContext) { + String json = restClient.get() + .uri("/submission/workspaceitems/{workspaceItemId}", workspaceItemId) + .header(AUTHORIZATION, authContext.authToken()) + .header(X_XSRF_TOKEN, authContext.xsrfToken()) + .header(COOKIE, DSPACE_XSRF_COOKIE + authContext.xsrfToken()) + .retrieve().body(String.class); + + return json; + } + + public boolean verifyConnectivity() { + // The base API URL is a valid service endpoint + return repositoryConnectivityService.verifyConnectByURL(dspaceApiUrl); + } } diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/Transport.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/Transport.java index c40d321f3..019eadbc9 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/Transport.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/Transport.java @@ -129,7 +129,8 @@ enum PROTOCOL { SWORDv2, filesystem, devnull, - invenioRdm + invenioRdm, + DSpace } /** diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceResponse.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceResponse.java new file mode 100644 index 000000000..a06ae2a2f --- /dev/null +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceResponse.java @@ -0,0 +1,69 @@ +/* + * Copyright 2025 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.transport.dspace; + +import java.io.IOException; +import java.net.URI; + +import org.eclipse.pass.deposit.service.DepositUtil; +import org.eclipse.pass.deposit.transport.TransportResponse; +import org.eclipse.pass.support.client.PassClient; +import org.eclipse.pass.support.client.model.CopyStatus; +import org.eclipse.pass.support.client.model.Deposit; +import org.eclipse.pass.support.client.model.DepositStatus; +import org.eclipse.pass.support.client.model.RepositoryCopy; + +class DSpaceResponse implements TransportResponse { + private final boolean success; + private final Throwable throwable; + private final String depositAccessUrl; + + DSpaceResponse(boolean success, String depositAccessUrl) { + this(success, depositAccessUrl, null); + } + + DSpaceResponse(boolean success, String depositAccessUrl, Throwable throwable) { + this.success = success; + this.depositAccessUrl = depositAccessUrl; + this.throwable = throwable; + } + + @Override + public boolean success() { + return success; + } + + @Override + public Throwable error() { + return throwable; + } + + @Override + public void onSuccess(DepositUtil.DepositWorkerContext depositWorkerContext, PassClient passClient) { + try { + RepositoryCopy repositoryCopy = depositWorkerContext.repoCopy(); + repositoryCopy.setAccessUrl(URI.create(depositAccessUrl)); + repositoryCopy.getExternalIds().add(depositAccessUrl); + repositoryCopy.setCopyStatus(CopyStatus.COMPLETE); + passClient.updateObject(repositoryCopy); + Deposit deposit = depositWorkerContext.deposit(); + deposit.setDepositStatus(DepositStatus.ACCEPTED); + passClient.updateObject(deposit); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceSession.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceSession.java new file mode 100644 index 000000000..56a0d9262 --- /dev/null +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceSession.java @@ -0,0 +1,98 @@ +/* + * Copyright 2025 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.transport.dspace; + +import java.util.Map; + +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import org.eclipse.pass.deposit.assembler.PackageStream; +import org.eclipse.pass.deposit.model.DepositSubmission; +import org.eclipse.pass.deposit.provider.dspace.DSpaceMetadataMapper; +import org.eclipse.pass.deposit.support.dspace.DSpaceDepositService; +import org.eclipse.pass.deposit.support.dspace.DSpaceDepositService.AuthContext; +import org.eclipse.pass.deposit.transport.TransportResponse; +import org.eclipse.pass.deposit.transport.TransportSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * In order to do a deposit to DSpace, first a workspace item is created with the files. + * Then the workspace item is patched with the correct metadata. + * Finally a workflow item is created referencing the workspace item in order to trigger submission. + */ +class DSpaceSession implements TransportSession { + private static final Logger LOG = LoggerFactory.getLogger(DSpaceSession.class); + + private final DSpaceDepositService dspaceDepositService; + private final DSpaceMetadataMapper dspaceMetadataMapper; + + public DSpaceSession(DSpaceDepositService dspaceDepositService, DSpaceMetadataMapper dspaceMetadataMapper) { + this.dspaceDepositService = dspaceDepositService; + this.dspaceMetadataMapper = dspaceMetadataMapper; + } + + @Override + public TransportResponse send(PackageStream packageStream, Map metadata) { + try { + DepositSubmission depositSubmission = packageStream.getDepositSubmission(); + + LOG.warn("Processing Dspace Deposit for Submission: {}", depositSubmission.getId()); + + AuthContext authContext = dspaceDepositService.authenticate(); + + // Create WorkspaceItem + + DocumentContext workspaceItemContext = JsonPath.parse(dspaceDepositService.createWorkspaceItem( + packageStream.getCustodialContent(), authContext)); + int workspaceItemId = workspaceItemContext.read("$._embedded.workspaceitems[0].id"); + + LOG.debug("Created WorkspaceItem: {}", workspaceItemId); + + // Patch in metadata + + String patchJson = dspaceMetadataMapper.patchWorkspaceItem(depositSubmission); + + LOG.debug("Patching WorkspaceItem to add metadata {}", patchJson); + dspaceDepositService.patchWorkspaceItem(workspaceItemId, patchJson, authContext); + + // Publish the WorkspaceItem + + LOG.debug("Creating WorkflowItem for WorkspaceItem {}", workspaceItemId); + dspaceDepositService.createWorkflowItem(workspaceItemId, authContext); + + String itemUuid = workspaceItemContext.read("$._embedded.workspaceitems[0]._embedded.item.uuid"); + String accessUrl = dspaceDepositService.createAccessUrlFromItemUuid(itemUuid); + + LOG.warn("Completed DSpace Deposit for Submission: {}, accessUrl: {}", + depositSubmission.getId(), accessUrl); + return new DSpaceResponse(true, accessUrl); + } catch (Exception e) { + LOG.error("Error depositing into DSpace", e); + return new DSpaceResponse(false, null, e); + } + } + + @Override + public boolean closed() { + return true; + } + + @Override + public void close() { + // no-op resources are closed with try-with-resources + } +} diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceTransport.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceTransport.java new file mode 100644 index 000000000..e258c670a --- /dev/null +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/transport/dspace/DSpaceTransport.java @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.transport.dspace; + +import java.util.Map; + +import org.eclipse.pass.deposit.provider.dspace.DSpaceMetadataMapper; +import org.eclipse.pass.deposit.support.dspace.DSpaceDepositService; +import org.eclipse.pass.deposit.transport.Transport; +import org.eclipse.pass.deposit.transport.TransportSession; +import org.springframework.stereotype.Component; + +@Component +public class DSpaceTransport implements Transport { + private final DSpaceDepositService dspaceDepositService; + private final DSpaceMetadataMapper dspaceMetadataMapper; + + public DSpaceTransport(DSpaceDepositService dspaceDepositService, DSpaceMetadataMapper dspaceMetadataMapper) { + this.dspaceDepositService = dspaceDepositService; + this.dspaceMetadataMapper = dspaceMetadataMapper; + } + + @Override + public PROTOCOL protocol() { + return PROTOCOL.DSpace; + } + + @Override + public TransportSession open(Map hints) { + return new DSpaceSession(dspaceDepositService, dspaceMetadataMapper); + } + + @Override + public boolean checkConnectivity(Map hints) { + return dspaceDepositService.verifyConnectivity(); + } +} diff --git a/pass-deposit-services/deposit-core/src/main/resources/application.properties b/pass-deposit-services/deposit-core/src/main/resources/application.properties index 6c7039f14..544847899 100644 --- a/pass-deposit-services/deposit-core/src/main/resources/application.properties +++ b/pass-deposit-services/deposit-core/src/main/resources/application.properties @@ -27,13 +27,14 @@ pmc.ftp.port=${PMC_FTP_PORT} pmc.ftp.user=${PMC_FTP_USER} pmc.ftp.password=${PMC_FTP_PASSWORD} -dspace.host=${DSPACE_HOST} -dspace.port=${DSPACE_PORT} dspace.server=${DSPACE_SERVER} +dspace.api.url=${DSPACE_API_URL} +dspace.website.url=${DSPACE_WEBSITE_URL} dspace.user=${DSPACE_USER} dspace.password=${DSPACE_PASSWORD} -dspace.server.api.protocol=${DSPACE_API_PROTOCOL} -dspace.server.api.path=${DSPACE_API_PATH} +dspace.collection.handle=${DSPACE_COLLECTION_HANDLE} +dspace.field.embargo.lift=${DSPACE_FIELD_EMBARGO_LIFT:local.embargo.lift} +dspace.field.embargo.terms=${DSPACE_FIELD_EMBARGO_TERMS:local.embargo.terms} inveniordm.api.baseUrl=${INVENIORDM_API_BASE_URL:} inveniordm.api.token=${INVENIORDM_API_TOKEN} diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/assembler/PassFileResourceIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/assembler/PassFileResourceIT.java index c654d4300..9ce0bd73e 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/assembler/PassFileResourceIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/assembler/PassFileResourceIT.java @@ -41,7 +41,7 @@ void testGetInputStream() throws IOException { file.setUri(fileUri); passClient.createObject(file); - PassFileResource passFileResource = new PassFileResource(passClient, file.getId()); + PassFileResource passFileResource = new PassFileResource(passClient, file.getId(), file.getName()); // WHEN InputStream inputStream = passFileResource.getInputStream(); diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/AbstractJacksonMappingTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/AbstractJacksonMappingTest.java index 5f5451561..6e14a9ec6 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/AbstractJacksonMappingTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/AbstractJacksonMappingTest.java @@ -30,8 +30,7 @@ @TestPropertySource( locations = "/test-application.properties", properties = { - "dspace.host=test-dspace-host", - "dspace.port=test-dspace-port", + "dspace.server=test-dspace-host:8000", "pmc.ftp.host=test-ftp-host", "pmc.ftp.port=test-ftp-port", }) diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/PropertyResolvingDeserializerTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/PropertyResolvingDeserializerTest.java index 76d5237f0..cbc00c20d 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/PropertyResolvingDeserializerTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/PropertyResolvingDeserializerTest.java @@ -30,7 +30,7 @@ public void noPropertyResolutionTest() throws Exception { RepositoryConfig.class); assertEquals(SwordV2Binding.PROTO, config.getTransportConfig().getProtocolBinding().getProtocol()); SwordV2Binding swordV2Binding = (SwordV2Binding) config.getTransportConfig().getProtocolBinding(); - assertTrue(swordV2Binding.getDefaultCollectionUrl().contains("http://test-dspace-host:test-dspace-port")); + assertTrue(swordV2Binding.getDefaultCollectionUrl().contains("http://test-dspace-host:8000")); } @Test @@ -39,6 +39,6 @@ public void resolvePropertiesTest() throws Exception { RepositoryConfig.class); assertTrue(config.getTransportConfig().getProtocolBinding().getProtocol().equals(SwordV2Binding.PROTO)); SwordV2Binding swordV2Binding = (SwordV2Binding) config.getTransportConfig().getProtocolBinding(); - assertFalse(swordV2Binding.getDefaultCollectionUrl().contains("${dspace.host}")); + assertFalse(swordV2Binding.getDefaultCollectionUrl().contains("${dspace.server}")); } } diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/RepositoryConfigMappingTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/RepositoryConfigMappingTest.java index a2184f7e8..ddaa547d9 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/RepositoryConfigMappingTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/RepositoryConfigMappingTest.java @@ -75,12 +75,12 @@ public class RepositoryConfigMappingTest extends AbstractJacksonMappingTest { " \"protocol\": \"SWORDv2\",\n" + " \"username\": \"sworduser\",\n" + " \"password\": \"swordpass\",\n" + - " \"server-fqdn\": \"${dspace.host}\",\n" + - " \"server-port\": \"${dspace.port}\",\n" + - " \"service-doc\": \"http://${dspace.host}:${dspace" + - ".port}/swordv2/servicedocument\",\n" + - " \"default-collection\": \"http://${dspace.host}:${dspace" + - ".port}/swordv2/collection/123456789/2\",\n" + + " \"server-fqdn\": null,\n" + + " \"server-port\": null,\n" + + " \"service-doc\": \"http://${dspace.server}" + + "/swordv2/servicedocument\",\n" + + " \"default-collection\": \"http://${dspace.server}" + + "/swordv2/collection/123456789/2\",\n" + " \"on-behalf-of\": null,\n" + " \"deposit-receipt\": true,\n" + " \"user-agent\": \"pass-deposit/x.y.z\"\n" + diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/SimpleClassMappingTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/SimpleClassMappingTest.java index 939dcacd8..a7ec26b86 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/SimpleClassMappingTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/SimpleClassMappingTest.java @@ -40,10 +40,10 @@ public class SimpleClassMappingTest extends AbstractJacksonMappingTest { " \"protocol\": \"SWORDv2\",\n" + " \"username\": \"sworduser\",\n" + " \"password\": \"swordpass\",\n" + - " \"service-doc\": \"http://${dspace.host}:${dspace" + - ".port}/swordv2/servicedocument\",\n" + - " \"default-collection\": \"http://${dspace" + - ".host}:${dspace.port}/swordv2/collection/123456789/2\",\n" + + " \"service-doc\": \"http://${dspace.server}" + + "/swordv2/servicedocument\",\n" + + " \"default-collection\": \"http://${dspace.server}" + + "/swordv2/collection/123456789/2\",\n" + " \"on-behalf-of\": null,\n" + " \"deposit-receipt\": true,\n" + " \"user-agent\": \"pass-deposit/x.y.z\"\n" + @@ -108,9 +108,9 @@ public void mapSwordBinding() throws IOException { assertEquals("SWORDv2", swordBinding.getProtocol()); assertEquals("sworduser", swordBinding.getUsername()); assertEquals("swordpass", swordBinding.getPassword()); - assertEquals("http://test-dspace-host:test-dspace-port/swordv2/servicedocument", + assertEquals("http://test-dspace-host:8000/swordv2/servicedocument", swordBinding.getServiceDocUrl()); - assertEquals("http://test-dspace-host:test-dspace-port/swordv2/collection/123456789/2", + assertEquals("http://test-dspace-host:8000/swordv2/collection/123456789/2", swordBinding.getDefaultCollectionUrl()); assertNull(swordBinding.getOnBehalfOf()); assertTrue(swordBinding.isDepositReceipt()); diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/TransportConfigMappingTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/TransportConfigMappingTest.java index 2412c5637..b164be35c 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/TransportConfigMappingTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/repository/TransportConfigMappingTest.java @@ -96,10 +96,10 @@ public class TransportConfigMappingTest extends AbstractJacksonMappingTest { " \"protocol\": \"SWORDv2\",\n" + " \"username\": \"sworduser\",\n" + " \"password\": \"swordpass\",\n" + - " \"service-doc\": \"http://${dspace.host}:${dspace" + - ".port}/swordv2/servicedocument\",\n" + - " \"default-collection\": \"http://${dspace" + - ".host}:${dspace.port}/swordv2/collection/123456789/2\",\n" + + " \"service-doc\": \"http://${dspace.server}" + + "/swordv2/servicedocument\",\n" + + " \"default-collection\": \"http://${dspace.server}" + + "/swordv2/collection/123456789/2\",\n" + " \"on-behalf-of\": null,\n" + " \"deposit-receipt\": true,\n" + " \"user-agent\": \"pass-deposit/x.y.z\",\n" + diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/spring/AwsParamStoreConfigTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/spring/AwsParamStoreConfigTest.java index 9c4bbd02d..3d8deb84d 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/spring/AwsParamStoreConfigTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/config/spring/AwsParamStoreConfigTest.java @@ -45,6 +45,10 @@ pass.client.password=${PASS_CORE_PASSWORD:test-pw} dspace.user=${DSPACE_USER:test@test.edu} dspace.password=${DSPACE_PASSWORD:test-dspace-pw} + dspace.server=localhost:8000 + dspace.api.url=http://localhost:8000/api + dspace.website.url=http://localhost:8000/website + dspace.collection.handle=1234/1 """ ) @ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class) diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/dspace/DSpaceMetadataMapperTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/dspace/DSpaceMetadataMapperTest.java new file mode 100644 index 000000000..6cd7996e4 --- /dev/null +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/dspace/DSpaceMetadataMapperTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2025 Johns Hopkins University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.pass.deposit.provider.dspace; + +import static org.eclipse.pass.deposit.provider.dspace.DSpaceMetadataMapper.SECTION_ONE; +import static org.eclipse.pass.deposit.provider.dspace.DSpaceMetadataMapper.SECTION_TWO; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URI; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; + +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import org.eclipse.pass.deposit.model.DepositManifest; +import org.eclipse.pass.deposit.model.DepositMetadata; +import org.eclipse.pass.deposit.model.DepositMetadata.Article; +import org.eclipse.pass.deposit.model.DepositMetadata.Journal; +import org.eclipse.pass.deposit.model.DepositMetadata.Manuscript; +import org.eclipse.pass.deposit.model.DepositMetadata.PERSON_TYPE; +import org.eclipse.pass.deposit.model.DepositMetadata.Person; +import org.eclipse.pass.deposit.model.DepositSubmission; +import org.junit.jupiter.api.Test; + +public class DSpaceMetadataMapperTest { + @Test + public void testPatchWorkspaceItem() { + DSpaceMetadataMapper mapper = new DSpaceMetadataMapper("test.embargo.lift", "test.embargo.terms"); + + DepositSubmission ds = new DepositSubmission(); + DepositManifest manifest = new DepositManifest(); + DepositMetadata md = new DepositMetadata(); + + ds.setManifest(manifest); + ds.setMetadata(md); + + Article article = md.getArticleMetadata(); + Journal journal = md.getJournalMetadata(); + Manuscript manuscript = md.getManuscriptMetadata(); + + manuscript.setTitle("this is a title"); + manuscript.setMsAbstract("This is a compelling abstract."); + journal.setJournalTitle("journal title"); + journal.setPublisherName("publisher name"); + article.setDoi(URI.create("10.1016/j.iheduc.2015.08.004")); + article.setIssue("1"); + article.setVolume("2"); + + Person author1 = new Person(); + author1.setEmail("p1@example.com"); + author1.setFirstName("P1"); + author1.setFullName("P1 Person"); + author1.setLastName("Person"); + author1.setMiddleName("One"); + author1.setType(PERSON_TYPE.author); + + Person author2 = new Person(); + author2.setEmail("p2@example.com"); + author2.setFirstName("P2"); + author2.setFullName("P2 Person"); + author2.setLastName("Person"); + author2.setMiddleName("Two"); + author2.setType(PERSON_TYPE.author); + + md.getPersons().add(author1); + md.getPersons().add(author2); + + ZonedDateTime pubDate = ZonedDateTime.of(2024, 12, 19, 0, 0, 0, 0, ZoneId.systemDefault()); + ZonedDateTime embargoDate = ZonedDateTime.now().plusYears(1); + + journal.setPublicationDate(pubDate); + article.setEmbargoLiftDate(embargoDate); + + ds.setSubmissionDate(pubDate); + + String json = mapper.patchWorkspaceItem(ds); + + DocumentContext jsonContext = JsonPath.parse(json); + + checkValue(jsonContext, SECTION_ONE, "dc.title", manuscript.getTitle()); + checkValue(jsonContext, SECTION_ONE, "dc.identifier.doi", article.getDoi().toString()); + checkValue(jsonContext, SECTION_TWO, "dc.description.abstract", manuscript.getMsAbstract()); + checkValue(jsonContext, SECTION_ONE, "dc.publisher", journal.getPublisherName()); + checkValue(jsonContext, SECTION_ONE, "dc.identifier.citation", + "Person, P1 One, Person, P2 Two. (2024-12-19). journal title. 2 (1). 10.1016/j.iheduc.2015.08.004."); + checkValue(jsonContext, SECTION_ONE, "dc.contributor.author", "P1 Person", "P2 Person"); + checkValue(jsonContext, SECTION_ONE, "dc.date.issued", + journal.getPublicationDate().format(DateTimeFormatter.ISO_LOCAL_DATE)); + checkValue(jsonContext, SECTION_ONE, "test.embargo.lift", + article.getEmbargoLiftDate().format(DateTimeFormatter.ISO_LOCAL_DATE)); + checkValue(jsonContext, SECTION_ONE, "test.embargo.terms", + article.getEmbargoLiftDate().format(DateTimeFormatter.ISO_LOCAL_DATE)); + } + + private void checkValue(DocumentContext context, String section, String key, String... expected) { + String path = "$[?(@.path == '/sections/" + section + "/" + key + "')].value[*].value"; + + List values = context.read(path); + + assertEquals(Arrays.asList(expected), values); + } + + @Test + public void testPatchWorkspaceItemMinimalMetadata() { + DSpaceMetadataMapper mapper = new DSpaceMetadataMapper("test.embargo.lift", "test.embargo.terms"); + + DepositSubmission ds = new DepositSubmission(); + DepositManifest manifest = new DepositManifest(); + DepositMetadata md = new DepositMetadata(); + Journal journal = md.getJournalMetadata(); + + ds.setManifest(manifest); + ds.setMetadata(md); + + Manuscript manuscript = md.getManuscriptMetadata(); + ZonedDateTime pubDate = ZonedDateTime.now(); + + manuscript.setTitle("this is a title"); + journal.setPublicationDate(pubDate); + + String json = mapper.patchWorkspaceItem(ds); + + DocumentContext jsonContext = JsonPath.parse(json); + + checkValue(jsonContext, SECTION_ONE, "dc.title", manuscript.getTitle()); + checkValue(jsonContext, SECTION_ONE, "dc.date.issued", + journal.getPublicationDate().format(DateTimeFormatter.ISO_LOCAL_DATE)); + } +} diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriterTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriterTest.java index 7f4e6e3b5..9d8cacf54 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriterTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/DspaceMetadataDomWriterTest.java @@ -104,8 +104,6 @@ import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -120,10 +118,6 @@ public class DspaceMetadataDomWriterTest { @TempDir private Path tempDir; - private List custodialContent = Arrays.asList( - new ClassPathResource(this.getClass().getPackage().getName().replace(".", "/") + "/manuscript.txt"), - new ClassPathResource(this.getClass().getPackage().getName().replace(".", "/") + "/figure.jpg")); - private final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); private DspaceMetadataDomWriter underTest; @@ -229,7 +223,7 @@ public void writeSampleMets() throws Exception { when(journal.getJournalTitle()).thenReturn("American Journal of XYZ Research"); when(journal.getJournalId()).thenReturn("Am J of XYZ Res"); when(journal.getPublisherName()).thenReturn("Super Publisher"); - when(journal.getPublicationDate()).thenReturn("2018-09-12"); + when(journal.getPublicationDate()).thenReturn(ZonedDateTime.now()); underTest.addResource(r); underTest.addSubmission(submission); @@ -296,8 +290,6 @@ public void testAddResource() throws Exception { String type = "text/plain"; String checksumMd5Val = "abcdef12345"; String checksumMd5 = Checksum.OPTS.MD5.name(); - String checksumShaVal = "123456abcdef"; - String checksumSha = Checksum.OPTS.SHA256.name(); PackageStream.Checksum checksum = mock(PackageStream.Checksum.class); when(checksum.algorithm()).thenReturn(Checksum.OPTS.MD5); @@ -448,8 +440,7 @@ public void testCreateDublinCoreMetadata() throws Exception { DepositMetadata.Journal jMd = mock(DepositMetadata.Journal.class); String publisherName = "Big Publisher"; when(jMd.getPublisherName()).thenReturn(publisherName); - String publicationDate = "1/9/1919"; - when(jMd.getPublicationDate()).thenReturn(publicationDate); + when(jMd.getPublicationDate()).thenReturn(ZonedDateTime.now()); DepositMetadata md = mock(DepositMetadata.class); when(md.getArticleMetadata()).thenReturn(artMd); diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java index 3d7432408..29ea14301 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java @@ -51,7 +51,7 @@ public class MetadataIT extends AbstractDepositSubmissionIT { " },\n {\n \"author\": \"Mary Beth Lacey\"\n }\n ],\n \"agreements\": {\n " + "\"JScholarship\": \"Text removed.\"\n },\n \"title\": \"My Test Article\",\n \"journal-title\": " + "\"Nature Communications\",\n \"issns\": [\n {\n \"issn\": \"2041-1723\",\n \"pubType\": " + - "\"Print\"\n }\n ],\n \"publisher\": \"Elsevier\",\n \"publicationDate\": \"Fall 2016\",\n " + + "\"Print\"\n }\n ],\n \"publisher\": \"Elsevier\",\n \"publicationDate\": \"2001-03-05\",\n " + "\"abstract\": \"Abstract text\",\n \"journal-NLMTA-ID\": \"Nat Commun\",\n \"agent_information\": " + "{\n \"name\": \"Chrome\",\n \"version\": \"69\"\n }\n}"; @@ -61,7 +61,7 @@ public class MetadataIT extends AbstractDepositSubmissionIT { "\"JScholarship\": \"Text removed.\"\n },\n \"title\": \"My Test Article\",\n \"journal-title\": " + "\"Nature Communications\",\n \"issns\": [\n {\n \"issn\": \"2041-1723\",\n " + "\"pubType\": \"Print\"\n }\n ],\n \"publisher\": \"Elsevier\",\n \"publicationDate\": " + - "\"Fall 2016\",\n \"abstract\": \"Abstract text\",\n \"journal-NLMTA-ID\": \"Nat Commun\",\n " + + "\"2020-05-06\",\n \"abstract\": \"Abstract text\",\n \"journal-NLMTA-ID\": \"Nat Commun\",\n " + "\"agent_information\": {\n \"name\": \"Chrome\",\n \"version\": \"69\"\n }\n}"; @Autowired private SubmissionTestUtil submissionTestUtil; @@ -96,7 +96,7 @@ public void commonContributorsAndFewAuthors() throws Exception { // In citation, list up to three authors. Take publication date from "common" if not in "crossref". assertNotNull(qdc.getElementsByTagNameNS(DCTERMS_NS, DCT_BIBLIOCITATION).item(0).getTextContent()); - assertEquals("Christine Cagney, Mary Beth Lacey. (Fall 2016). \"My Test Article.\" Nature Communications.", + assertEquals("Christine Cagney, Mary Beth Lacey. (2001-03-05). \"My Test Article.\" Nature Communications.", qdc.getElementsByTagNameNS(DCTERMS_NS, DCT_BIBLIOCITATION).item(0).getTextContent()); } @@ -116,7 +116,7 @@ public void crossrefAndManyAuthors() throws Exception { // Publication date in "crossref" has precedence over one in "common". assertNotNull(qdc.getElementsByTagNameNS(DCTERMS_NS, DCT_BIBLIOCITATION).item(0).getTextContent()); assertEquals( - "Christine Cagney, Mary Beth Lacey, David Michael Starsky, et al. (Fall 2016). \"My Test Article.\" " + + "Christine Cagney, Mary Beth Lacey, David Michael Starsky, et al. (2020-05-06). \"My Test Article.\" " + "Nature Communications.", qdc.getElementsByTagNameNS(DCTERMS_NS, DCT_BIBLIOCITATION).item(0).getTextContent()); } diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/AbstractDepositIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/AbstractDepositIT.java index 78affd2c5..9b9db271c 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/AbstractDepositIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/AbstractDepositIT.java @@ -50,8 +50,6 @@ @TestPropertySource(properties = { "pass.deposit.repository.configuration=classpath:org/eclipse/pass/deposit/messaging/status/DepositTaskIT.json", - "dspace.host=localhost", - "dspace.port=9020", "dspace.user=test-dspace-user", "dspace.password=test-dspace-password", "dspace.server=localhost:9020", diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/SubmissionProcessorIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/SubmissionProcessorIT.java index 803c4954d..f56cffc24 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/SubmissionProcessorIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/service/SubmissionProcessorIT.java @@ -21,6 +21,8 @@ import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.patch; +import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.put; @@ -78,7 +80,11 @@ @TestPropertySource(properties = { "pass.deposit.repository.configuration=classpath:/full-test-repositories.json", "inveniordm.api.token=test-invenio-api-token", - "inveniordm.api.baseUrl=http://localhost:9030/api" + "inveniordm.api.baseUrl=http://localhost:9030/api", + "dspace.server=localhost:9030", + "dspace.api.url=http://localhost:9030/dspace/api", + "dspace.website.url=http://localhost:9030/dspace/website", + "dspace.collection.handle=collectionhandle" }) @WireMockTest(httpPort = 9030) public class SubmissionProcessorIT extends AbstractSubmissionIT { @@ -104,6 +110,7 @@ void testSubmissionProcessing_Full() throws Exception { ResourceTestUtil.readSubmissionJson("sample1-unsubmitted"))); resetGrantProjectName(submission, null); initInvenioApiStubs(); + initDSpaceApiStubs(); // WHEN/THEN testSubmissionProcessor(submission, false); @@ -111,6 +118,7 @@ void testSubmissionProcessing_Full() throws Exception { verify(invenioRdmTransport, times(1)).open(anyMap()); verify(devNullTransport, times(0)).open(anyMap()); verifyInvenioApiStubs(1); + verifyDSpaceApiStubs(1); } @Test @@ -120,6 +128,8 @@ void testSubmissionProcessing_Full_InvenioExistingRecord() throws Exception { ResourceTestUtil.readSubmissionJson("sample1-unsubmitted"))); resetGrantProjectName(submission, null); initInvenioApiStubs(); + initDSpaceApiStubs(); + String searchRecordsJsonResponse = "{ \"hits\": { \"hits\": [{ \"id\": \"existing-record-id\", " + "\"is_published\": \"false\"} ] } }"; stubFor(get("/api/user/records?q=metadata.title:%22Specific%20protein%20supplementation%20using%20" + @@ -136,6 +146,8 @@ void testSubmissionProcessing_Full_InvenioExistingRecord() throws Exception { verify(invenioRdmTransport, times(1)).open(anyMap()); verify(devNullTransport, times(0)).open(anyMap()); verifyInvenioApiStubs(1); + verifyDSpaceApiStubs(1); + WireMock.verify(1, deleteRequestedFor( urlEqualTo("/api/records/existing-record-id/draft"))); } @@ -149,10 +161,11 @@ void testSubmissionProcessing_SkipTestSubmission() throws Exception { // WHEN/THEN testSubmissionProcessor(submission, true); - verify(devNullTransport, times(4)).open(anyMap()); + verify(devNullTransport, times(5)).open(anyMap()); verify(filesystemTransport, times(0)).open(anyMap()); verify(invenioRdmTransport, times(0)).open(anyMap()); verifyInvenioApiStubs(0); + verifyDSpaceApiStubs(0); } @Test @@ -162,6 +175,7 @@ void testSubmissionProcessing_DontSkipTestSubmission() throws Exception { ResourceTestUtil.readSubmissionJson("sample1-unsubmitted"))); resetGrantProjectName(submission, DeploymentTestDataService.PASS_E2E_TEST_GRANT); initInvenioApiStubs(); + initDSpaceApiStubs(); ReflectionTestUtils.setField(depositTaskHelper, "skipDeploymentTestDeposits", Boolean.FALSE); // WHEN/THEN @@ -170,6 +184,7 @@ void testSubmissionProcessing_DontSkipTestSubmission() throws Exception { verify(filesystemTransport, times(3)).open(anyMap()); verify(invenioRdmTransport, times(1)).open(anyMap()); verifyInvenioApiStubs(1); + verifyDSpaceApiStubs(1); } private void testSubmissionProcessor(Submission submission, boolean usingDevNull) throws IOException { @@ -236,7 +251,7 @@ private void testSubmissionProcessor(Submission submission, boolean usingDevNull List repoKeys = resultDeposits.stream() .map(deposit -> deposit.getRepository().getRepositoryKey()) .toList(); - List expectedRepoKey = List.of("PubMed Central", "JScholarship", "BagIt", "InvenioRDM"); + List expectedRepoKey = List.of("PubMed Central", "JScholarship", "BagIt", "InvenioRDM", "DSpace"); assertTrue(repoKeys.size() == expectedRepoKey.size() && repoKeys.containsAll(expectedRepoKey) && expectedRepoKey.containsAll(repoKeys)); Deposit pmcDeposit = resultDeposits.stream() @@ -388,4 +403,49 @@ private void verifyInvenioApiStubs(int expectedCount) throws IOException, URISyn urlEqualTo("/api/records/test-record-id/draft/actions/publish"))); } + private void initDSpaceApiStubs() throws IOException { + stubFor(get("/dspace/api/security/csrf").willReturn(WireMock.notFound(). + withHeader("DSPACE-XSRF-TOKEN", "csrftoken"))); + stubFor(post("/dspace/api/authn/login").willReturn(WireMock.ok().withHeader("Authorization", "authtoken"))); + + String searchJson = "{\n" + + " \"_embedded\": {\n" + + " \"searchResult\": {\n" + + " \"_embedded\": {\n" + + " \"objects\": [\n" + + " {\n" + + " \"_embedded\": {\n" + + " \"indexableObject\": {\n" + + " \"handle\": \"collectionhandle\",\n" + + " \"uuid\": \"collectionuuid\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"; + stubFor(get("/dspace/api/discover/search/objects?query=handle:collectionhandle") + .willReturn(ok(searchJson))); + + stubFor(post("/dspace/api/submission/workspaceitems?owningCollection=collectionuuid") + .willReturn(WireMock.ok("{\"_embedded\": {\"workspaceitems\": [{\"id\": 1," + + "\"_embedded\": {\"item\": {\"uuid\": \"uuid\", \"metadata\": {}}}}]}}"))); + + stubFor(patch("/dspace/api/submission/workspaceitems/1").willReturn(WireMock.ok())); + + stubFor(post("/dspace/api/workflow/workflowitems").willReturn(WireMock.ok())); + } + + private void verifyDSpaceApiStubs(int expectedCount) throws IOException { + WireMock.verify(expectedCount, getRequestedFor(urlEqualTo("/dspace/api/security/csrf"))); + WireMock.verify(expectedCount, postRequestedFor(urlEqualTo("/dspace/api/authn/login"))); + WireMock.verify(expectedCount, getRequestedFor( + urlEqualTo("/dspace/api/discover/search/objects?query=handle:collectionhandle"))); + WireMock.verify(expectedCount, postRequestedFor( + urlEqualTo("/dspace/api/submission/workspaceitems?owningCollection=collectionuuid"))); + WireMock.verify(expectedCount, patchRequestedFor(urlEqualTo("/dspace/api/submission/workspaceitems/1"))); + WireMock.verify(expectedCount, postRequestedFor(urlEqualTo("/dspace/api/workflow/workflowitems"))); + } } diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataServiceIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataServiceIT.java index b300b210c..ef8f4c039 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataServiceIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/deploymenttest/DeploymentTestDataServiceIT.java @@ -44,6 +44,7 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import org.eclipse.pass.deposit.service.AbstractDepositIT; +import org.eclipse.pass.deposit.support.dspace.DSpaceDepositService; import org.eclipse.pass.support.client.PassClientSelector; import org.eclipse.pass.support.client.RSQL; import org.eclipse.pass.support.client.model.AwardStatus; @@ -73,8 +74,9 @@ * @author Russ Poetker (rpoetke1@jh.edu) */ @TestPropertySource(properties = { - "dspace.server.api.protocol=http", - "dspace.server.api.path=/server/api", + "dspace.server=localhost:9020", + "dspace.api.url=http://localhost:9020/server/api", + "dspace.website.url=http://localhost:9020/website", "pass.test.data.job.enabled=true", "pass.test.data.policy.title=test-policy-title", "pass.test.data.user.email=test-user-email@foo", @@ -84,7 +86,7 @@ class DeploymentTestDataServiceIT extends AbstractDepositIT { @Autowired private DeploymentTestDataService deploymentTestDataService; - @SpyBean private DspaceDepositService dspaceDepositService; + @SpyBean private DSpaceDepositService dspaceDepositService; @BeforeEach public void initPolicyAndUser() throws IOException { @@ -207,10 +209,10 @@ void testProcessTestData_DeleteTestSubmissionsForTestGrant() throws Exception { verifyTestGrantDeleted(); verify(dspaceDepositService, times(1)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestDspace")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verify(dspaceDepositService, times(0)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestNihms")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verifyDspaceApiStubs(1, 1); } @@ -232,10 +234,10 @@ void testProcessTestData_DeleteTestSubmissionsWithNullRepoCopy() throws Exceptio verifyTestGrantDeleted(); verify(dspaceDepositService, times(1)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestDspace")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verify(dspaceDepositService, times(0)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestNihms")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verifyDspaceApiStubs(0, 1); } @@ -308,10 +310,10 @@ void testProcessTestData_DoesNotDeleteTestSubmissionsForOtherGrant() throws Exce verify(dspaceDepositService, times(1)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestDspace")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verify(dspaceDepositService, times(0)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestNihms")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verifyDspaceApiStubs(1, 1); } @@ -332,10 +334,10 @@ void testProcessTestData_DoesNotDeleteDspaceDepositIfSkip() throws Exception { verifyTestGrantDeleted(); verify(dspaceDepositService, times(0)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestDspace")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verify(dspaceDepositService, times(0)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestNihms")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verifyDspaceApiStubs(0, 0); } @@ -357,10 +359,10 @@ void testProcessTestData_DoesNotDeleteDspaceDepositIfNameMismatch() throws Excep verifyTestGrantDeleted(); verify(dspaceDepositService, times(1)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestDspace")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); verify(dspaceDepositService, times(0)).deleteDeposit( argThat(deposit -> deposit.getRepository().getRepositoryKey().equals("TestNihms")), - any(DspaceDepositService.AuthContext.class)); + any(DSpaceDepositService.AuthContext.class)); WireMock.verify(1, getRequestedFor(urlEqualTo("/server/api/security/csrf"))); WireMock.verify(1, postRequestedFor(urlEqualTo("/server/api/authn/login"))); WireMock.verify(1, getRequestedFor( diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/jobs/ScheduledJobsTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/jobs/ScheduledJobsTest.java index 235e03214..b13ded9b9 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/jobs/ScheduledJobsTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/support/jobs/ScheduledJobsTest.java @@ -45,9 +45,9 @@ "pass.deposit.jobs.3.init.delay=120", "dspace.user=test-dspace-user", "dspace.password=test-dspace-password", + "dspace.port=9020", "dspace.server=localhost:9020", - "dspace.server.api.protocol=http", - "dspace.server.api.path=/server/api", + "dspace.server.api.path=http://localhost/server/api", }) @DirtiesContext public class ScheduledJobsTest { diff --git a/pass-deposit-services/deposit-core/src/test/resources/full-test-repositories.json b/pass-deposit-services/deposit-core/src/test/resources/full-test-repositories.json index 19107b5f8..401ccd187 100644 --- a/pass-deposit-services/deposit-core/src/test/resources/full-test-repositories.json +++ b/pass-deposit-services/deposit-core/src/test/resources/full-test-repositories.json @@ -145,5 +145,16 @@ "protocol": "invenioRdm" } } + }, + "DSpace": { + "assembler": { + "specification": "DSpace", + "beanName": "DSpaceAssembler" + }, + "transport-config": { + "protocol-binding": { + "protocol": "DSpace" + } + } } -} \ No newline at end of file +} diff --git a/pass-deposit-services/deposit-core/src/test/resources/org/eclipse/pass/deposit/messaging/status/DepositTaskIT.json b/pass-deposit-services/deposit-core/src/test/resources/org/eclipse/pass/deposit/messaging/status/DepositTaskIT.json index 4fef904e9..fdcf51a29 100644 --- a/pass-deposit-services/deposit-core/src/test/resources/org/eclipse/pass/deposit/messaging/status/DepositTaskIT.json +++ b/pass-deposit-services/deposit-core/src/test/resources/org/eclipse/pass/deposit/messaging/status/DepositTaskIT.json @@ -27,8 +27,8 @@ "protocol": "SWORDv2", "username": "${dspace.user}", "password": "${dspace.password}", - "server-fqdn": "${dspace.host}", - "server-port": "${dspace.port}", + "server-fqdn": null, + "server-port": null, "service-doc": "${dspace.baseuri}/swordv2/servicedocument", "default-collection": "${dspace.baseuri}/swordv2/collection/${dspace.collection.handle}", "on-behalf-of": null, @@ -37,4 +37,4 @@ } } } -} \ No newline at end of file +} diff --git a/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1-unsubmitted.json b/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1-unsubmitted.json index 192e2f2e4..85de620c6 100644 --- a/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1-unsubmitted.json +++ b/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1-unsubmitted.json @@ -95,6 +95,13 @@ "id": "7", "@type": "Repository" }, + { + "name": "DSpace Repo", + "repositoryKey": "DSpace", + "description": "DSpace Repository", + "id": "8", + "@type": "Repository" + }, { "journalName": "AAPS PharmSci", "issns": [ @@ -104,7 +111,7 @@ "nlmta": "AAPS PharmSci", "pmcParticipation": "A", "publisher": null, - "id": "8", + "id": "9", "@type": "Journal" }, { @@ -112,10 +119,10 @@ "abstract": "This is a great paper!", "doi": "abcdef", "pmid": "fedcba", - "journal": "8", + "journal": "9", "volume": "123", "issue": "May 2015", - "id": "9", + "id": "10", "@type": "Publication" }, { @@ -127,23 +134,23 @@ { "id": "7" } ], "institution": "fake:institution1", - "id": "10", + "id": "11", "@type": "Policy" }, { "name": "National Eye Institute", "url": "http://example.com/eyeguys", "localKey": "aabbcc", - "policy": "10", - "id": "11", + "policy": "11", + "id": "12", "@type": "Funder" }, { "name": "International Eye Institute", "url": "http://example.com/othereyeguys", "localKey": "ddeeff", - "policy": "10", - "id": "12", + "policy": "11", + "id": "13", "@type": "Funder" }, { @@ -154,14 +161,14 @@ "awardDate": "2017-06-01T00:00:00.000Z", "startDate": "2017-05-01T00:00:00.000Z", "endDate": "2018-06-01T00:00:00.000Z", - "primaryFunder": "11", - "directFunder": "12", + "primaryFunder": "12", + "directFunder": "13", "pi": "2", "coPis": [ "3", "1" ], - "id": "13", + "id": "14", "@type": "Grant" }, { @@ -171,18 +178,19 @@ "submittedDate": "2017-06-02T00:00:00.000Z", "aggregatedDepositStatus": "NOT_STARTED", "submissionStatus": "APPROVAL_REQUESTED", - "publication": "9", + "publication": "10", "repositories": [ "4", "5", "6", - "7" + "7", + "8" ], "submitter": "2", "grants": [ - "13" + "14" ], - "id": "14", + "id": "15", "@type": "Submission" }, { @@ -191,8 +199,8 @@ "description": "Custodial content", "fileRole": "SUPPLEMENTAL", "mimeType": "image/jpg", - "submission": "14", - "id": "15", + "submission": "15", + "id": "16", "@type": "File" }, { @@ -201,8 +209,8 @@ "description": "Custodial content", "fileRole": "FIGURE", "mimeType": "image/tiff", - "submission": "14", - "id": "16", + "submission": "15", + "id": "17", "@type": "File" }, { @@ -211,8 +219,8 @@ "description": "Custodial content", "fileRole": "FIGURE", "mimeType": "image/png", - "submission": "14", - "id": "17", + "submission": "15", + "id": "18", "@type": "File" }, { @@ -221,8 +229,8 @@ "description": "Custodial content", "fileRole": "MANUSCRIPT", "mimeType": "application/msword", - "submission": "14", - "id": "18", + "submission": "15", + "id": "19", "@type": "File" }, { @@ -231,8 +239,8 @@ "description": "Custodial content", "fileRole": "TABLE", "mimeType": "application/vnd.oasis.opendocument.spreadsheet", - "submission": "14", - "id": "19", + "submission": "15", + "id": "20", "@type": "File" }, { @@ -241,8 +249,8 @@ "description": "Custodial content, meant to conflict with NIH package spec", "fileRole": "SUPPLEMENTAL", "mimeType": "application/xml", - "submission": "14", - "id": "20", + "submission": "15", + "id": "21", "@type": "File" }, { @@ -251,8 +259,8 @@ "description": "Custodial content, meant to conflict with NIH package spec", "fileRole": "SUPPLEMENTAL", "mimeType": "text/plain", - "submission": "14", - "id": "21", + "submission": "15", + "id": "22", "@type": "File" }, { @@ -261,8 +269,8 @@ "description": "Custodial content with a space in the filename", "fileRole": "SUPPLEMENTAL", "mimeType": "text/plain", - "submission": "14", - "id": "22", + "submission": "15", + "id": "23", "@type": "File" } ] diff --git a/pass-deposit-services/deposit-core/src/test/resources/test-application.properties b/pass-deposit-services/deposit-core/src/test/resources/test-application.properties index fcffe7631..fd0a40b38 100644 --- a/pass-deposit-services/deposit-core/src/test/resources/test-application.properties +++ b/pass-deposit-services/deposit-core/src/test/resources/test-application.properties @@ -20,3 +20,12 @@ spring.cloud.aws.s3.enabled=false pass.client.url=http://localhost:8080/ pass.client.user=test pass.client.password=test + +dspace.server=localhost:8000 +dspace.user=user +dspace.password=test +dspace.api.url=http://localhost:8000/api +dspace.website.url=http://localhost:8000/website +dspace.collection.handle=1234/1 +dspace.field.embargo.lift=local.embargo.lift +dspace.field.embargo.terms=local.embargo.terms diff --git a/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java b/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java index f95ff630a..c0761b4e0 100644 --- a/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java +++ b/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java @@ -237,7 +237,7 @@ public static class Journal { /** * Date of publication */ - public String publicationDate; + public ZonedDateTime publicationDate; /** * ISSN mapped to journal publication type @@ -302,14 +302,14 @@ public void setPublisherName(String publisherName) { /** * @return {@link #publicationDate} */ - public String getPublicationDate() { + public ZonedDateTime getPublicationDate() { return publicationDate; } /** * @param publicationDate {@link #publicationDate} */ - public void setPublicationDate(String publicationDate) { + public void setPublicationDate(ZonedDateTime publicationDate) { this.publicationDate = publicationDate; } diff --git a/pass-deposit-services/pom.xml b/pass-deposit-services/pom.xml index 1617e52c9..dec824fd8 100644 --- a/pass-deposit-services/pom.xml +++ b/pass-deposit-services/pom.xml @@ -93,6 +93,12 @@ spring-boot-starter-test ${spring-boot-maven-plugin.version} test + + + com.vaadin.external.google + android-json + +