Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getZipDownloadLimit and getMaxEmbargoDurationInMonths API info endpoints #9881

Merged
merged 11 commits into from
Sep 13, 2023
5 changes: 5 additions & 0 deletions doc/release-notes/9880-info-api-zip-limit-embargo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Implemented the following new endpoints:

- getZipDownloadLimit (/api/info/zipDownloadLimit): Get the configured zip file download limit. The response contains the long value of the limit in bytes.

- getMaxEmbargoDurationInMonths (/api/info/settings/:MaxEmbargoDurationInMonths): Get the maximum embargo duration in months, if available, configured through the database setting :MaxEmbargoDurationInMonths.
41 changes: 41 additions & 0 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3318,6 +3318,8 @@ Show Support Of Incomplete Metadata Deposition
Learn if an instance has been configured to allow deposition of incomplete datasets via the API.
See also :ref:`create-dataset-command` and :ref:`dataverse.api.allow-incomplete-metadata`

.. 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
Expand All @@ -3330,6 +3332,45 @@ The fully expanded example above (without environment variables) looks like this

curl "https://demo.dataverse.org/api/info/settings/incompleteMetadataViaApi"

Get Zip File Download Limit
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Get the configured zip file download limit. The response contains the long value of the limit in bytes.

This limit comes from the database setting :ref:`:ZipDownloadLimit` if set, or the default value if the database setting is not set, which is 104857600 (100MB).

.. 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/zipDownloadLimit"

The fully expanded example above (without environment variables) looks like this:

.. code-block:: bash

curl "https://demo.dataverse.org/api/info/zipDownloadLimit"

Get Maximum Embargo Duration In Months
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Get the maximum embargo duration in months, if available, configured through the database setting :ref:`:MaxEmbargoDurationInMonths` from the Configuration section of the Installation Guide.

.. 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/settings/:MaxEmbargoDurationInMonths"

The fully expanded example above (without environment variables) looks like this:

.. code-block:: bash

curl "https://demo.dataverse.org/api/info/settings/:MaxEmbargoDurationInMonths"

.. _metadata-blocks-api:

Expand Down
63 changes: 63 additions & 0 deletions doc/sphinx-guides/source/developers/api-design.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
==========
API Design
==========

API design is a large topic. We expect this page to grow over time.

.. contents:: |toctitle|
:local:

Paths
-----

A reminder `from Wikipedia <https://en.wikipedia.org/wiki/Uniform_Resource_Identifier>`_ of what a path is:

.. code-block:: bash

userinfo host port
┌──┴───┐ ┌──────┴──────┐ ┌┴┐
https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top
└─┬─┘ └─────────────┬────────────┘└───────┬───────┘ └────────────┬────────────┘ └┬┘
scheme authority path query fragment

Exposing Settings
~~~~~~~~~~~~~~~~~

Since Dataverse 4, database settings have been exposed via API at http://localhost:8080/api/admin/settings

(JVM options are probably available via the Payara REST API, but this is out of scope.)

Settings need to be exposed outside to API clients outside of ``/api/admin`` (which is typically restricted to localhost). Here are some guidelines to follow when exposing settings.

- When you are exposing a database setting as-is:

- Use ``/api/info/settings`` as the root path.

- Append the name of the setting including the colon (e.g. ``:DatasetPublishPopupCustomText``)

- Final path example: ``/api/info/settings/:DatasetPublishPopupCustomText``

- If the absence of the database setting is filled in by a default value (e.g. ``:ZipDownloadLimit`` or ``:ApiTermsOfUse``):

- Use ``/api/info`` as the root path.

- Append the setting but remove the colon and downcase the first character (e.g. ``zipDownloadLimit``)

- Final path example: ``/api/info/zipDownloadLimit``

- If the database setting you're exposing make more sense outside of ``/api/info`` because there's more context (e.g. ``:CustomDatasetSummaryFields``):

- Feel free to use a path outside of ``/api/info`` as the root path.

- Given additional context, append a shortened name (e.g. ``/api/datasets/summaryFieldNames``).

- Final path example: ``/api/datasets/summaryFieldNames``

- If you need to expose a JVM option (MicroProfile setting) such as ``dataverse.api.allow-incomplete-metadata``:

