diff --git a/doc/release-notes/11562-templates-api.md b/doc/release-notes/11562-templates-api.md new file mode 100644 index 00000000000..30e35687a33 --- /dev/null +++ b/doc/release-notes/11562-templates-api.md @@ -0,0 +1,4 @@ +New endpoints have been implemented in the Dataverses API for the management of dataverse templates: + +- POST `/dataverses/{id}/templates`: Creates a template for a given Dataverse collection ``id``. +- GET `/dataverses/{id}/templates`: Lists the templates for a given Dataverse collection ``id``. diff --git a/doc/sphinx-guides/source/_static/api/dataverse-template.json b/doc/sphinx-guides/source/_static/api/dataverse-template.json new file mode 100644 index 00000000000..373d3edefa7 --- /dev/null +++ b/doc/sphinx-guides/source/_static/api/dataverse-template.json @@ -0,0 +1,27 @@ +{ + "name": "Dataverse template", + "isDefault": true, + "fields": [ + { + "typeName": "author", + "value": [ + { + "authorName": { + "typeName": "authorName", + "value": "Belicheck, Bill" + }, + "authorAffiliation": { + "typeName": "authorIdentifierScheme", + "value": "ORCID" + } + } + ] + } + ], + "instructions": [ + { + "instructionField": "author", + "instructionText": "The author data" + } + ] +} diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 293fc94638d..da93d357534 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -1400,6 +1400,46 @@ The fully expanded example above (without environment variables) looks like this curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X GET "https://demo.dataverse.org/api/access/dataverseFeaturedItemImage/1" +List Templates of a Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Lists the templates for a given Dataverse collection ``id``: + +.. 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 GET "$SERVER_URL/api/dataverses/{ID}/templates" + +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 GET "https://demo.dataverse.org/api/dataverses/1/templates" + +Create a Template for a Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Creates a template for a given Dataverse collection ``id``. + +To create the template, you must send a JSON file. Your JSON file might look like :download:`dataverse-template.json <../_static/api/dataverse-template.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 POST "$SERVER_URL/api/dataverses/{ID}/templates" --upload-file dataverse-template.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 POST "https://demo.dataverse.org/api/dataverses/1/templates" --upload-file dataverse-template.json + Datasets -------- diff --git a/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java b/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java index 029732591ee..4d6d59cb013 100644 --- a/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java +++ b/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java @@ -6,6 +6,7 @@ import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean; import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUserServiceBean; import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItemServiceBean; +import edu.harvard.iq.dataverse.license.LicenseServiceBean; import edu.harvard.iq.dataverse.util.cache.CacheFactoryBean; import edu.harvard.iq.dataverse.engine.DataverseEngine; import edu.harvard.iq.dataverse.authorization.Permission; @@ -192,6 +193,9 @@ public class EjbDataverseEngine { @EJB DataverseFeaturedItemServiceBean dataverseFeaturedItemServiceBean; + @EJB + LicenseServiceBean licenseServiceBean; + @EJB DatasetFieldsValidator datasetFieldsValidator; @@ -541,6 +545,11 @@ public DatasetFieldsValidator datasetFieldsValidator() { return datasetFieldsValidator; } + @Override + public LicenseServiceBean licenses() { + return licenseServiceBean; + } + @Override public StorageUseServiceBean storageUse() { return storageUseService; diff --git a/src/main/java/edu/harvard/iq/dataverse/Template.java b/src/main/java/edu/harvard/iq/dataverse/Template.java index 05c6df51197..2768cf417a4 100644 --- a/src/main/java/edu/harvard/iq/dataverse/Template.java +++ b/src/main/java/edu/harvard/iq/dataverse/Template.java @@ -137,7 +137,11 @@ public List getDatasetFields() { @Transient private Map instructionsMap = null; - + + public void setInstructionsMap(Map instructionsMap) { + this.instructionsMap = instructionsMap; + } + @Transient private TreeMap> metadataBlocksForView = new TreeMap<>(); @Transient diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java index b1d56b6b8a9..b2ae97a0c7f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -1931,4 +1931,34 @@ public Response deleteFeaturedItems(@Context ContainerRequestContext crc, @PathP return e.getResponse(); } } + + @GET + @AuthRequired + @Path("{identifier}/templates") + public Response getTemplates(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) { + try { + Dataverse dataverse = findDataverseOrDie(dvIdtf); + return ok(jsonTemplates(execCommand(new ListDataverseTemplatesCommand(createDataverseRequest(getRequestUser(crc)), dataverse)))); + } catch (WrappedResponse e) { + return e.getResponse(); + } + } + + @POST + @AuthRequired + @Path("{identifier}/templates") + public Response createTemplate(@Context ContainerRequestContext crc, String body, @PathParam("identifier") String dvIdtf) { + try { + Dataverse dataverse = findDataverseOrDie(dvIdtf); + NewTemplateDTO newTemplateDTO; + try { + newTemplateDTO = NewTemplateDTO.fromRequestBody(body, jsonParser()); + } catch (JsonParseException ex) { + return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.createTemplate.error.jsonParseMetadataFields"), ex.getMessage())); + } + return ok(jsonTemplate(execCommand(new CreateTemplateCommand(newTemplateDTO.toTemplate(), createDataverseRequest(getRequestUser(crc)), dataverse, true)))); + } catch (WrappedResponse e) { + return e.getResponse(); + } + } } diff --git a/src/main/java/edu/harvard/iq/dataverse/api/dto/NewTemplateDTO.java b/src/main/java/edu/harvard/iq/dataverse/api/dto/NewTemplateDTO.java new file mode 100644 index 00000000000..28e21ffc6c0 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/api/dto/NewTemplateDTO.java @@ -0,0 +1,75 @@ +package edu.harvard.iq.dataverse.api.dto; + +import edu.harvard.iq.dataverse.*; +import edu.harvard.iq.dataverse.util.json.JsonParseException; +import edu.harvard.iq.dataverse.util.json.JsonParser; +import edu.harvard.iq.dataverse.util.json.JsonUtil; +import jakarta.json.*; + +import java.sql.Timestamp; +import java.util.*; + +public class NewTemplateDTO { + + private String name; + private List datasetFields; + private Map instructionsMap; + private boolean isDefault; + + public static NewTemplateDTO fromRequestBody(String requestBody, JsonParser jsonParser) throws JsonParseException { + NewTemplateDTO newTemplateDTO = new NewTemplateDTO(); + + 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; + } + + public Template toTemplate() { + Template template = new Template(); + + template.setDatasetFields(getDatasetFields()); + template.setName(getName()); + template.setInstructionsMap(getInstructionsMap()); + template.updateInstructions(); + template.setCreateTime(new Timestamp(new Date().getTime())); + template.setUsageCount(0L); + + return template; + } + + public String getName() { + return name; + } + + public List getDatasetFields() { + return datasetFields; + } + + public Map getInstructionsMap() { + return instructionsMap; + } + + public boolean isDefault() { + return isDefault; + } + + private static Map parseRequestBodyInstructionsMap(JsonObject jsonObject) { + Map 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; + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/CommandContext.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/CommandContext.java index f7a268abf64..1945d44cd78 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/CommandContext.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/CommandContext.java @@ -4,6 +4,7 @@ import edu.harvard.iq.dataverse.dataset.DatasetFieldsValidator; import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUserServiceBean; import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItemServiceBean; +import edu.harvard.iq.dataverse.license.LicenseServiceBean; import edu.harvard.iq.dataverse.search.IndexServiceBean; import edu.harvard.iq.dataverse.search.SearchService; import edu.harvard.iq.dataverse.search.SearchServiceFactory; @@ -140,4 +141,6 @@ public interface CommandContext { public DataverseFeaturedItemServiceBean dataverseFeaturedItems(); public DatasetFieldsValidator datasetFieldsValidator(); + + public LicenseServiceBean licenses(); } diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreateTemplateCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreateTemplateCommand.java index 03177ee8c1f..32c63e55cb4 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreateTemplateCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreateTemplateCommand.java @@ -1,7 +1,6 @@ - package edu.harvard.iq.dataverse.engine.command.impl; -import edu.harvard.iq.dataverse.Dataverse; -import edu.harvard.iq.dataverse.Template; + +import edu.harvard.iq.dataverse.*; import edu.harvard.iq.dataverse.authorization.Permission; import edu.harvard.iq.dataverse.engine.command.AbstractCommand; @@ -9,25 +8,81 @@ import edu.harvard.iq.dataverse.engine.command.DataverseRequest; import edu.harvard.iq.dataverse.engine.command.RequiredPermissions; import edu.harvard.iq.dataverse.engine.command.exception.CommandException; +import edu.harvard.iq.dataverse.settings.JvmSettings; +import edu.harvard.iq.dataverse.util.DatasetFieldUtil; + +import java.util.ArrayList; +import java.util.List; + /** - * * @author skraffmiller + * Creates a template {@link Template} for a {@link Dataverse}. */ -@RequiredPermissions( Permission.EditDataverse ) +@RequiredPermissions(Permission.EditDataverse) public class CreateTemplateCommand extends AbstractCommand