Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions doc/release-notes/11912-edit-template-apis
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## New Endpoint: PUT `/dataverses/{templateId}/metadata`

A new endpoint has been implemented to edit the metadata and field instructions for a given template.

### Functionality
- Updates the metadata and field instructions for a template based on a json file provided.
- You must have edit dataverse permission in the collection in order to use this endpoint.

## New Endpoint: PUT `/dataverses/{templateId}/licenseTerms`

A new endpoint has been implemented to edit the license or custom terms of use for a given template.

### Functionality
- Updates the license or custom terms of use for a template based on a json file provided.
- You must have edit dataverse permission in the collection in order to use this endpoint.

## New Endpoint: PUT `/dataverses/{templateId}/access`

A new endpoint has been implemented to edit the terms of access for a given template.

### Functionality
- Updates the terms of access for a template based on a json file provided.
- You must have edit dataverse permission in the collection in order to use this endpoint.
13 changes: 13 additions & 0 deletions doc/sphinx-guides/source/_static/api/template-update-access.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"customTermsOfAccess": {
"fileAccessRequest": false,
"termsOfAccess": "Here are the terms...",
"dataAccessPlace": "dataAccessPlace",
"originalArchive": "originalArchive",
"availabilityStatus": "availabilityStatus",
"contactForAccess": "contactForAccess",
"sizeOfCollection": "sizeOfCollection",
"studyCompletion": "studyCompletion",
"confidentialityDeclaration": "confidentialityDeclaration"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "name": "CC BY 4.0" }
30 changes: 30 additions & 0 deletions doc/sphinx-guides/source/_static/api/template-update-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "Dataverse template - edited",
"fields": [
{
"typeName": "author",
"value": [
{
"authorName": {
"typeName": "authorName",
"value": "Brady, Tom"
},
"authorAffiliation": {
"typeName": "authorIdentifierScheme",
"value": "ORCID"
}
}
]
}
],
"instructions": [
{
"instructionField": "author",
"instructionText": "The author data, edited"
},
{
"instructionField": "subtitle",
"instructionText": "Instructions for subtitle"
}
]
}
12 changes: 12 additions & 0 deletions doc/sphinx-guides/source/_static/api/template-update-terms.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"customTerms": {
"termsOfUse": "testTermsOfUse",
"confidentialityDeclaration": "testConfidentialityDeclaration",
"specialPermissions": "testSpecialPermissions",
"restrictions": "testRestrictions",
"citationRequirements": "testCitationRequirements",
"depositorRequirements": "testDepositorRequirements",
"conditions": "testConditions",
"disclaimer": "testDisclaimer"
}
}
63 changes: 63 additions & 0 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,69 @@ The fully expanded example above (without environment variables) looks like this

curl -H "X-Dataverse-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X POST "https://demo.dataverse.org/api/dataverses/1/templates" --upload-file dataverse-template.json

Update the Metadata and Instructions of a Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Updates the metadata and instructions of a template with a given ``id``.

To update the template, you must send a JSON file. Your JSON file might look like :download:`template-update-metadata.json <../_static/api/template-update-metadata.json>` which you would send to the Dataverse installation like this:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export ID=1

curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/dataverses/{ID}/metadata" --upload-file template-update-metadata.json

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

.. code-block:: bash

curl -H "X-Dataverse-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT "https://demo.dataverse.org/api/dataverses/1/metadata" --upload-file template-update-metadata.json

Update the License or Terms Of Use of a Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Updates the license or custom terms of use of a template with a given ``id``.

To update the template, you must send a JSON file containing either the name of an active license or custom terms of use. Your JSON file might look like :download:`template-update-license.json <../_static/api/template-update-license.json>` or :download:`template-update-terms.json <../_static/api/template-update-terms.json>` which you would send to the Dataverse installation like this:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export ID=1

curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/dataverses/{ID}/licenseTerms" --upload-file template-update-license.json

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

.. code-block:: bash

curl -H "X-Dataverse-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT "https://demo.dataverse.org/api/dataverses/1/licenseTerms" --upload-file template-update-license.json

Update the Terms Of Access of a Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Updates the terms of access of a template with a given ``id``.

To update the template, you must send a JSON file containing either the name of an active license or custom terms of use. Your JSON file might look like :download:`template-update-access.json <../_static/api/template-update-access.json>` which you would send to the Dataverse installation like this:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export ID=1

curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/dataverses/{ID}/access" --upload-file template-update-access.json

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

.. code-block:: bash

curl -H "X-Dataverse-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT "https://demo.dataverse.org/api/dataverses/1/access" --upload-file template-update-access.json

