Skip to content

Commit 718b114

Browse files
authored
Merge pull request #12159 from IQSS/11912-edit-template-api
11912 edit template api
2 parents a0cf2ef + 5727c1b commit 718b114

19 files changed

Lines changed: 1422 additions & 40 deletions
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## New Endpoint: PUT `/dataverses/{templateId}/metadata`
2+
3+
A new endpoint has been implemented to edit the metadata and field instructions for a given template.
4+
5+
### Functionality
6+
- Updates the metadata and field instructions for a template based on a json file provided.
7+
- You must have edit dataverse permission in the collection in order to use this endpoint.
8+
9+
## New Endpoint: PUT `/dataverses/{templateId}/licenseTerms`
10+
11+
A new endpoint has been implemented to edit the license or custom terms of use for a given template.
12+
13+
### Functionality
14+
- Updates the license or custom terms of use for a template based on a json file provided.
15+
- You must have edit dataverse permission in the collection in order to use this endpoint.
16+
17+
## New Endpoint: PUT `/dataverses/{templateId}/access`
18+
19+
A new endpoint has been implemented to edit the terms of access for a given template.
20+
21+
### Functionality
22+
- Updates the terms of access for a template based on a json file provided.
23+
- You must have edit dataverse permission in the collection in order to use this endpoint.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"customTermsOfAccess": {
3+
"fileAccessRequest": false,
4+
"termsOfAccess": "Here are the terms...",
5+
"dataAccessPlace": "dataAccessPlace",
6+
"originalArchive": "originalArchive",
7+
"availabilityStatus": "availabilityStatus",
8+
"contactForAccess": "contactForAccess",
9+
"sizeOfCollection": "sizeOfCollection",
10+
"studyCompletion": "studyCompletion",
11+
"confidentialityDeclaration": "confidentialityDeclaration"
12+
}
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "name": "CC BY 4.0" }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "Dataverse template - edited",
3+
"fields": [
4+
{
5+
"typeName": "author",
6+
"value": [
7+
{
8+
"authorName": {
9+
"typeName": "authorName",
10+
"value": "Brady, Tom"
11+
},
12+
"authorAffiliation": {
13+
"typeName": "authorIdentifierScheme",
14+
"value": "ORCID"
15+
}
16+
}
17+
]
18+
}
19+
],
20+
"instructions": [
21+
{
22+
"instructionField": "author",
23+
"instructionText": "The author data, edited"
24+
},
25+
{
26+
"instructionField": "subtitle",
27+
"instructionText": "Instructions for subtitle"
28+
}
29+
]
30+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"customTerms": {
3+
"termsOfUse": "testTermsOfUse",
4+
"confidentialityDeclaration": "testConfidentialityDeclaration",
5+
"specialPermissions": "testSpecialPermissions",
6+
"restrictions": "testRestrictions",
7+
"citationRequirements": "testCitationRequirements",
8+
"depositorRequirements": "testDepositorRequirements",
9+
"conditions": "testConditions",
10+
"disclaimer": "testDisclaimer"
11+
}
12+
}

doc/sphinx-guides/source/api/native-api.rst

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,69 @@ The fully expanded example above (without environment variables) looks like this
17551755
17561756
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
17571757
1758+
Update the Metadata and Instructions of a Template
1759+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1760+
1761+
Updates the metadata and instructions of a template with a given ``id``.
1762+
1763+
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:
1764+
1765+
.. code-block:: bash
1766+
1767+
export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
1768+
export SERVER_URL=https://demo.dataverse.org
1769+
export ID=1
1770+
1771+
curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/dataverses/{ID}/metadata" --upload-file template-update-metadata.json
1772+
1773+
The fully expanded example above (without environment variables) looks like this:
1774+
1775+
.. code-block:: bash
1776+
1777+
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
1778+
1779+
Update the License or Terms Of Use of a Template
1780+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1781+
1782+
Updates the license or custom terms of use of a template with a given ``id``.
1783+
1784+
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:
1785+
1786+
.. code-block:: bash
1787+
1788+
export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
1789+
export SERVER_URL=https://demo.dataverse.org
1790+
export ID=1
1791+
1792+
curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/dataverses/{ID}/licenseTerms" --upload-file template-update-license.json
1793+
1794+
The fully expanded example above (without environment variables) looks like this:
1795+
1796+
.. code-block:: bash
1797+
1798+
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
1799+
1800+
Update the Terms Of Access of a Template
1801+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1802+
1803+
Updates the terms of access of a template with a given ``id``.
1804+
1805+
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:
1806+
1807+
.. code-block:: bash
1808+
1809+
export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
1810+
export SERVER_URL=https://demo.dataverse.org
1811+
export ID=1
1812+
1813+
curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/dataverses/{ID}/access" --upload-file template-update-access.json
1814+
1815+
The fully expanded example above (without environment variables) looks like this:
1816+
1817+
.. code-block:: bash
1818+
1819+
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
1820+
17581821
Set a Default Template for a Collection
17591822
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17601823

