diff --git a/doc/release-notes/8551-sword-license.md b/doc/release-notes/8551-sword-license.md new file mode 100644 index 00000000000..90b7180ac77 --- /dev/null +++ b/doc/release-notes/8551-sword-license.md @@ -0,0 +1 @@ +As of Dataverse 5.10, "NONE" is no longer supported as a valid license when creating a dataset using the SWORD API. The API Guide has been updated to reflect this. Additionally, if you specify an invalid license, a list of available licenses will be returned in the response. diff --git a/doc/sphinx-guides/source/api/sword-atom-entry.xml b/doc/sphinx-guides/source/api/sword-atom-entry.xml index d47a3dd4f56..8d73653e93e 100755 --- a/doc/sphinx-guides/source/api/sword-atom-entry.xml +++ b/doc/sphinx-guides/source/api/sword-atom-entry.xml @@ -27,8 +27,8 @@ United States Canada - NONE - Downloader will not use the Materials in any way prohibited by applicable laws. + CC0 1.0 + Peets, J., & Stumptown, J. (2013). Roasting at Home. New England Journal of Coffee, 3(1), 22-34. diff --git a/doc/sphinx-guides/source/api/sword.rst b/doc/sphinx-guides/source/api/sword.rst index d853994f073..d4f56ddb5b4 100755 --- a/doc/sphinx-guides/source/api/sword.rst +++ b/doc/sphinx-guides/source/api/sword.rst @@ -65,6 +65,8 @@ Differences in Dataverse Software 4 from DVN 3.x lead to a few minor backward in - The Service Document will show a single API Terms of Use rather than root level and Dataverse collection level Deposit Terms of Use. +- As of Dataverse Software 5.10, ``NONE`` is no longer supported as a license. + New features as of v1.1 ----------------------- @@ -80,7 +82,7 @@ New features as of v1.1 - "Contributor" can now be populated and the "Type" (Editor, Funder, Researcher, etc.) can be specified with an XML attribute. For example: ``CaffeineForAll`` -- "License" can now be set with ``dcterms:license`` and the possible values are "CC0" and "NONE". "License" interacts with "Terms of Use" (``dcterms:rights``) in that if you include ``dcterms:rights`` in the XML, the license will be set to "NONE". If you don't include ``dcterms:rights``, the license will default to "CC0". It is invalid to specify "CC0" as a license and also include ``dcterms:rights``; an error will be returned. For backwards compatibility, ``dcterms:rights`` is allowed to be blank (i.e. ````) but blank values will not be persisted to the database and the license will be set to "NONE". +- "License" can now be set with ``dcterms:license`` and the possible values determined by the installation ("CC0 1.0" and "CC BY 4.0" by default). "License" interacts with "Terms of Use" (``dcterms:rights``) in that if you include ``dcterms:rights`` in the XML and don't include ``dcterms:license``, the license will be "Custom Dataset Terms" and "Terms of Use" will be populated. If you don't include ``dcterms:rights``, the default license will be used. It is invalid to specify a license and also include ``dcterms:rights``; an error will be returned. For backwards compatibility, ``dcterms:rights`` is allowed to be blank (i.e. ````) but blank values will not be persisted to the database and the license will be set to "Custom Dataset Terms". - "Contact E-mail" is automatically populated from dataset owner's email. @@ -143,9 +145,9 @@ Dublin Core Terms (DC Terms) Qualified Mapping - Dataverse Project DB Element Cr +-----------------------------+----------------------------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ |dcterms:coverage | otherGeographicCoverage | | General information on the geographic coverage of the Dataset. | +-----------------------------+----------------------------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ -|dcterms:license | license | | Set the license to CC0 (default in a Dataverse installation for new Datasets), otherwise enter "NONE" and fill in the dcterms:rights field. | +|dcterms:license | license | | Set the license. Alternatively, use the dcterms:rights field instead. | +-----------------------------+----------------------------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ -|dcterms:rights | termsofuse | | If not using CC0, enter any terms of use or restrictions for the Dataset. | +|dcterms:rights | termsofuse | | If not using dcterms:license, enter any terms of use or restrictions for the Dataset. | +-----------------------------+----------------------------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ |dcterms:isReferencedBy | publicationCitation | | The publication (journal article, book, other work) that uses this dataset (include citation, permanent identifier (DOI), and permanent URL). | +-----------------------------+----------------------------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/main/java/edu/harvard/iq/dataverse/api/datadeposit/SwordServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/api/datadeposit/SwordServiceBean.java index 7e45381d410..46c38e04153 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/datadeposit/SwordServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/datadeposit/SwordServiceBean.java @@ -12,6 +12,7 @@ import edu.harvard.iq.dataverse.license.License; import edu.harvard.iq.dataverse.license.LicenseServiceBean; import edu.harvard.iq.dataverse.util.BundleUtil; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -186,7 +187,13 @@ public void setDatasetLicenseAndTermsOfUse(DatasetVersion datasetVersionToMutate setTermsOfUse(datasetVersionToMutate, dcterms, null); } else { License licenseToSet = licenseServiceBean.getByNameOrUri(licenseProvided); - if (licenseToSet == null) throw new SwordError("Couldn't find an active license with: " + licenseProvided); + if (licenseToSet == null || !licenseToSet.isActive()) { + List licenses = new ArrayList<>(); + for (License license : licenseServiceBean.listAllActive()) { + licenses.add(license.getName()); + } + throw new SwordError("Couldn't find an active license with: " + licenseProvided + ". Valid licenses: " + licenses); + } terms.setLicense(licenseToSet); setTermsOfUse(datasetVersionToMutate, dcterms, licenseToSet); } @@ -204,7 +211,7 @@ private void setTermsOfUse(DatasetVersion datasetVersionToMutate, Map 0) { - throw new SwordError("Terms of Use (dcterms:rights) can not be specified in combination with a license. A Custom License can be used instead."); + throw new SwordError("Terms of Use (dcterms:rights) cannot be specified in combination with a license."); } } else { if (numRightsProvided != 1) { diff --git a/src/test/java/edu/harvard/iq/dataverse/api/SwordIT.java b/src/test/java/edu/harvard/iq/dataverse/api/SwordIT.java index ea6709cb915..29173d3bd76 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/SwordIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/SwordIT.java @@ -5,6 +5,9 @@ import com.jayway.restassured.response.Response; import edu.harvard.iq.dataverse.GlobalId; import edu.harvard.iq.dataverse.api.datadeposit.SwordConfigurationImpl; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; import java.util.List; import java.util.Map; @@ -195,6 +198,13 @@ public void testCreateDataverseCreateDatasetUploadFileDownloadFileEditTitle() { String persistentId = UtilIT.getDatasetPersistentIdFromSwordResponse(createDatasetResponse); logger.info("persistent id: " + persistentId); + Response getJson = UtilIT.nativeGetUsingPersistentId(persistentId, apiToken); + getJson.prettyPrint(); + getJson.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.latestVersion.license.name", equalTo("CC0 1.0")) + .body("data.latestVersion.license.uri", equalTo("http://creativecommons.org/publicdomain/zero/1.0")); + Response atomEntryUnAuth = UtilIT.getSwordAtomEntry(persistentId, apiTokenNoPrivs); atomEntryUnAuth.prettyPrint(); atomEntryUnAuth.then().assertThat() @@ -638,6 +648,107 @@ public void testCreateDatasetPublishDestroy() { } + + @Test + public void testLicenses() { + + Response createUser = UtilIT.createRandomUser(); + String username = UtilIT.getUsernameFromResponse(createUser); + String apiToken = UtilIT.getApiTokenFromResponse(createUser); + + Response createDataverse = UtilIT.createRandomDataverse(apiToken); + createDataverse.prettyPrint(); + createDataverse.then().assertThat() + .statusCode(CREATED.getStatusCode()); + String dataverseAlias = UtilIT.getAliasFromResponse(createDataverse); + + String title = "License to Kill"; + String description = "Spies in 1989"; + String license = "NONE"; + Response failToCreateDataset1 = UtilIT.createDatasetViaSwordApi(dataverseAlias, title, description, license, apiToken); + failToCreateDataset1.prettyPrint(); + // As of 5.10 and PR #7920, you cannot pass NONE as a license. + failToCreateDataset1.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()); + + String rights = "Call me"; + Response failToCreateDataset2 = UtilIT.createDatasetViaSwordApi(dataverseAlias, title, description, license, rights, apiToken); + failToCreateDataset2.prettyPrint(); + // You can't pass both license and rights + failToCreateDataset2.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()); + + license = "CC0 1.0"; + Response createDataset = UtilIT.createDatasetViaSwordApi(dataverseAlias, title, description, license, apiToken); + createDataset.prettyPrint(); + createDataset.then().assertThat() + .statusCode(CREATED.getStatusCode()); + + String persistentId = UtilIT.getDatasetPersistentIdFromSwordResponse(createDataset); + + Response getJson = UtilIT.nativeGetUsingPersistentId(persistentId, apiToken); + getJson.prettyPrint(); + getJson.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.latestVersion.license.name", equalTo("CC0 1.0")) + .body("data.latestVersion.license.uri", equalTo("http://creativecommons.org/publicdomain/zero/1.0")) + .body("data.latestVersion.termsOfUse", equalTo(null)); + } + + @Test + public void testCustomTerms() { + + Response createUser = UtilIT.createRandomUser(); + String username = UtilIT.getUsernameFromResponse(createUser); + String apiToken = UtilIT.getApiTokenFromResponse(createUser); + + Response createDataverse = UtilIT.createRandomDataverse(apiToken); + createDataverse.prettyPrint(); + createDataverse.then().assertThat() + .statusCode(CREATED.getStatusCode()); + String dataverseAlias = UtilIT.getAliasFromResponse(createDataverse); + + String title = "Terms of Endearment"; + String description = "Aurora, etc."; + String license = null; + String rights = "Call me"; + Response createDataset = UtilIT.createDatasetViaSwordApi(dataverseAlias, title, description, license, rights, apiToken); + createDataset.prettyPrint(); + createDataset.then().assertThat() + .statusCode(CREATED.getStatusCode()); + + String persistentId = UtilIT.getDatasetPersistentIdFromSwordResponse(createDataset); + + Response getJson = UtilIT.nativeGetUsingPersistentId(persistentId, apiToken); + getJson.prettyPrint(); + getJson.then().assertThat() + .statusCode(OK.getStatusCode()) + .body("data.latestVersion.termsOfUse", equalTo("Call me")) + .body("data.latestVersion.license", equalTo(null)); + } + + @Test + public void testXmlExampleInGuides() throws IOException { + + Response createUser = UtilIT.createRandomUser(); + String username = UtilIT.getUsernameFromResponse(createUser); + String apiToken = UtilIT.getApiTokenFromResponse(createUser); + + Response createDataverse = UtilIT.createRandomDataverse(apiToken); + createDataverse.prettyPrint(); + createDataverse.then().assertThat() + .statusCode(CREATED.getStatusCode()); + String dataverseAlias = UtilIT.getAliasFromResponse(createDataverse); + + File exampleFile = new File("doc/sphinx-guides/source/api/sword-atom-entry.xml"); + String xmlIn = new String(java.nio.file.Files.readAllBytes(Paths.get(exampleFile.getAbsolutePath()))); + Response createDataset = UtilIT.createDatasetViaSwordApiFromXML(dataverseAlias, xmlIn, apiToken); + createDataset.prettyPrint(); + createDataset.then().assertThat() + .statusCode(CREATED.getStatusCode()); + + } + /** * This test requires the root dataverse to have been published already. * diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java index 23672c45916..7b9b5f3b129 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -436,7 +436,18 @@ static Response createDatasetViaSwordApi(String dataverseToCreateDatasetIn, Stri return createDatasetViaSwordApiFromXML(dataverseToCreateDatasetIn, xmlIn, apiToken); } - private static Response createDatasetViaSwordApiFromXML(String dataverseToCreateDatasetIn, String xmlIn, String apiToken) { + static Response createDatasetViaSwordApi(String dataverseToCreateDatasetIn, String title, String description, String license, String apiToken) { + String nullRights = null; + String xmlIn = getDatasetXml(title, "Lastname, Firstname", description, license, nullRights); + return createDatasetViaSwordApiFromXML(dataverseToCreateDatasetIn, xmlIn, apiToken); + } + + static Response createDatasetViaSwordApi(String dataverseToCreateDatasetIn, String title, String description, String license, String rights, String apiToken) { + String xmlIn = getDatasetXml(title, "Lastname, Firstname", description, license, rights); + return createDatasetViaSwordApiFromXML(dataverseToCreateDatasetIn, xmlIn, apiToken); + } + + public static Response createDatasetViaSwordApiFromXML(String dataverseToCreateDatasetIn, String xmlIn, String apiToken) { Response createDatasetResponse = given() .auth().basic(apiToken, EMPTY_STRING) .body(xmlIn) @@ -534,11 +545,27 @@ static Response loadMetadataBlock(String apiToken, byte[] body) { } static private String getDatasetXml(String title, String author, String description) { + String nullLicense = null; + String nullRights = null; + return getDatasetXml(title, author, description, nullLicense, nullRights); + } + + static private String getDatasetXml(String title, String author, String description, String license, String rights) { + String optionalLicense = ""; + if (license != null) { + optionalLicense = " " + license + "\n"; + } + String optionalRights = ""; + if (rights != null) { + optionalRights = " " + rights + "\n"; + } String xmlIn = "\n" + "\n" + " " + title + "\n" + " " + author + "\n" + " " + description + "\n" + + optionalLicense + + optionalRights + "\n" + ""; return xmlIn;