Set a Default Template for a Collection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
15 changes: 14 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@ String getWrappedMessageWhenJson() {

@EJB
GuestbookResponseServiceBean gbRespSvc;

@EJB
TemplateServiceBean templateSvc;

@Inject
FailedPIDResolutionLoggingServiceBean fprLogService;
Expand Down Expand Up @@ -370,8 +373,18 @@ protected Dataverse findDataverseOrDie( String dvIdtf ) throws WrappedResponse {
}
return dv;
}

protected Template findTemplateOrDie(Long templateId) throws WrappedResponse {

Template template = templateSvc.find(templateId);
if (template == null) {
throw new WrappedResponse(
error(Response.Status.NOT_FOUND, "Can't find template with identifier='" + templateId + "'"));
}
return template;
}

protected Template findTemplateOrDie(Long templateId, Dataverse dataverse) throws WrappedResponse {
protected Template findAllTemplatesOrDie(Long templateId, Dataverse dataverse) throws WrappedResponse {

List<Template> templates = new ArrayList<>();

Expand Down
111 changes: 108 additions & 3 deletions src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.engine.command.impl.*;
import edu.harvard.iq.dataverse.license.License;
import edu.harvard.iq.dataverse.pidproviders.PidProvider;
import edu.harvard.iq.dataverse.pidproviders.PidUtil;
import edu.harvard.iq.dataverse.settings.JvmSettings;
Expand Down Expand Up @@ -72,6 +73,7 @@
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
import jakarta.ws.rs.core.StreamingOutput;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
Expand Down Expand Up @@ -2016,9 +2018,9 @@ public Response getTemplate(@Context ContainerRequestContext crc, @PathParam("id
public Response createTemplate(@Context ContainerRequestContext crc, String body, @PathParam("identifier") String dvIdtf) {
try {
Dataverse dataverse = findDataverseOrDie(dvIdtf);
NewTemplateDTO newTemplateDTO;
TemplateDTO newTemplateDTO;
try {
newTemplateDTO = NewTemplateDTO.fromRequestBody(body, jsonParser());
newTemplateDTO = TemplateDTO.fromRequestBody(body, jsonParser());
} catch (JsonParseException ex) {
return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.createTemplate.error.jsonParseMetadataFields"), ex.getMessage()));
}
Expand All @@ -2030,6 +2032,109 @@ public Response createTemplate(@Context ContainerRequestContext crc, String body
return e.getResponse();
}
}

@PUT
@AuthRequired
@Path("{templateId}/metadata")
public Response updateTemplateMetadata(@Context ContainerRequestContext crc, String body, @PathParam("templateId") Long templateId, @QueryParam("replace") boolean replaceData) {
try {
Template template = findTemplateOrDie(templateId);
Dataverse dataverse = template.getDataverse();

JsonObject json = JsonUtil.getJsonObject(body);

/*
You can also set a new name for your template in the json
*/

String templateName = json.getString("name");
if (!templateName.isEmpty() && !templateName.isBlank()){
template.setName(templateName);
}


List<DatasetField> updatedFields = new ArrayList<>();
if (json.getJsonArray("fields") == null) {
updatedFields.add(jsonParser().parseField(json, Boolean.FALSE, replaceData));
} else {
updatedFields = jsonParser().parseMultipleFields(json, replaceData);
}

Map<String, String> instructionsMap = jsonParser().parseRequestBodyInstructionsMap(json);

Template updated = execCommand(new UpdateTemplateFieldsCommand(template, dataverse, updatedFields, instructionsMap, replaceData, createDataverseRequest(getRequestUser(crc))));

return created("/dataverses/template/" + updated.getId(), jsonTemplate(updated));
} catch (JsonParseException ex) {
logger.log(Level.SEVERE, "Semantic error parsing dataset update Json: " + ex.getMessage(), ex);
return error(Response.Status.BAD_REQUEST, BundleUtil.getStringFromBundle("datasets.api.editMetadata.error.parseUpdate", List.of(ex.getMessage())));


} catch (WrappedResponse e) {
return e.getResponse();
}
}

@PUT
@AuthRequired
@Path("{templateId}/licenseTerms")
public Response updateTemplateLicenseTerms(@Context ContainerRequestContext crc, LicenseUpdateRequest requestBody, @PathParam("templateId") Long templateId, @QueryParam("replace") boolean replaceData) {
try {
Template template = findTemplateOrDie(templateId);
Dataverse dataverse = template.getDataverse();

if (requestBody.getName() != null && !requestBody.getName().isEmpty()) {
String licenseName = requestBody.getName();
License license = licenseSvc.getByNameOrUri(licenseName);
if (license == null) {
return notFound(BundleUtil.getStringFromBundle("datasets.api.updateLicense.licenseNotFound", List.of(licenseName)));
}

execCommand(new UpdateTemplateLicenseCommand(createDataverseRequest(getRequestUser(crc)), template, dataverse, license));
return ok(BundleUtil.getStringFromBundle("dataverses.api.update.template.license.success"));
} else if (requestBody.getCustomTerms() != null) {
CustomTermsDTO customTerms = requestBody.getCustomTerms();
execCommand(new UpdateTemplateLicenseCommand(createDataverseRequest(getRequestUser(crc)), template, dataverse, customTerms.toTermsOfUseAndAccess()));
return ok(BundleUtil.getStringFromBundle("dataverses.api.update.template.license.success"));
} else {
return badRequest(BundleUtil.getStringFromBundle("datasets.api.updateLicense.licenseNameIsEmpty"));
}

} catch (WrappedResponse e) {
return e.getResponse();
}
}

@PUT
@AuthRequired
@Path("{templateId}/access")
public Response updateTemplateTermsOfAccess(@Context ContainerRequestContext crc, String jsonBody, @PathParam("templateId") Long templateId) {
try {

boolean publicInstall = settingsSvc.isTrueForKey(SettingsServiceBean.Key.PublicInstall, false);

Template template = findTemplateOrDie(templateId);

JsonObject json = JsonUtil.getJsonObject(jsonBody);

TermsOfUseAndAccess toua = jsonParser().parseTermsOfAccess(json);

if (publicInstall && (toua.isFileAccessRequest() || !toua.getTermsOfAccess().isEmpty())){
return error(BAD_REQUEST, "Setting File Access Request or Terms of Access is not permitted on a public installation.");
}

execCommand(new UpdateTemplateTermsOfAccessCommand(createDataverseRequest(getRequestUser(crc)), template, template.getDataverse(), toua ));

return ok(BundleUtil.getStringFromBundle("dataverses.api.update.template.access.success"));

} catch (JsonParseException ex) {
logger.log(Level.SEVERE, "Semantic error parsing template terms update Json: " + ex.getMessage(), ex);
return error(Response.Status.BAD_REQUEST, BundleUtil.getStringFromBundle("datasets.api.editMetadata.error.parseUpdate", List.of(ex.getMessage())));
} catch (WrappedResponse ex) {
logger.log(Level.SEVERE, "Update terms of access error: " + ex.getMessage(), ex);
return ex.getResponse();
}
}

@POST
@AuthRequired
Expand All @@ -2041,7 +2146,7 @@ public Response setDefaultTemplate(@Context ContainerRequestContext crc,
try {

Dataverse dataverse = findDataverseOrDie(dvId);
Template template = findTemplateOrDie(templateId, dataverse);
Template template = findAllTemplatesOrDie(templateId, dataverse);
DataverseRequest dvReq = createDataverseRequest(getRequestUser(crc));
SetDefaultTemplateCommand command = new SetDefaultTemplateCommand(template, dvReq, dataverse);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@
import java.sql.Timestamp;
import java.util.*;

public class NewTemplateDTO {
public class TemplateDTO {

private String name;
private List<DatasetField> datasetFields;
private Map<String, String> instructionsMap;
private boolean isDefault;

public static NewTemplateDTO fromRequestBody(String requestBody, JsonParser jsonParser) throws JsonParseException {
NewTemplateDTO newTemplateDTO = new NewTemplateDTO();
public static TemplateDTO fromRequestBody(String requestBody, JsonParser jsonParser) throws JsonParseException {
TemplateDTO templateDTO = new TemplateDTO();

JsonObject jsonObject = JsonUtil.getJsonObject(requestBody);

newTemplateDTO.name = jsonObject.getString("name");
newTemplateDTO.datasetFields = jsonParser.parseMultipleFields(jsonObject);
newTemplateDTO.instructionsMap = parseRequestBodyInstructionsMap(jsonObject);
newTemplateDTO.isDefault = jsonObject.getBoolean("isDefault", false);

return newTemplateDTO;
templateDTO.name = jsonObject.getString("name");
templateDTO.datasetFields = jsonParser.parseMultipleFields(jsonObject);
templateDTO.instructionsMap = jsonParser.parseRequestBodyInstructionsMap(jsonObject);
templateDTO.isDefault = jsonObject.getBoolean("isDefault", false);
return templateDTO;
}

public Template toTemplate() {
Expand Down Expand Up @@ -59,18 +59,4 @@ public boolean isDefault() {
return isDefault;
}

private static Map<String, String> parseRequestBodyInstructionsMap(JsonObject jsonObject) {
Map<String, String> instructionsMap = new HashMap<>();
JsonArray instructionsJsonArray = jsonObject.getJsonArray("instructions");
if (instructionsJsonArray == null) {
return null;
}
for (JsonObject instructionJsonObject : instructionsJsonArray.getValuesAs(JsonObject.class)) {
instructionsMap.put(
instructionJsonObject.getString("instructionField"),
instructionJsonObject.getString("instructionText")
);
}
return instructionsMap;
}
}
Loading