src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ String getWrappedMessageWhenJson() {
242242

243243
@EJB
244244
GuestbookResponseServiceBean gbRespSvc;
245+
246+
@EJB
247+
TemplateServiceBean templateSvc;
245248

246249
@Inject
247250
FailedPIDResolutionLoggingServiceBean fprLogService;
@@ -370,8 +373,18 @@ protected Dataverse findDataverseOrDie( String dvIdtf ) throws WrappedResponse {
370373
}
371374
return dv;
372375
}
376+
377+
protected Template findTemplateOrDie(Long templateId) throws WrappedResponse {
378+
379+
Template template = templateSvc.find(templateId);
380+
if (template == null) {
381+
throw new WrappedResponse(
382+
error(Response.Status.NOT_FOUND, "Can't find template with identifier='" + templateId + "'"));
383+
}
384+
return template;
385+
}
373386

374-
protected Template findTemplateOrDie(Long templateId, Dataverse dataverse) throws WrappedResponse {
387+
protected Template findTemplateInDataverseOrParentsOrDie(Long templateId, Dataverse dataverse) throws WrappedResponse {
375388

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

src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
2727
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
2828
import edu.harvard.iq.dataverse.engine.command.impl.*;
29+
import edu.harvard.iq.dataverse.license.License;
2930
import edu.harvard.iq.dataverse.pidproviders.PidProvider;
3031
import edu.harvard.iq.dataverse.pidproviders.PidUtil;
3132
import edu.harvard.iq.dataverse.settings.JvmSettings;
@@ -72,6 +73,7 @@
7273
import jakarta.ws.rs.WebApplicationException;
7374
import jakarta.ws.rs.core.Context;
7475
import jakarta.ws.rs.core.HttpHeaders;
76+
import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
7577
import jakarta.ws.rs.core.StreamingOutput;
7678
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
7779
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
@@ -2031,9 +2033,9 @@ public Response getTemplate(@Context ContainerRequestContext crc, @PathParam("id
20312033
public Response createTemplate(@Context ContainerRequestContext crc, String body, @PathParam("identifier") String dvIdtf) {
20322034
try {
20332035
Dataverse dataverse = findDataverseOrDie(dvIdtf);
2034-
NewTemplateDTO newTemplateDTO;
2036+
TemplateDTO newTemplateDTO;
20352037
try {
2036-
newTemplateDTO = NewTemplateDTO.fromRequestBody(body, jsonParser());
2038+
newTemplateDTO = TemplateDTO.fromRequestBody(body, jsonParser());
20372039
} catch (JsonParseException ex) {
20382040
return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.createTemplate.error.jsonParseMetadataFields"), ex.getMessage()));
20392041
}
@@ -2045,6 +2047,118 @@ public Response createTemplate(@Context ContainerRequestContext crc, String body
20452047
return e.getResponse();
20462048
}
20472049
}
2050+
2051+
@PUT
2052+
@AuthRequired
2053+
@Path("{templateId}/metadata")
2054+
public Response updateTemplateMetadata(@Context ContainerRequestContext crc, String body, @PathParam("templateId") Long templateId, @QueryParam("replace") boolean replaceData) {
2055+
try {
2056+
Template template = findTemplateOrDie(templateId);
2057+
Dataverse dataverse = template.getDataverse();
2058+
boolean nameOnly = false;
2059+
JsonObject json = JsonUtil.getJsonObject(body);
2060+
2061+
/*
2062+
You can also set a new name for your template in the json
2063+
*/
2064+
if (json.containsKey("name") && !json.getString("name").isBlank()) {
2065+
template.setName(json.getString("name"));
2066+
nameOnly = true;
2067+
}
2068+
2069+
List<DatasetField> updatedFields = new ArrayList<>();
2070+
//if it doesn't contain fields, instructions or name it better have a single dataset field
2071+
//to be updated
2072+
if (json.getJsonArray("fields") == null) {
2073+
if (!json.containsKey("instructions") && !json.containsKey("name")) {
2074+
updatedFields.add(jsonParser().parseField(json, Boolean.FALSE, replaceData));
2075+
}
2076+
} else {
2077+
updatedFields = jsonParser().parseMultipleFields(json, replaceData);
2078+
}
2079+
2080+
Map<String, String> instructionsMap = jsonParser().parseRequestBodyInstructionsMap(json);
2081+
2082+
//if we're only updating the name then return the metadata and instructions to previous
2083+
nameOnly = nameOnly && updatedFields.isEmpty() && instructionsMap == null;
2084+
2085+
if (nameOnly) {
2086+
updatedFields = template.getDatasetFields();
2087+
instructionsMap = template.getInstructionsMap();
2088+
}
2089+
2090+
Template updated = execCommand(new UpdateTemplateFieldsCommand(template, dataverse, updatedFields, instructionsMap, replaceData, createDataverseRequest(getRequestUser(crc))));
2091+
2092+
return created("/dataverses/template/" + updated.getId(), jsonTemplate(updated));
2093+
} catch (JsonParseException ex) {
2094+
logger.log(Level.SEVERE, "Semantic error parsing dataset update Json: " + ex.getMessage(), ex);
2095+
return error(Response.Status.BAD_REQUEST, BundleUtil.getStringFromBundle("datasets.api.editMetadata.error.parseUpdate", List.of(ex.getMessage())));
2096+
2097+
} catch (WrappedResponse e) {
2098+
return e.getResponse();
2099+
}
2100+
}
2101+
2102+
@PUT
2103+
@AuthRequired
2104+
@Path("{templateId}/licenseTerms")
2105+
public Response updateTemplateLicenseTerms(@Context ContainerRequestContext crc, LicenseUpdateRequest requestBody, @PathParam("templateId") Long templateId, @QueryParam("replace") boolean replaceData) {
2106+
try {
2107+
Template template = findTemplateOrDie(templateId);
2108+
Dataverse dataverse = template.getDataverse();
2109+
2110+
if (requestBody.getName() != null && !requestBody.getName().isBlank()) {
2111+
String licenseName = requestBody.getName();
2112+
License license = licenseSvc.getByNameOrUri(licenseName);
2113+
if (license == null) {
2114+
return notFound(BundleUtil.getStringFromBundle("datasets.api.updateLicense.licenseNotFound", List.of(licenseName)));
2115+
}
2116+
2117+
execCommand(new UpdateTemplateLicenseCommand(createDataverseRequest(getRequestUser(crc)), template, dataverse, license));
2118+
return ok(BundleUtil.getStringFromBundle("dataverses.api.update.template.license.success"));
2119+
} else if (requestBody.getCustomTerms() != null) {
2120+
CustomTermsDTO customTerms = requestBody.getCustomTerms();
2121+
execCommand(new UpdateTemplateLicenseCommand(createDataverseRequest(getRequestUser(crc)), template, dataverse, customTerms.toTermsOfUseAndAccess()));
2122+
return ok(BundleUtil.getStringFromBundle("dataverses.api.update.template.license.success"));
2123+
} else {
2124+
return badRequest(BundleUtil.getStringFromBundle("datasets.api.updateLicense.licenseNameIsEmpty"));
2125+
}
2126+
2127+
} catch (WrappedResponse e) {
2128+
return e.getResponse();
2129+
}
2130+
}
2131+
2132+
@PUT
2133+
@AuthRequired
2134+
@Path("{templateId}/access")
2135+
public Response updateTemplateTermsOfAccess(@Context ContainerRequestContext crc, String jsonBody, @PathParam("templateId") Long templateId) {
2136+
try {
2137+
2138+
boolean publicInstall = settingsSvc.isTrueForKey(SettingsServiceBean.Key.PublicInstall, false);
2139+
2140+
Template template = findTemplateOrDie(templateId);
2141+
2142+
JsonObject json = JsonUtil.getJsonObject(jsonBody);
2143+
2144+
TermsOfUseAndAccess toua = jsonParser().parseTermsOfAccess(json);
2145+
2146+
if (publicInstall && (toua.isFileAccessRequest() || !toua.getTermsOfAccess().isEmpty())){
2147+
return error(BAD_REQUEST, "Setting File Access Request or Terms of Access is not permitted on a public installation.");
2148+
}
2149+
2150+
execCommand(new UpdateTemplateTermsOfAccessCommand(createDataverseRequest(getRequestUser(crc)), template, template.getDataverse(), toua ));
2151+
2152+
return ok(BundleUtil.getStringFromBundle("dataverses.api.update.template.access.success"));
2153+
2154+
} catch (JsonParseException ex) {
2155+
logger.log(Level.SEVERE, "Semantic error parsing template terms update Json: " + ex.getMessage(), ex);
2156+
return error(Response.Status.BAD_REQUEST, BundleUtil.getStringFromBundle("datasets.api.editMetadata.error.parseUpdate", List.of(ex.getMessage())));
2157+
} catch (WrappedResponse ex) {
2158+
logger.log(Level.SEVERE, "Update terms of access error: " + ex.getMessage(), ex);
2159+
return ex.getResponse();
2160+
}
2161+
}
20482162

20492163
@POST
20502164
@AuthRequired
@@ -2056,7 +2170,7 @@ public Response setDefaultTemplate(@Context ContainerRequestContext crc,
20562170
try {
20572171

20582172
Dataverse dataverse = findDataverseOrDie(dvId);
2059-
Template template = findTemplateOrDie(templateId, dataverse);
2173+
Template template = findTemplateInDataverseOrParentsOrDie(templateId, dataverse);
20602174
DataverseRequest dvReq = createDataverseRequest(getRequestUser(crc));
20612175
SetDefaultTemplateCommand command = new SetDefaultTemplateCommand(template, dvReq, dataverse);
20622176

src/main/java/edu/harvard/iq/dataverse/api/dto/NewTemplateDTO.java renamed to src/main/java/edu/harvard/iq/dataverse/api/dto/TemplateDTO.java

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,24 @@
99
import java.sql.Timestamp;
1010
import java.util.*;
1111

12-
public class NewTemplateDTO {
12+
public class TemplateDTO {
1313

1414
private String name;
1515
private List<DatasetField> datasetFields;
1616
private Map<String, String> instructionsMap;
1717
private boolean isDefault;
1818

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

2222
JsonObject jsonObject = JsonUtil.getJsonObject(requestBody);
2323

24-
newTemplateDTO.name = jsonObject.getString("name");
25-
newTemplateDTO.datasetFields = jsonParser.parseMultipleFields(jsonObject);
26-
newTemplateDTO.instructionsMap = parseRequestBodyInstructionsMap(jsonObject);
27-
newTemplateDTO.isDefault = jsonObject.getBoolean("isDefault", false);
28-
29-
return newTemplateDTO;
24+
templateDTO.name = jsonObject.getString("name");
25+
templateDTO.datasetFields = jsonParser.parseMultipleFields(jsonObject);
26+
templateDTO.instructionsMap = jsonParser.parseRequestBodyInstructionsMap(jsonObject);
27+
templateDTO.isDefault = jsonObject.getBoolean("isDefault", false);
28+
29+
return templateDTO;
3030
}
3131

3232
public Template toTemplate() {
@@ -59,18 +59,4 @@ public boolean isDefault() {
5959
return isDefault;
6060
}
6161

62-
private static Map<String, String> parseRequestBodyInstructionsMap(JsonObject jsonObject) {
63-
Map<String, String> instructionsMap = new HashMap<>();
64-
JsonArray instructionsJsonArray = jsonObject.getJsonArray("instructions");
65-
if (instructionsJsonArray == null) {
66-
return null;
67-
}
68-
for (JsonObject instructionJsonObject : instructionsJsonArray.getValuesAs(JsonObject.class)) {
69-
instructionsMap.put(
70-
instructionJsonObject.getString("instructionField"),
71-
instructionJsonObject.getString("instructionText")
72-
);
73-
}
74-
return instructionsMap;
75-
}
7662
}

0 commit comments

Comments
 (0)