diff --git a/doc/release-notes/expose-export-formats.md b/doc/release-notes/expose-export-formats.md new file mode 100644 index 00000000000..a21906d7bbb --- /dev/null +++ b/doc/release-notes/expose-export-formats.md @@ -0,0 +1,2 @@ +# New API method for listing the available exporters +Found at `/api/info/exportFormats`, produces an object with available format names as keys, and as values an object with various info about the exporter. See also #10739. \ No newline at end of file diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 691aa94c834..1e5d84c1b27 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -4858,6 +4858,35 @@ The fully expanded example above (without environment variables) looks like this curl "https://demo.dataverse.org/api/info/settings/:MaxEmbargoDurationInMonths" +Get Export Formats +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Get the available export formats, including custom formats. + +The response contains an object with available format names as keys, and as values an object with the following properties: + +* ``displayName`` +* ``mediaType`` +* ``isHarvestable`` +* ``isVisibleInUserInterface`` (corresponds to isAvailableToUsers) +* ``XMLNameSpace`` (only for XML exporters) +* ``XMLSchemaLocation`` (only for XML exporters) +* ``XMLSchemaVersion`` (only for XML exporters) + +.. note:: See :ref:`curl-examples-and-environment-variables` if you are unfamiliar with the use of export below. + +.. code-block:: bash + + export SERVER_URL=https://demo.dataverse.org + + curl "$SERVER_URL/api/info/exportFormats" + +The fully expanded example above (without environment variables) looks like this: + +.. code-block:: bash + + curl "https://demo.dataverse.org/api/info/exportFormats" + .. _metadata-blocks-api: Metadata Blocks diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Info.java b/src/main/java/edu/harvard/iq/dataverse/api/Info.java index 257519677d3..2439c996816 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Info.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Info.java @@ -12,12 +12,17 @@ import jakarta.ws.rs.Produces; import org.apache.commons.io.IOUtils; +import edu.harvard.iq.dataverse.export.ExportService; import edu.harvard.iq.dataverse.settings.JvmSettings; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; import edu.harvard.iq.dataverse.util.BundleUtil; import edu.harvard.iq.dataverse.util.SystemConfig; +import io.gdcc.spi.export.Exporter; +import io.gdcc.spi.export.ExportException; +import io.gdcc.spi.export.XMLExporter; import jakarta.ejb.EJB; import jakarta.json.Json; +import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonValue; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -92,6 +97,32 @@ public Response getZipDownloadLimit() { return ok(zipDownloadLimit); } + @GET + @Path("exportFormats") + public Response getExportFormats() { + JsonObjectBuilder responseModel = Json.createObjectBuilder(); + ExportService instance = ExportService.getInstance(); + for (String[] labels : instance.getExportersLabels()) { + try { + Exporter exporter = instance.getExporter(labels[1]); + JsonObjectBuilder exporterObject = Json.createObjectBuilder().add("displayName", labels[0]) + .add("mediaType", exporter.getMediaType()).add("isHarvestable", exporter.isHarvestable()) + .add("isVisibleInUserInterface", exporter.isAvailableToUsers()); + if (exporter instanceof XMLExporter xmlExporter) { + exporterObject.add("XMLNameSpace", xmlExporter.getXMLNameSpace()) + .add("XMLSchemaLocation", xmlExporter.getXMLSchemaLocation()) + .add("XMLSchemaVersion", xmlExporter.getXMLSchemaVersion()); + } + responseModel.add(labels[1], exporterObject); + } + catch (ExportException ex){ + logger.warning("Failed to get: " + labels[1]); + logger.warning(ex.getLocalizedMessage()); + } + } + return ok(responseModel); + } + private Response getSettingResponseByKey(SettingsServiceBean.Key key) { String setting = settingsService.getValueForKey(key); if (setting != null) { diff --git a/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java b/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java index 5e436dd0e98..b198d2769a0 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java @@ -6,11 +6,14 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static jakarta.ws.rs.core.Response.Status.OK; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; +import org.skyscreamer.jsonassert.JSONAssert; public class InfoIT { @@ -81,6 +84,22 @@ public void testGetZipDownloadLimit() { .body("data", notNullValue()); } + @Test + public void testGetExportFormats() throws IOException { + Response response = given().urlEncodingEnabled(false) + .get("/api/info/exportFormats"); + response.prettyPrint(); + response.then().assertThat().statusCode(OK.getStatusCode()); + + String actual = response.getBody().asString(); + String expected = + java.nio.file.Files.readString( + Paths.get("src/test/resources/json/export-formats.json"), + StandardCharsets.UTF_8); + JSONAssert.assertEquals(expected, actual, true); + + } + private void testSettingEndpoint(SettingsServiceBean.Key settingKey, String testSettingValue) { String endpoint = "/api/info/settings/" + settingKey; diff --git a/src/test/resources/json/export-formats.json b/src/test/resources/json/export-formats.json new file mode 100644 index 00000000000..b4dc0168629 --- /dev/null +++ b/src/test/resources/json/export-formats.json @@ -0,0 +1,83 @@ +{ + "status": "OK", + "data": { + "OAI_ORE": { + "displayName": "OAI_ORE", + "mediaType": "application/json", + "isHarvestable": false, + "isVisibleInUserInterface": true + }, + "Datacite": { + "displayName": "DataCite", + "mediaType": "application/xml", + "isHarvestable": true, + "isVisibleInUserInterface": true, + "XMLNameSpace": "http://datacite.org/schema/kernel-4", + "XMLSchemaLocation": "http://datacite.org/schema/kernel-4 http://schema.datacite.org/meta/kernel-4.5/metadata.xsd", + "XMLSchemaVersion": "4.5" + }, + "oai_dc": { + "displayName": "Dublin Core", + "mediaType": "application/xml", + "isHarvestable": true, + "isVisibleInUserInterface": false, + "XMLNameSpace": "http://www.openarchives.org/OAI/2.0/oai_dc/", + "XMLSchemaLocation": "http://www.openarchives.org/OAI/2.0/oai_dc.xsd", + "XMLSchemaVersion": "2.0" + }, + "oai_datacite": { + "displayName": "OpenAIRE", + "mediaType": "application/xml", + "isHarvestable": true, + "isVisibleInUserInterface": true, + "XMLNameSpace": "http://datacite.org/schema/kernel-4", + "XMLSchemaLocation": "http://schema.datacite.org/meta/kernel-4.1/metadata.xsd", + "XMLSchemaVersion": "4.1" + }, + "schema.org": { + "displayName": "Schema.org JSON-LD", + "mediaType": "application/json", + "isHarvestable": false, + "isVisibleInUserInterface": true + }, + "ddi": { + "displayName": "DDI Codebook v2", + "mediaType": "application/xml", + "isHarvestable": false, + "isVisibleInUserInterface": true, + "XMLNameSpace": "ddi:codebook:2_5", + "XMLSchemaLocation": "https://ddialliance.org/Specification/DDI-Codebook/2.5/XMLSchema/codebook.xsd", + "XMLSchemaVersion": "2.5" + }, + "dcterms": { + "displayName": "Dublin Core", + "mediaType": "application/xml", + "isHarvestable": false, + "isVisibleInUserInterface": true, + "XMLNameSpace": "http://purl.org/dc/terms/", + "XMLSchemaLocation": "http://dublincore.org/schemas/xmls/qdc/dcterms.xsd", + "XMLSchemaVersion": "2.0" + }, + "html": { + "displayName": "DDI HTML Codebook", + "mediaType": "text/html", + "isHarvestable": false, + "isVisibleInUserInterface": true + }, + "dataverse_json": { + "displayName": "JSON", + "mediaType": "application/json", + "isHarvestable": true, + "isVisibleInUserInterface": true + }, + "oai_ddi": { + "displayName": "DDI Codebook v2", + "mediaType": "application/xml", + "isHarvestable": true, + "isVisibleInUserInterface": false, + "XMLNameSpace": "ddi:codebook:2_5", + "XMLSchemaLocation": "https://ddialliance.org/Specification/DDI-Codebook/2.5/XMLSchema/codebook.xsd", + "XMLSchemaVersion": "2.5" + } + } +}