From ed2fe931a6f86f620b38e8701a1a04e019910769 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 16 Sep 2026 10:47:06 -0400 Subject: [PATCH 1/4] give files a resourceTypeGeneral of "Other" #12476 --- doc/release-notes/12476-files-as-other.md | 1 + .../pidproviders/doi/XmlMetadataTemplate.java | 8 +++ .../doi/datacite/XmlMetadataTemplateTest.java | 55 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 doc/release-notes/12476-files-as-other.md diff --git a/doc/release-notes/12476-files-as-other.md b/doc/release-notes/12476-files-as-other.md new file mode 100644 index 00000000000..c730b9beb85 --- /dev/null +++ b/doc/release-notes/12476-files-as-other.md @@ -0,0 +1 @@ +When files are given Persistent Identifiers (PIDs) such as a DOI, the DataCite export resourceTypeGeneral is now "Other" instead of "Dataset". Dataverse uses "Dataset" to mean a container for files. For files, "Other" is not ideal, but it's a step as we work with DataCite in https://github.com/datacite/datacite-suggestions/discussions/214 on defining an appropriate value for dataset files. See #12476. diff --git a/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/XmlMetadataTemplate.java b/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/XmlMetadataTemplate.java index 954ddc46f36..f323bececfb 100644 --- a/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/XmlMetadataTemplate.java +++ b/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/XmlMetadataTemplate.java @@ -885,6 +885,14 @@ private void writeResourceType(XMLStreamWriter xmlw, DvObject dvObject) throws X case DatasetType.DATASET_TYPE_REVIEW -> "Other"; default -> "Dataset"; }; + } else if (dvObject instanceof DataFile) { + // For now, we are giving data files a resourceTypeGeneral of "Other" + // but longer term, we are working with DataCite on a more appropriate + // value. See the following: + // - https://github.com/IQSS/dataverse/issues/12476 + // - https://github.com/IQSS/dataverse/issues/5086 + // - https://github.com/datacite/datacite-suggestions/discussions/214 + resourceType = "Other"; } attributes.put("resourceTypeGeneral", resourceType); if (dvObject instanceof Dataset d) { diff --git a/src/test/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/XmlMetadataTemplateTest.java b/src/test/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/XmlMetadataTemplateTest.java index 19da198399a..0f2c8fbdd18 100644 --- a/src/test/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/XmlMetadataTemplateTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/XmlMetadataTemplateTest.java @@ -2,6 +2,8 @@ import edu.harvard.iq.dataverse.ControlledVocabularyValue; import edu.harvard.iq.dataverse.DataCitation; +import edu.harvard.iq.dataverse.DataFile; +import edu.harvard.iq.dataverse.FileMetadata; import edu.harvard.iq.dataverse.Dataset; import edu.harvard.iq.dataverse.DatasetAuthor; import edu.harvard.iq.dataverse.DatasetField; @@ -53,6 +55,9 @@ import javax.xml.transform.stream.StreamSource; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; @@ -90,6 +95,56 @@ public static void setupMocks() { } + @ParameterizedTest + @EnumSource(value = VersionState.class, names = {"DRAFT", "RELEASED", "DEACCESSIONED"}) + public void testFileResourceTypeGeneral(VersionState state) { + Dataset dataset = resourceTypeTestDataset(DatasetType.DATASET_TYPE_DATASET); + DatasetVersion version = dataset.getLatestVersionForCopy(); + version.setVersionState(state); + version.setVersionNumber(1L); + version.setMinorVersionNumber(0L); + version.setReleaseTime(java.sql.Timestamp.valueOf("2026-01-01 00:00:00")); + DataFile file = new DataFile(); + file.setOwner(dataset); + file.setGlobalId(new GlobalId("doi", "10.5072", "FK2/ABCDEF/FILE", null, null, null)); + file.setFilesize(10L); + FileMetadata metadata = new FileMetadata(); + metadata.setDataFile(file); + metadata.setDatasetVersion(version); + file.setFileMetadatas(List.of(metadata)); + + String xml = new XmlMetadataTemplate(new DoiMetadata()).generateXML(file); + + assertEquals("Other", XmlPath.from(xml).getString("resource.resourceType.@resourceTypeGeneral")); + assertEquals("", XmlPath.from(xml).getString("resource.resourceType")); + } + + @ParameterizedTest + @CsvSource({"dataset, Dataset", "software, Software", "workflow, Workflow", "review, Other", "custom, Dataset"}) + public void testDatasetResourceTypeGeneral(String datasetType, String expectedResourceType) { + Dataset dataset = resourceTypeTestDataset(datasetType); + + String xml = new XmlMetadataTemplate(new DoiMetadata()).generateXML(dataset); + + assertEquals(expectedResourceType, XmlPath.from(xml).getString("resource.resourceType.@resourceTypeGeneral")); + assertEquals("review".equals(datasetType) ? "Review" : "", XmlPath.from(xml).getString("resource.resourceType")); + } + + private Dataset resourceTypeTestDataset(String typeName) { + Dataset dataset = new Dataset(); + dataset.setGlobalId(new GlobalId("doi", "10.5072", "FK2/ABCDEF", null, null, null)); + DatasetType type = new DatasetType(); + type.setName(typeName); + dataset.setDatasetType(type); + DatasetVersion version = new DatasetVersion(); + version.setDataset(dataset); + version.setVersionState(VersionState.DRAFT); + version.setDatasetFields(new ArrayList<>()); + version.setTermsOfUseAndAccess(new TermsOfUseAndAccess()); + dataset.setVersions(new ArrayList<>(List.of(version))); + return dataset; + } + /** * A minimal example to assure that the XMLMetadataTemplate generates output * consistent with the DataCite XML v4.5 schema. From c9e90fc964972ca39f84ac68c15ee15f0ce4fd7c Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 22 Sep 2026 15:05:01 -0400 Subject: [PATCH 2/4] add docs #12476 --- doc/sphinx-guides/source/installation/config.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/sphinx-guides/source/installation/config.rst b/doc/sphinx-guides/source/installation/config.rst index 360a96e8eb5..a9936794b7d 100644 --- a/doc/sphinx-guides/source/installation/config.rst +++ b/doc/sphinx-guides/source/installation/config.rst @@ -4573,6 +4573,8 @@ If you don't want to register file-based PIDs for your entire installation:: ``curl -X PUT -d 'false' http://localhost:8080/api/admin/settings/:FilePIDsEnabled`` +If you are using DataCite as your PID provider, resourceTypeGeneral will be "Other". See discussion at https://github.com/IQSS/dataverse/issues/5086. + .. _:AllowEnablingFilePIDsPerCollection: :AllowEnablingFilePIDsPerCollection From 27d35a2ec319ef6e7eb409663f87360372a5e6b8 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 22 Sep 2026 15:16:29 -0400 Subject: [PATCH 3/4] improve release note, add an upgrade step #12476 --- doc/release-notes/12476-files-as-other.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/release-notes/12476-files-as-other.md b/doc/release-notes/12476-files-as-other.md index c730b9beb85..a34a7f4e86a 100644 --- a/doc/release-notes/12476-files-as-other.md +++ b/doc/release-notes/12476-files-as-other.md @@ -1 +1,11 @@ -When files are given Persistent Identifiers (PIDs) such as a DOI, the DataCite export resourceTypeGeneral is now "Other" instead of "Dataset". Dataverse uses "Dataset" to mean a container for files. For files, "Other" is not ideal, but it's a step as we work with DataCite in https://github.com/datacite/datacite-suggestions/discussions/214 on defining an appropriate value for dataset files. See #12476. +When files are given Persistent Identifiers (PIDs) such as a DOI, the DataCite export resourceTypeGeneral is now "Other" instead of "Dataset". Dataverse uses "Dataset" to mean a container for files. For files, "Other" is not ideal, but it's a step as we work with DataCite in https://github.com/datacite/datacite-suggestions/discussions/214 to define an appropriate value for dataset files. See [the guides](https://guides.dataverse.org/en/6.13/installation/config.html#filepidsenabled), #5086, #12476, and #12713. + +## Upgrade Instructions + +1. Optionally, update metadata for files with DataCite DOIs + + If you are using DataCite as a Persistent ID provider and have files that have DOIs, you may want to update their metadata records to pick up the change from "Dataset" to "Other" for resourceTypeGeneral (see #12713). + + We recommend experimenting with a single dataset first (see [docs](https://guides.dataverse.org/en/6.13/admin/dataverses-datasets.html#update-metadata-for-a-published-dataset-at-the-pid-provider)). Depending on how many DOIs need to be changed, you might want to iterate though datasets one by one but an API endpoint is available to process all datasets at once (see [docs](https://guides.dataverse.org/en/6.13/admin/dataverses-datasets.html#update-metadata-for-all-published-datasets-at-the-pid-provider)). + + \ No newline at end of file From e38241ff10fde60cbe9b59c83a5a53238d7fc6b3 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 22 Sep 2026 15:45:23 -0400 Subject: [PATCH 4/4] add ability to log XML sent to DataCite #12476 --- .../pidproviders/doi/datacite/DataCiteRESTfullClient.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/DataCiteRESTfullClient.java b/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/DataCiteRESTfullClient.java index c0b7661022c..0f8de9d612f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/DataCiteRESTfullClient.java +++ b/src/main/java/edu/harvard/iq/dataverse/pidproviders/doi/datacite/DataCiteRESTfullClient.java @@ -298,6 +298,7 @@ public String postMetadata(String metadata) throws IOException { HttpPost httpPost = new HttpPost(this.url + "/metadata"); httpPost.setHeader("Content-Type", "application/xml;charset=UTF-8"); httpPost.setEntity(new StringEntity(metadata, "utf-8")); + logger.log(Level.FINE, "XML to send to DataCite:\n{0}", metadata); HttpResponse response = executeWithRetry(httpPost, "postMetadata"); String data = EntityUtils.toString(response.getEntity(), encoding); if (response.getStatusLine().getStatusCode() != 201) {