- Use ``/api/info`` as the root path.

- Append a meaningful name for the setting (e.g. ``incompleteMetadataViaApi``).

- Final path example: ``/api/info/incompleteMetadataViaApi``

1 change: 1 addition & 0 deletions doc/sphinx-guides/source/developers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Developer Guide
sql-upgrade-scripts
testing
documentation
api-design
security
dependencies
debugging
Expand Down
2 changes: 2 additions & 0 deletions doc/sphinx-guides/source/installation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2980,6 +2980,8 @@ This setting controls the number of files that can be uploaded through the UI at

``curl -X PUT -d 500 http://localhost:8080/api/admin/settings/:MultipleUploadFilesLimit``

.. _:ZipDownloadLimit:

:ZipDownloadLimit
+++++++++++++++++

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,12 @@ protected Response ok( boolean value ) {
.add("data", value).build() ).build();
}

protected Response ok(long value) {
return Response.ok().entity(Json.createObjectBuilder()
.add("status", ApiConstants.STATUS_OK)
.add("data", value).build()).build();
}

/**
* @param data Payload to return.
* @param mediaType Non-JSON media type.
Expand Down
39 changes: 28 additions & 11 deletions src/main/java/edu/harvard/iq/dataverse/api/Info.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ public class Info extends AbstractApiBean {
@GET
@Path("settings/:DatasetPublishPopupCustomText")
public Response getDatasetPublishPopupCustomText() {
String setting = settingsService.getValueForKey(SettingsServiceBean.Key.DatasetPublishPopupCustomText);
if (setting != null) {
return ok(Json.createObjectBuilder().add("message", setting));
} else {
return notFound("Setting " + SettingsServiceBean.Key.DatasetPublishPopupCustomText + " not found");
}
return getSettingResponseByKey(SettingsServiceBean.Key.DatasetPublishPopupCustomText);
}


@GET
@Path("settings/:MaxEmbargoDurationInMonths")
public Response getMaxEmbargoDurationInMonths() {
return getSettingResponseByKey(SettingsServiceBean.Key.MaxEmbargoDurationInMonths);
}

@GET
@AuthRequired
@Path("version")
Expand All @@ -41,28 +42,44 @@ public Response getInfo(@Context ContainerRequestContext crc) {
String[] comps = versionStr.split("build",2);
String version = comps[0].trim();
JsonValue build = comps.length > 1 ? Json.createArrayBuilder().add(comps[1].trim()).build().get(0) : JsonValue.NULL;

return response( req -> ok( Json.createObjectBuilder().add("version", version)
.add("build", build)), getRequestUser(crc));
}

@GET
@AuthRequired
@Path("server")
public Response getServer(@Context ContainerRequestContext crc) {
return response( req -> ok(JvmSettings.FQDN.lookup()), getRequestUser(crc));
}

@GET
@AuthRequired
@Path("apiTermsOfUse")
public Response getTermsOfUse(@Context ContainerRequestContext crc) {
return response( req -> ok(systemConfig.getApiTermsOfUse()), getRequestUser(crc));
}

@GET
@Path("settings/incompleteMetadataViaApi")
public Response getAllowsIncompleteMetadata() {
return ok(JvmSettings.API_ALLOW_INCOMPLETE_METADATA.lookupOptional(Boolean.class).orElse(false));
}

@GET
@Path("zipDownloadLimit")
public Response getZipDownloadLimit() {
long zipDownloadLimit = SystemConfig.getLongLimitFromStringOrDefault(settingsSvc.getValueForKey(SettingsServiceBean.Key.ZipDownloadLimit), SystemConfig.defaultZipDownloadLimit);
return ok(zipDownloadLimit);
}

private Response getSettingResponseByKey(SettingsServiceBean.Key key) {
String setting = settingsService.getValueForKey(key);
if (setting != null) {
return ok(Json.createObjectBuilder().add("message", setting));
} else {
return notFound("Setting " + key + " not found");
}
}
}
87 changes: 60 additions & 27 deletions src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
package edu.harvard.iq.dataverse.api;

import static io.restassured.RestAssured.given;

import io.restassured.response.Response;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
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.NOT_FOUND;
import static jakarta.ws.rs.core.Response.Status.OK;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;

public class InfoIT {

@Test
public void testGetDatasetPublishPopupCustomText() {

given().urlEncodingEnabled(false)
.body("Hello world!")
.put("/api/admin/settings/"
+ SettingsServiceBean.Key.DatasetPublishPopupCustomText);
@BeforeAll
public static void setUpClass() {
UtilIT.deleteSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths);
UtilIT.deleteSetting(SettingsServiceBean.Key.DatasetPublishPopupCustomText);
}

Response response = given().urlEncodingEnabled(false)
.get("/api/info/settings/" + SettingsServiceBean.Key.DatasetPublishPopupCustomText);
response.prettyPrint();
response.then().assertThat().statusCode(200)
.body("data.message", equalTo("Hello world!"));
@AfterAll
public static void afterClass() {
UtilIT.deleteSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths);
UtilIT.deleteSetting(SettingsServiceBean.Key.DatasetPublishPopupCustomText);
}

given().urlEncodingEnabled(false)
.delete("/api/admin/settings/"
+ SettingsServiceBean.Key.DatasetPublishPopupCustomText);
@Test
public void testGetDatasetPublishPopupCustomText() {
testSettingEndpoint(SettingsServiceBean.Key.DatasetPublishPopupCustomText, "Hello world!");
}

response = given().urlEncodingEnabled(false)
.get("/api/info/settings/" + SettingsServiceBean.Key.DatasetPublishPopupCustomText);
response.prettyPrint();
response.then().assertThat().statusCode(404)
.body("message", equalTo("Setting "
+ SettingsServiceBean.Key.DatasetPublishPopupCustomText
+ " not found"));
@Test
public void testGetMaxEmbargoDurationInMonths() {
testSettingEndpoint(SettingsServiceBean.Key.MaxEmbargoDurationInMonths, "12");
}

@Test
public void testGetVersion() {
Response response = given().urlEncodingEnabled(false)
.get("/api/info/version");
response.prettyPrint();
response.then().assertThat().statusCode(200)
response.then().assertThat().statusCode(OK.getStatusCode())
.body("data.version", notNullValue());
}

Expand All @@ -51,16 +51,49 @@ public void testGetServer() {
Response response = given().urlEncodingEnabled(false)
.get("/api/info/server");
response.prettyPrint();
response.then().assertThat().statusCode(200)
response.then().assertThat().statusCode(OK.getStatusCode())
.body("data.message", notNullValue());
}

@Test
public void getTermsOfUse() {
public void testGetTermsOfUse() {
Response response = given().urlEncodingEnabled(false)
.get("/api/info/apiTermsOfUse");
response.prettyPrint();
response.then().assertThat().statusCode(200)
response.then().assertThat().statusCode(OK.getStatusCode())
.body("data.message", notNullValue());
}

@Test
public void testGetAllowsIncompleteMetadata() {
Response response = given().urlEncodingEnabled(false)
.get("/api/info/settings/incompleteMetadataViaApi");
response.prettyPrint();
response.then().assertThat().statusCode(OK.getStatusCode())
.body("data", notNullValue());
}

@Test
public void testGetZipDownloadLimit() {
Response response = given().urlEncodingEnabled(false)
.get("/api/info/zipDownloadLimit");
response.prettyPrint();
response.then().assertThat().statusCode(OK.getStatusCode())
.body("data", notNullValue());
}

private void testSettingEndpoint(SettingsServiceBean.Key settingKey, String testSettingValue) {
String endpoint = "/api/info/settings/" + settingKey;
// Setting not found
Response response = given().urlEncodingEnabled(false).get(endpoint);
response.prettyPrint();
response.then().assertThat().statusCode(NOT_FOUND.getStatusCode())
.body("message", equalTo("Setting " + settingKey + " not found"));
// Setting exists
UtilIT.setSetting(settingKey, testSettingValue);
response = given().urlEncodingEnabled(false).get(endpoint);
response.prettyPrint();
response.then().assertThat().statusCode(OK.getStatusCode())
.body("data.message", equalTo(testSettingValue));
}
}
Loading