From 112c2337ffdbec801caee1e78dec1d697fad87d7 Mon Sep 17 00:00:00 2001 From: Aday Bujeda Date: Fri, 22 Jul 2022 23:18:21 +0100 Subject: [PATCH 1/6] Added new static facet to show metadata blocks with values --- .../8535-metadata-types-static-facet.md | 6 + doc/sphinx-guides/source/api/native-api.rst | 40 ++++ .../edu/harvard/iq/dataverse/Dataverse.java | 27 ++- .../DataverseMetadataBlockFacet.java | 82 ++++++++ .../harvard/iq/dataverse/MetadataBlock.java | 14 +- .../harvard/iq/dataverse/api/Dataverses.java | 54 +++++ .../dto/DataverseMetadataBlockFacetDTO.java | 56 ++++++ .../impl/ListMetadataBlockFacetsCommand.java | 40 ++++ .../iq/dataverse/search/IndexServiceBean.java | 11 ++ .../iq/dataverse/search/SearchFields.java | 1 + .../search/SearchIncludeFragment.java | 12 ++ .../dataverse/search/SearchServiceBean.java | 21 +- src/main/java/propertyFiles/Bundle.properties | 11 -- .../propertyFiles/astrophysics.properties | 1 + .../java/propertyFiles/biomedical.properties | 1 + .../java/propertyFiles/citation.properties | 1 + .../java/propertyFiles/customARCS.properties | 1 + .../java/propertyFiles/customCHIA.properties | 1 + .../propertyFiles/customDigaai.properties | 3 +- .../java/propertyFiles/customGSD.properties | 1 + .../java/propertyFiles/customMRA.properties | 1 + .../java/propertyFiles/customPSI.properties | 1 + .../java/propertyFiles/customPSRI.properties | 1 + .../propertyFiles/custom_hbgdki.properties | 1 + .../java/propertyFiles/geospatial.properties | 3 +- .../java/propertyFiles/journal.properties | 1 + .../propertyFiles/socialscience.properties | 1 + .../staticSearchFields.properties | 11 ++ .../V5.11.0.2__8536-metadata-block-facet.sql | 11 ++ .../DataverseMetadataBlockFacetTest.java | 45 +++++ .../harvard/iq/dataverse/DataverseTest.java | 65 +++++++ .../iq/dataverse/MetadataBlockTest.java | 79 ++++++++ .../iq/dataverse/api/DataversesTest.java | 184 ++++++++++++++++++ .../impl/ListMetadataBlocksCommandTest.java | 66 +++++++ .../search/SearchIncludeFragmentTest.java | 126 ++++++++++++ .../metadataBlockTest.properties | 2 + 36 files changed, 961 insertions(+), 21 deletions(-) create mode 100644 doc/release-notes/8535-metadata-types-static-facet.md create mode 100644 src/main/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacet.java create mode 100644 src/main/java/edu/harvard/iq/dataverse/api/dto/DataverseMetadataBlockFacetDTO.java create mode 100644 src/main/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlockFacetsCommand.java create mode 100644 src/main/java/propertyFiles/staticSearchFields.properties create mode 100644 src/main/resources/db/migration/V5.11.0.2__8536-metadata-block-facet.sql create mode 100644 src/test/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacetTest.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/DataverseTest.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/MetadataBlockTest.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlocksCommandTest.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/search/SearchIncludeFragmentTest.java create mode 100644 src/test/resources/propertyFiles/metadataBlockTest.properties diff --git a/doc/release-notes/8535-metadata-types-static-facet.md b/doc/release-notes/8535-metadata-types-static-facet.md new file mode 100644 index 00000000000..023000c0977 --- /dev/null +++ b/doc/release-notes/8535-metadata-types-static-facet.md @@ -0,0 +1,6 @@ +## Adding new static search facet: Metadata Types +A new static search facet has been added to the search side panel. This new facet is called "Metadata Types" and is driven from metadata blocks. When a metadata field value is inserted into a dataset, an entry for the metadata block it belongs to is added to this new facet. + +This new facet needs to be configured for it to appear on the search side panel. The configuration assigns to a dataverse what metadata blocks to show. The configuration is inherited by child dataverses. + +To configure the new facet, use the Metadata Block Facet API: \ No newline at end of file diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 75eb7b5424e..85e7fa4fe55 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -229,6 +229,46 @@ The fully expanded example above (without environment variables) looks like this Where ``facets.json`` contains a JSON encoded list of metadata keys (e.g. ``["authorName","authorAffiliation"]``). +List Metadata Block Facets Configured for a Dataverse Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|CORS| List the metadata block facet configuration with all the metadata block configured 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=root + + curl -H X-Dataverse-key:$API_TOKEN $SERVER_URL/api/dataverses/$ID/metadatablockfacets + +The fully expanded example above (without environment variables) looks like this: + +.. code-block:: bash + + curl -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx https://demo.dataverse.org/api/dataverses/root/metadatablockfacets + +Set Metadata Block Facets for a Dataverse Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Set the ``metadataBlockFacetRoot`` to ``true`` and assign the metadata blocks that will appear in the ``Metadata Types`` facet category for a given Dataverse collection identified by ``id``: + +.. code-block:: bash + + export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + export SERVER_URL=https://demo.dataverse.org + export ID=root + + curl -H X-Dataverse-key:$API_TOKEN" -X POST $SERVER_URL/api/dataverses/$ID/metadatablockfacets --upload-file metadata_blocks.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/root/metadatablockfacets --upload-file metadata_blocks.json + +Where ``metadata_blocks.json`` contains a JSON encoded list of metadata block names (e.g. ``["socialscience","geospatial"]``). + Create a New Role in a Dataverse Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/main/java/edu/harvard/iq/dataverse/Dataverse.java b/src/main/java/edu/harvard/iq/dataverse/Dataverse.java index db5f9d172cd..bc8716b6129 100644 --- a/src/main/java/edu/harvard/iq/dataverse/Dataverse.java +++ b/src/main/java/edu/harvard/iq/dataverse/Dataverse.java @@ -324,8 +324,31 @@ public boolean isHarvested() { return harvestingClient != null; } */ - - + private boolean metadataBlockFacetRoot; + + public boolean isMetadataBlockFacetRoot() { + return metadataBlockFacetRoot; + } + + public void setMetadataBlockFacetRoot(boolean metadataBlockFacetRoot) { + this.metadataBlockFacetRoot = metadataBlockFacetRoot; + } + + @OneToMany(mappedBy = "dataverse",cascade={ CascadeType.REMOVE, CascadeType.MERGE,CascadeType.PERSIST }, orphanRemoval=true) + private List metadataBlockFacets = new ArrayList<>(); + + public List getMetadataBlockFacets() { + if (isMetadataBlockFacetRoot() || getOwner() == null) { + return metadataBlockFacets; + } else { + return getOwner().getMetadataBlockFacets(); + } + } + + public void setMetadataBlockFacets(List metadataBlockFacets) { + this.metadataBlockFacets = metadataBlockFacets; + } + public List getParentGuestbooks() { List retList = new ArrayList<>(); Dataverse testDV = this; diff --git a/src/main/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacet.java b/src/main/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacet.java new file mode 100644 index 00000000000..a2659b81974 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacet.java @@ -0,0 +1,82 @@ +package edu.harvard.iq.dataverse; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Objects; + +/** + * + * @author adaybujeda + */ +@Entity +@Table(indexes = {@Index(columnList="dataverse_id") + , @Index(columnList="metadatablock_id")}) +public class DataverseMetadataBlockFacet implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "dataverse_id") + private Dataverse dataverse; + + @ManyToOne + @JoinColumn(name = "metadatablock_id") + private MetadataBlock metadataBlock; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Dataverse getDataverse() { + return dataverse; + } + + public void setDataverse(Dataverse dataverse) { + this.dataverse = dataverse; + } + + public MetadataBlock getMetadataBlock() { + return metadataBlock; + } + + public void setMetadataBlock(MetadataBlock metadataBlock) { + this.metadataBlock = metadataBlock; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (this.id != null ? this.id.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof DataverseMetadataBlockFacet)) { + return false; + } + DataverseMetadataBlockFacet other = (DataverseMetadataBlockFacet) object; + return !(!Objects.equals(this.id, other.id) && (this.id == null || !this.id.equals(other.id))); + } + + @Override + public String toString() { + return String.format("edu.harvard.iq.dataverse.DataverseMetadataBlockFacet[ id=%s ]", id); + } + +} + diff --git a/src/main/java/edu/harvard/iq/dataverse/MetadataBlock.java b/src/main/java/edu/harvard/iq/dataverse/MetadataBlock.java index 844c0ec5be7..33e75efffb5 100644 --- a/src/main/java/edu/harvard/iq/dataverse/MetadataBlock.java +++ b/src/main/java/edu/harvard/iq/dataverse/MetadataBlock.java @@ -202,10 +202,18 @@ public String toString() { return "edu.harvard.iq.dataverse.MetadataBlock[ id=" + id + " ]"; } - public String getLocaleDisplayName() - { + public String getLocaleDisplayName() { + return getLocaleValue("metadatablock.displayName"); + } + + public String getLocaleDisplayFacet() { + return getLocaleValue("metadatablock.displayFacet"); + } + + // Visible for testing + String getLocaleValue(String metadataBlockKey) { try { - return BundleUtil.getStringFromPropertyFile("metadatablock.displayName", getName()); + return BundleUtil.getStringFromPropertyFile(metadataBlockKey, getName()); } catch (MissingResourceException e) { return displayName; } 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 d15b0f1c48f..54cd3869a47 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -7,8 +7,10 @@ import edu.harvard.iq.dataverse.Dataverse; import edu.harvard.iq.dataverse.DataverseFacet; import edu.harvard.iq.dataverse.DataverseContact; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; import edu.harvard.iq.dataverse.DataverseServiceBean; import edu.harvard.iq.dataverse.api.datadeposit.SwordServiceBean; +import edu.harvard.iq.dataverse.api.dto.DataverseMetadataBlockFacetDTO; import edu.harvard.iq.dataverse.authorization.DataverseRole; import edu.harvard.iq.dataverse.DvObject; import edu.harvard.iq.dataverse.DvObjectContainer; @@ -49,6 +51,7 @@ import edu.harvard.iq.dataverse.engine.command.impl.ListDataverseContentCommand; import edu.harvard.iq.dataverse.engine.command.impl.ListExplicitGroupsCommand; import edu.harvard.iq.dataverse.engine.command.impl.ListFacetsCommand; +import edu.harvard.iq.dataverse.engine.command.impl.ListMetadataBlockFacetsCommand; import edu.harvard.iq.dataverse.engine.command.impl.ListMetadataBlocksCommand; import edu.harvard.iq.dataverse.engine.command.impl.ListRoleAssignments; import edu.harvard.iq.dataverse.engine.command.impl.ListRolesCommand; @@ -117,6 +120,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; @@ -713,6 +717,56 @@ public Response setFacets(@PathParam("identifier") String dvIdtf, String facetId } } + @GET + @Path("{identifier}/metadatablockfacets") + @Produces(MediaType.APPLICATION_JSON) + public Response listMetadataBlockFacets(@PathParam("identifier") String dvIdtf) { + try { + User u = findUserOrDie(); + DataverseRequest request = createDataverseRequest(u); + Dataverse dataverse = findDataverseOrDie(dvIdtf); + List metadataBlockFacets = Optional.ofNullable(execCommand(new ListMetadataBlockFacetsCommand(request, dataverse))).orElse(Collections.emptyList()); + List metadataBlocksDTOs = metadataBlockFacets.stream() + .map(item -> new DataverseMetadataBlockFacetDTO.MetadataBlockDTO(item.getMetadataBlock().getName(), item.getMetadataBlock().getLocaleDisplayFacet())) + .collect(Collectors.toList()); + DataverseMetadataBlockFacetDTO response = new DataverseMetadataBlockFacetDTO(dataverse.getId(), dataverse.getAlias(), dataverse.isMetadataBlockFacetRoot(), metadataBlocksDTOs); + return Response.ok(response).build(); + } catch (WrappedResponse e) { + return e.getResponse(); + } + } + + @POST + @Path("{identifier}/metadatablockfacets") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response setMetadataBlockFacets(@PathParam("identifier") String dvIdtf, List metadataBlockNames) { + try { + Dataverse dataverse = findDataverseOrDie(dvIdtf); + + List metadataBlockFacets = new LinkedList<>(); + for(String metadataBlockName: metadataBlockNames) { + MetadataBlock metadataBlock = findMetadataBlock(metadataBlockName); + if (metadataBlock == null) { + return error(Response.Status.BAD_REQUEST, String.format("Invalid metadata block name: %s", metadataBlockName)); + } + + DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setDataverse(dataverse); + metadataBlockFacet.setMetadataBlock(metadataBlock); + metadataBlockFacets.add(metadataBlockFacet); + } + + dataverse.setMetadataBlockFacetRoot(true); + dataverse.setMetadataBlockFacets(metadataBlockFacets); + execCommand(new UpdateDataverseCommand(dataverse, null, null, createDataverseRequest(findUserOrDie()), null)); + return ok(String.format("Metadata block facets updated. DataverseId: %s blocks: %s", dvIdtf, metadataBlockNames)); + + } catch (WrappedResponse ex) { + return ex.getResponse(); + } + } + // FIXME: This listContent method is way too optimistic, always returning "ok" and never "error". // TODO: Investigate why there was a change in the timeframe of when pull request #4350 was merged // (2438-4295-dois-for-files branch) such that a contributor API token no longer allows this method diff --git a/src/main/java/edu/harvard/iq/dataverse/api/dto/DataverseMetadataBlockFacetDTO.java b/src/main/java/edu/harvard/iq/dataverse/api/dto/DataverseMetadataBlockFacetDTO.java new file mode 100644 index 00000000000..65b6f0ff58f --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/api/dto/DataverseMetadataBlockFacetDTO.java @@ -0,0 +1,56 @@ +package edu.harvard.iq.dataverse.api.dto; + +import java.util.List; + +/** + * + * @author adaybujeda + */ +public class DataverseMetadataBlockFacetDTO { + + private Long dataverseId; + private String dataverseAlias; + private boolean isMetadataBlockFacetRoot; + private List metadataBlocks; + + public DataverseMetadataBlockFacetDTO(Long dataverseId, String dataverseAlias, boolean isMetadataBlockFacetRoot, List metadataBlocks) { + this.dataverseId = dataverseId; + this.dataverseAlias = dataverseAlias; + this.isMetadataBlockFacetRoot = isMetadataBlockFacetRoot; + this.metadataBlocks = metadataBlocks; + } + + public Long getDataverseId() { + return dataverseId; + } + + public String getDataverseAlias() { + return dataverseAlias; + } + + public boolean isMetadataBlockFacetRoot() { + return isMetadataBlockFacetRoot; + } + + public List getMetadataBlocks() { + return metadataBlocks; + } + + public static class MetadataBlockDTO { + private String metadataBlockName; + private String metadataBlockFacet; + + public MetadataBlockDTO(String metadataBlockName, String metadataBlockFacet) { + this.metadataBlockName = metadataBlockName; + this.metadataBlockFacet = metadataBlockFacet; + } + + public String getMetadataBlockName() { + return metadataBlockName; + } + + public String getMetadataBlockFacet() { + return metadataBlockFacet; + } + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlockFacetsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlockFacetsCommand.java new file mode 100644 index 00000000000..abc444dc538 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlockFacetsCommand.java @@ -0,0 +1,40 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.authorization.Permission; +import edu.harvard.iq.dataverse.engine.command.AbstractCommand; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +import edu.harvard.iq.dataverse.engine.command.DataverseRequest; +import edu.harvard.iq.dataverse.engine.command.exception.CommandException; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * + * @author adaybujeda + */ +public class ListMetadataBlockFacetsCommand extends AbstractCommand> { + + private final Dataverse dv; + + public ListMetadataBlockFacetsCommand(DataverseRequest aRequest, Dataverse aDataverse) { + super(aRequest, aDataverse); + dv = aDataverse; + } + + @Override + public List execute(CommandContext ctxt) throws CommandException { + return dv.getMetadataBlockFacets(); + } + + @Override + public Map> getRequiredPermissions() { + return Collections.singletonMap("", + dv.isReleased() ? Collections.emptySet() + : Collections.singleton(Permission.ViewUnpublishedDataverse)); + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index bd66e822c20..484e5768eb1 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Future; import java.util.function.Function; @@ -816,6 +817,7 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set langs = settingsService.getConfiguredLanguages(); Map cvocMap = datasetFieldService.getCVocConf(false); + Set metadataBlocksWithValue = new HashSet<>(); for (DatasetField dsf : datasetVersion.getFlatDatasetFields()) { DatasetFieldType dsfType = dsf.getDatasetFieldType(); @@ -823,6 +825,11 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set dataversePaths = retrieveDVOPaths(dataset); diff --git a/src/main/java/edu/harvard/iq/dataverse/search/SearchFields.java b/src/main/java/edu/harvard/iq/dataverse/search/SearchFields.java index 63b5a777b0e..2e75a81ed5f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/SearchFields.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/SearchFields.java @@ -206,6 +206,7 @@ public class SearchFields { * A dataverse, a dataset, or a file. */ public static final String TYPE = "dvObjectType"; + public static final String METADATA_TYPES = "metadata_type_ss"; public static final String NAME_SORT = "nameSort"; // PUBLICATION_YEAR used to be called PUBLICATION_DATE. public static final String PUBLICATION_YEAR = "publicationDate"; diff --git a/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java b/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java index 6da4960679d..9bb83c88add 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java @@ -32,6 +32,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.logging.Logger; import javax.ejb.EJB; @@ -1150,9 +1151,20 @@ public List getFriendlyNamesFromFilterQuery(String filterQuery) { friendlyNames.add(key); } } + String noLeadingQuote = value.replaceAll("^\"", ""); String noTrailingQuote = noLeadingQuote.replaceAll("\"$", ""); String valueWithoutQuotes = noTrailingQuote; + + if (key.equals(SearchFields.METADATA_TYPES) && getDataverse() != null && getDataverse().getMetadataBlockFacets() != null) { + Optional friendlyName = getDataverse().getMetadataBlockFacets().stream().filter(block -> block.getMetadataBlock().getName().equals(valueWithoutQuotes)).findFirst().map(block -> block.getMetadataBlock().getLocaleDisplayFacet()); + logger.fine(String.format("action=getFriendlyNamesFromFilterQuery key=%s value=%s friendlyName=%s", key, value, friendlyName)); + if(friendlyName.isPresent()) { + friendlyNames.add(friendlyName.get()); + return friendlyNames; + } + } + friendlyNames.add(valueWithoutQuotes); return friendlyNames; } diff --git a/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java index 8dc367ec5c9..ca158198204 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java @@ -7,6 +7,7 @@ import edu.harvard.iq.dataverse.DatasetVersionServiceBean; import edu.harvard.iq.dataverse.Dataverse; import edu.harvard.iq.dataverse.DataverseFacet; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; import edu.harvard.iq.dataverse.DvObjectServiceBean; import edu.harvard.iq.dataverse.authorization.groups.Group; import edu.harvard.iq.dataverse.authorization.groups.GroupServiceBean; @@ -26,9 +27,11 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.MissingResourceException; import java.util.logging.Level; @@ -207,6 +210,7 @@ public SolrQueryResponse search(DataverseRequest dataverseRequest, List metadataBlockFacets = new LinkedList<>(); //I'm not sure if just adding null here is good for hte permissions system... i think it needs something if(dataverses != null) { for(Dataverse dataverse : dataverses) { @@ -244,6 +249,8 @@ public SolrQueryResponse search(DataverseRequest dataverseRequest, List 0) { if(metadataBlockName.length() > 0 ) { localefriendlyName = getLocaleTitle(datasetFieldName,facetFieldCount.getName(), metadataBlockName); - } else { + } else if (facetField.getName().equals(SearchFields.METADATA_TYPES)) { + Optional metadataBlockFacet = metadataBlockFacets.stream().filter(blockFacet -> blockFacet.getMetadataBlock().getName().equals(facetFieldCount.getName())).findFirst(); + if (metadataBlockFacet.isEmpty()) { + // metadata block facet is not configured to be displayed => ignore + continue; + } + + localefriendlyName = metadataBlockFacet.get().getMetadataBlock().getLocaleDisplayFacet(); + } else { try { localefriendlyName = BundleUtil.getStringFromPropertyFile(facetFieldCount.getName(), "Bundle"); } catch (Exception e) { @@ -694,7 +709,7 @@ public SolrQueryResponse search(DataverseRequest dataverseRequest, List 0) { facetCategory.setFriendlyName(friendlyName); } else { @@ -749,7 +764,7 @@ public SolrQueryResponse search(DataverseRequest dataverseRequest, List 0) { - FacetLabel facetLabel = new FacetLabel(start + "-" + end, new Long(rangeFacetCount.getCount())); + FacetLabel facetLabel = new FacetLabel(start + "-" + end, Long.valueOf(rangeFacetCount.getCount())); // special [12 TO 34] syntax for range facets facetLabel.setFilterQuery(rangeFacet.getName() + ":" + "[" + start + " TO " + end + "]"); facetLabelList.add(facetLabel); diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index 5b6216aaff1..f954ec99024 100644 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -2739,17 +2739,6 @@ jsonparser.error.parsing.number=Error parsing number: {0} #ConfigureFragmentBean.java configurefragmentbean.apiTokenGenerated=API Token will be generated. Please keep it secure as you would do with a password. -#FacetCategory - staticSearchFields -staticSearchFields.dvCategory=Dataverse Category -staticSearchFields.metadataSource=Metadata Source -staticSearchFields.publicationDate=Publication Year -staticSearchFields.fileTypeGroupFacet=File Type -staticSearchFields.dvObjectType=Type -staticSearchFields.fileTag=File Tag -staticSearchFields.fileAccess=Access -staticSearchFields.publicationStatus=Publication Status -staticSearchFields.subject_ss=Subject - #dataverse category - Facet Labels Researcher=Researcher Research\u0020Project=Research Project diff --git a/src/main/java/propertyFiles/astrophysics.properties b/src/main/java/propertyFiles/astrophysics.properties index be81ccdc883..a49b8b66510 100644 --- a/src/main/java/propertyFiles/astrophysics.properties +++ b/src/main/java/propertyFiles/astrophysics.properties @@ -1,5 +1,6 @@ metadatablock.name=astrophysics metadatablock.displayName=Astronomy and Astrophysics Metadata +metadatablock.displayFacet=Astronomy and Astrophysics datasetfieldtype.astroType.title=Type datasetfieldtype.astroFacility.title=Facility datasetfieldtype.astroInstrument.title=Instrument diff --git a/src/main/java/propertyFiles/biomedical.properties b/src/main/java/propertyFiles/biomedical.properties index 723a4ac2f40..1bffed2ee03 100644 --- a/src/main/java/propertyFiles/biomedical.properties +++ b/src/main/java/propertyFiles/biomedical.properties @@ -1,5 +1,6 @@ metadatablock.name=biomedical metadatablock.displayName=Life Sciences Metadata +metadatablock.displayFacet=Life Sciences datasetfieldtype.studyDesignType.title=Design Type datasetfieldtype.studyOtherDesignType.title=Other Design Type datasetfieldtype.studyFactorType.title=Factor Type diff --git a/src/main/java/propertyFiles/citation.properties b/src/main/java/propertyFiles/citation.properties index b69a8e1549c..668542c92be 100644 --- a/src/main/java/propertyFiles/citation.properties +++ b/src/main/java/propertyFiles/citation.properties @@ -1,5 +1,6 @@ metadatablock.name=citation metadatablock.displayName=Citation Metadata +metadatablock.displayFacet=Citation datasetfieldtype.title.title=Title datasetfieldtype.subtitle.title=Subtitle datasetfieldtype.alternativeTitle.title=Alternative Title diff --git a/src/main/java/propertyFiles/customARCS.properties b/src/main/java/propertyFiles/customARCS.properties index e6665b94e64..8a19405208a 100644 --- a/src/main/java/propertyFiles/customARCS.properties +++ b/src/main/java/propertyFiles/customARCS.properties @@ -1,5 +1,6 @@ metadatablock.name=customARCS metadatablock.displayName=Alliance for Research on Corporate Sustainability Metadata +metadatablock.displayFacet=Alliance for Research on Corporate Sustainability datasetfieldtype.ARCS1.title=1) Were any of these data sets a) purchased, b) obtained through licensed databases, or c) provided by an organization under a nondisclosure or other agreement? datasetfieldtype.ARCS2.title=2) If you responded Yes to Q1, have you ensured that sharing the data does not violate terms of the agreement? If you responded No to Q1, please enter N/A here. datasetfieldtype.ARCS3.title=3) Do any of these data sets include individual-level data (either collected or pre-existing in the dataset) that might make them subject to U.S. or international human subjects considerations? diff --git a/src/main/java/propertyFiles/customCHIA.properties b/src/main/java/propertyFiles/customCHIA.properties index 0b05e388cee..0d59493da96 100644 --- a/src/main/java/propertyFiles/customCHIA.properties +++ b/src/main/java/propertyFiles/customCHIA.properties @@ -1,5 +1,6 @@ metadatablock.name=customCHIA metadatablock.displayName=CHIA Metadata +metadatablock.displayFacet=CHIA datasetfieldtype.sourceCHIA.title=Source datasetfieldtype.datesAdditionalInformationCHIA.title=Dates - Additional Information datasetfieldtype.variablesCHIA.title=Variables diff --git a/src/main/java/propertyFiles/customDigaai.properties b/src/main/java/propertyFiles/customDigaai.properties index 85d7df1f2b7..10bb8f23786 100644 --- a/src/main/java/propertyFiles/customDigaai.properties +++ b/src/main/java/propertyFiles/customDigaai.properties @@ -1,5 +1,6 @@ metadatablock.name=customDigaai metadatablock.displayName=Digaai Metadata +metadatablock.displayFacet=Digaai datasetfieldtype.titulo.title=Título datasetfieldtype.numero.title=Número datasetfieldtype.datadePublicao.title=Data de Publicação @@ -52,4 +53,4 @@ controlledvocabulary.titulo.tc_brazil=TC Brazil controlledvocabulary.titulo.texas_magazine=Texas Magazine controlledvocabulary.titulo.the_brazilian_journal=The Brazilian Journal controlledvocabulary.titulo.today_magazine=Today Magazine -controlledvocabulary.titulo.viver_magazine=Viver Magazine \ No newline at end of file +controlledvocabulary.titulo.viver_magazine=Viver Magazine diff --git a/src/main/java/propertyFiles/customGSD.properties b/src/main/java/propertyFiles/customGSD.properties index 15f118c73c4..40dc0328053 100644 --- a/src/main/java/propertyFiles/customGSD.properties +++ b/src/main/java/propertyFiles/customGSD.properties @@ -1,5 +1,6 @@ metadatablock.name=customGSD metadatablock.displayName=Graduate School of Design Metadata +metadatablock.displayFacet=Graduate School of Design datasetfieldtype.gsdStudentName.title=Student Name datasetfieldtype.gsdStudentProgram.title=Student's Program of Study datasetfieldtype.gsdCourseName.title=Course Name diff --git a/src/main/java/propertyFiles/customMRA.properties b/src/main/java/propertyFiles/customMRA.properties index 8d905d266f0..5a702b980cc 100644 --- a/src/main/java/propertyFiles/customMRA.properties +++ b/src/main/java/propertyFiles/customMRA.properties @@ -1,5 +1,6 @@ metadatablock.name=customMRA metadatablock.displayName=MRA Metadata +metadatablock.displayFacet=MRA datasetfieldtype.mraCollection.title=Murray Research Archive Collection datasetfieldtype.mraCollection.description=Browse the Murray Research Archive collection with the following terms. datasetfieldtype.mraCollection.watermark= diff --git a/src/main/java/propertyFiles/customPSI.properties b/src/main/java/propertyFiles/customPSI.properties index e72e4e50222..a88b7409c5a 100644 --- a/src/main/java/propertyFiles/customPSI.properties +++ b/src/main/java/propertyFiles/customPSI.properties @@ -1,5 +1,6 @@ metadatablock.name=customPSI metadatablock.displayName=PSI Metadata +metadatablock.displayFacet=PSI datasetfieldtype.psiBehavior.title=Behavior datasetfieldtype.psiDonor.title=Donor datasetfieldtype.psiHealthArea.title=Health Area diff --git a/src/main/java/propertyFiles/customPSRI.properties b/src/main/java/propertyFiles/customPSRI.properties index 61370bb9fd1..9e76b412bd8 100644 --- a/src/main/java/propertyFiles/customPSRI.properties +++ b/src/main/java/propertyFiles/customPSRI.properties @@ -1,5 +1,6 @@ metadatablock.name=customPSRI metadatablock.displayName=Political Science Replication Initiative Metadata +metadatablock.displayFacet=Political Science Replication Initiative datasetfieldtype.PSRI1.title=Are the original data publicly available? datasetfieldtype.PSRI2.title=Is the original code available? datasetfieldtype.PSRI3.title=Where are the original data archived (name and url)? diff --git a/src/main/java/propertyFiles/custom_hbgdki.properties b/src/main/java/propertyFiles/custom_hbgdki.properties index 087c706d014..2386b5d00a2 100644 --- a/src/main/java/propertyFiles/custom_hbgdki.properties +++ b/src/main/java/propertyFiles/custom_hbgdki.properties @@ -1,5 +1,6 @@ metadatablock.name=custom_hbgdki metadatablock.displayName=HBGDki Custom Metadata +metadatablock.displayFacet=HBGDki datasetfieldtype.hbgdkiStudyName.title=Name of Study datasetfieldtype.hbgdkiStudyRegistry.title=Study Registry datasetfieldtype.hbgdkiStudyRegistryType.title=ID Type diff --git a/src/main/java/propertyFiles/geospatial.properties b/src/main/java/propertyFiles/geospatial.properties index e47982377cb..04db8d3d05f 100644 --- a/src/main/java/propertyFiles/geospatial.properties +++ b/src/main/java/propertyFiles/geospatial.properties @@ -1,5 +1,6 @@ metadatablock.name=geospatial metadatablock.displayName=Geospatial Metadata +metadatablock.displayFacet=Geospatial datasetfieldtype.geographicCoverage.title=Geographic Coverage datasetfieldtype.country.title=Country / Nation datasetfieldtype.state.title=State / Province @@ -281,4 +282,4 @@ controlledvocabulary.country.western_sahara=Western Sahara controlledvocabulary.country.yemen=Yemen controlledvocabulary.country.zambia=Zambia controlledvocabulary.country.zimbabwe=Zimbabwe -controlledvocabulary.country.aland_islands=Åland Islands \ No newline at end of file +controlledvocabulary.country.aland_islands=Åland Islands diff --git a/src/main/java/propertyFiles/journal.properties b/src/main/java/propertyFiles/journal.properties index e17a9bd6d89..753b5895f0a 100644 --- a/src/main/java/propertyFiles/journal.properties +++ b/src/main/java/propertyFiles/journal.properties @@ -1,5 +1,6 @@ metadatablock.name=journal metadatablock.displayName=Journal Metadata +metadatablock.displayFacet=Journal datasetfieldtype.journalVolumeIssue.title=Journal datasetfieldtype.journalVolume.title=Volume datasetfieldtype.journalIssue.title=Issue diff --git a/src/main/java/propertyFiles/socialscience.properties b/src/main/java/propertyFiles/socialscience.properties index 91e73fa78b9..3698b32573f 100644 --- a/src/main/java/propertyFiles/socialscience.properties +++ b/src/main/java/propertyFiles/socialscience.properties @@ -1,5 +1,6 @@ metadatablock.name=socialscience metadatablock.displayName=Social Science and Humanities Metadata +metadatablock.displayFacet=Social Science and Humanities datasetfieldtype.unitOfAnalysis.title=Unit of Analysis datasetfieldtype.universe.title=Universe datasetfieldtype.timeMethod.title=Time Method diff --git a/src/main/java/propertyFiles/staticSearchFields.properties b/src/main/java/propertyFiles/staticSearchFields.properties new file mode 100644 index 00000000000..ab03de64f23 --- /dev/null +++ b/src/main/java/propertyFiles/staticSearchFields.properties @@ -0,0 +1,11 @@ +#FacetCategory - staticSearchFields +staticSearchFields.metadata_type_ss=Dataset Feature +staticSearchFields.dvCategory=Dataverse Category +staticSearchFields.metadataSource=Metadata Source +staticSearchFields.publicationDate=Publication Year +staticSearchFields.fileTypeGroupFacet=File Type +staticSearchFields.dvObjectType=Type +staticSearchFields.fileTag=File Tag +staticSearchFields.fileAccess=Access +staticSearchFields.publicationStatus=Publication Status +staticSearchFields.subject_ss=Subject \ No newline at end of file diff --git a/src/main/resources/db/migration/V5.11.0.2__8536-metadata-block-facet.sql b/src/main/resources/db/migration/V5.11.0.2__8536-metadata-block-facet.sql new file mode 100644 index 00000000000..47435004b6d --- /dev/null +++ b/src/main/resources/db/migration/V5.11.0.2__8536-metadata-block-facet.sql @@ -0,0 +1,11 @@ +ALTER TABLE dataverse + ADD COLUMN IF NOT EXISTS metadatablockfacetroot BOOLEAN; + +UPDATE dataverse SET metadatablockfacetroot = false; + +CREATE TABLE IF NOT EXISTS dataversemetadatablockfacet ( + id SERIAL NOT NULL, + dataverse_id BIGINT NOT NULL, + metadatablock_id BIGINT NOT NULL, + PRIMARY KEY (ID) +); diff --git a/src/test/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacetTest.java b/src/test/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacetTest.java new file mode 100644 index 00000000000..7ae2d26a113 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/DataverseMetadataBlockFacetTest.java @@ -0,0 +1,45 @@ +package edu.harvard.iq.dataverse; + +import edu.harvard.iq.dataverse.mocks.MocksFactory; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * + * @author adaybujeda + */ +public class DataverseMetadataBlockFacetTest { + + @Test + public void equals_should_be_based_on_id() { + Long sameId = MocksFactory.nextId(); + DataverseMetadataBlockFacet target1 = new DataverseMetadataBlockFacet(); + target1.setId(sameId); + target1.setDataverse(new Dataverse()); + target1.setMetadataBlock(new MetadataBlock()); + + DataverseMetadataBlockFacet target2 = new DataverseMetadataBlockFacet(); + target2.setId(sameId); + target2.setDataverse(new Dataverse()); + target2.setMetadataBlock(new MetadataBlock()); + + MatcherAssert.assertThat(target1.equals(target2), Matchers.is(true)); + + + Dataverse sameDataverse = new Dataverse(); + MetadataBlock sameMetadataBlock = new MetadataBlock(); + target1 = new DataverseMetadataBlockFacet(); + target1.setId(MocksFactory.nextId()); + target1.setDataverse(sameDataverse); + target1.setMetadataBlock(sameMetadataBlock); + + target2 = new DataverseMetadataBlockFacet(); + target2.setId(MocksFactory.nextId()); + target2.setDataverse(sameDataverse); + target2.setMetadataBlock(sameMetadataBlock); + + MatcherAssert.assertThat(target1.equals(target2), Matchers.is(false)); + } + +} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/DataverseTest.java b/src/test/java/edu/harvard/iq/dataverse/DataverseTest.java new file mode 100644 index 00000000000..cb0561dd0f4 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/DataverseTest.java @@ -0,0 +1,65 @@ +package edu.harvard.iq.dataverse; + +import edu.harvard.iq.dataverse.mocks.MocksFactory; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +/** + * + * @author adaybujeda + */ +public class DataverseTest { + + private Dataverse OWNER; + private List OWNER_METADATABLOCKFACETS; + + @Before + public void beforeEachTest() { + OWNER = new Dataverse(); + OWNER.setId(MocksFactory.nextId()); + OWNER.setMetadataBlockRoot(true); + + DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setId(MocksFactory.nextId()); + OWNER_METADATABLOCKFACETS = Arrays.asList(metadataBlockFacet); + OWNER.setMetadataBlockFacets(OWNER_METADATABLOCKFACETS); + } + + @Test + public void getMetadataBlockFacets_should_return_internal_metadatablockfacets_when_metadatablockfacetroot_is_true() { + Dataverse target = new Dataverse(); + target.setId(MocksFactory.nextId()); + target.setMetadataBlockFacetRoot(true); + target.setOwner(OWNER); + + DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setId(MocksFactory.nextId()); + List internalMetadataBlockFacets = Arrays.asList(metadataBlockFacet); + target.setMetadataBlockFacets(internalMetadataBlockFacets); + List result = target.getMetadataBlockFacets(); + + MatcherAssert.assertThat(result, Matchers.is(internalMetadataBlockFacets)); + } + + @Test + public void getMetadataBlockFacets_should_return_owner_metadatablockfacets_when_metadatablockfacetroot_is_false() { + Dataverse target = new Dataverse(); + target.setId(MocksFactory.nextId()); + target.setMetadataBlockFacetRoot(false); + target.setOwner(OWNER); + + DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setId(MocksFactory.nextId()); + List internalMetadataBlockFacets = Arrays.asList(metadataBlockFacet); + target.setMetadataBlockFacets(internalMetadataBlockFacets); + List result = target.getMetadataBlockFacets(); + + MatcherAssert.assertThat(result, Matchers.is(OWNER_METADATABLOCKFACETS)); + } + +} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/MetadataBlockTest.java b/src/test/java/edu/harvard/iq/dataverse/MetadataBlockTest.java new file mode 100644 index 00000000000..85aaa37bb30 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/MetadataBlockTest.java @@ -0,0 +1,79 @@ +package edu.harvard.iq.dataverse; + +import edu.harvard.iq.dataverse.mocks.MocksFactory; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.UUID; + +public class MetadataBlockTest { + + private static final String PROPERTIES_FILE_NAME = "metadataBlockTest"; + + @Test + public void equals_should_be_based_on_id_only() { + Long id = MocksFactory.nextId(); + MetadataBlock metadataBlock1 = new MetadataBlock(); + metadataBlock1.setId(id); + metadataBlock1.setName(UUID.randomUUID().toString()); + MetadataBlock metadataBlock2 = new MetadataBlock(); + metadataBlock2.setId(id); + metadataBlock1.setName(UUID.randomUUID().toString()); + + MatcherAssert.assertThat(metadataBlock1.equals(metadataBlock2), Matchers.is(true)); + + metadataBlock1 = new MetadataBlock(); + metadataBlock1.setId(MocksFactory.nextId()); + metadataBlock1.setName("EQUAL"); + metadataBlock2 = new MetadataBlock(); + metadataBlock2.setId(MocksFactory.nextId()); + metadataBlock1.setName("EQUAL"); + + MatcherAssert.assertThat(metadataBlock1.equals(metadataBlock2), Matchers.is(false)); + } + + @Test + public void getLocaleDisplayName_should_default_value_from_displayName_when_bundle_not_found() { + MetadataBlock target = Mockito.spy(new MetadataBlock()); + target.setName(UUID.randomUUID().toString()); + target.setDisplayName(UUID.randomUUID().toString()); + + //Value when no resource file found with metadata block name + MatcherAssert.assertThat(target.getLocaleDisplayName(), Matchers.is(target.getDisplayName())); + Mockito.verify(target).getLocaleValue("metadatablock.displayName"); + } + + @Test + public void getLocaleDisplayName_should_get_value_from_properties_based_on_name() { + MetadataBlock target = Mockito.spy(new MetadataBlock()); + target.setName(PROPERTIES_FILE_NAME); + target.setDisplayName(UUID.randomUUID().toString()); + + // Values is coming from the metadataBlockTest.properties file + MatcherAssert.assertThat(target.getLocaleDisplayName(), Matchers.is("property_value_for_displayName")); + Mockito.verify(target).getLocaleValue("metadatablock.displayName"); + } + + @Test + public void getLocaleDisplayFacet_should_default_value_from_displayName_when_bundle_not_found() { + MetadataBlock target = Mockito.spy(new MetadataBlock()); + target.setName(UUID.randomUUID().toString()); + target.setDisplayName(UUID.randomUUID().toString()); + + MatcherAssert.assertThat(target.getLocaleDisplayFacet(), Matchers.is(target.getDisplayName())); + Mockito.verify(target).getLocaleValue("metadatablock.displayFacet"); + } + + @Test + public void getLocaleDisplayFacet_should_get_value_from_properties_based_on_name() { + MetadataBlock target = Mockito.spy(new MetadataBlock()); + target.setName(PROPERTIES_FILE_NAME); + target.setDisplayName(UUID.randomUUID().toString()); + + // Values is coming from the metadataBlockTest.properties file + MatcherAssert.assertThat(target.getLocaleDisplayFacet(), Matchers.is("property_value_for_displayFacet")); + Mockito.verify(target).getLocaleValue("metadatablock.displayFacet"); + } +} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java new file mode 100644 index 00000000000..a230db066d8 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java @@ -0,0 +1,184 @@ +package edu.harvard.iq.dataverse.api; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.DataverseServiceBean; +import edu.harvard.iq.dataverse.EjbDataverseEngine; +import edu.harvard.iq.dataverse.GuestbookResponseServiceBean; +import edu.harvard.iq.dataverse.GuestbookServiceBean; +import edu.harvard.iq.dataverse.MetadataBlock; +import edu.harvard.iq.dataverse.MetadataBlockServiceBean; +import edu.harvard.iq.dataverse.api.datadeposit.SwordServiceBean; +import edu.harvard.iq.dataverse.api.dto.DataverseMetadataBlockFacetDTO; +import edu.harvard.iq.dataverse.api.imports.ImportServiceBean; +import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroupServiceBean; +import edu.harvard.iq.dataverse.authorization.users.PrivateUrlUser; +import edu.harvard.iq.dataverse.engine.command.impl.ListMetadataBlockFacetsCommand; +import edu.harvard.iq.dataverse.engine.command.impl.UpdateDataverseCommand; +import edu.harvard.iq.dataverse.mocks.MocksFactory; +import edu.harvard.iq.dataverse.privateurl.PrivateUrlServiceBean; +import edu.harvard.iq.dataverse.settings.SettingsServiceBean; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Response; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * + * @author adaybujeda + */ +@RunWith(MockitoJUnitRunner.class) +public class DataversesTest { + // From AbstractApiBean class + @Mock + private EjbDataverseEngine engineSvc; + @Mock + private MetadataBlockServiceBean metadataBlockSvc; + @Mock + private PrivateUrlServiceBean privateUrlSvc; + @Mock + private HttpServletRequest httpRequest; + + // From Dataverses class + @Mock + private ExplicitGroupServiceBean explicitGroupSvc; + @Mock + private ImportServiceBean importService; + @Mock + private SettingsServiceBean settingsService; + @Mock + private GuestbookResponseServiceBean guestbookResponseService; + @Mock + private GuestbookServiceBean guestbookService; + @Mock + private DataverseServiceBean dataverseService; + @Mock + private SwordServiceBean swordService; + + @InjectMocks + private Dataverses target; + + private Dataverse VALID_DATAVERSE; + + @Before + public void beforeEachTest() { + VALID_DATAVERSE = new Dataverse(); + VALID_DATAVERSE.setId(MocksFactory.nextId()); + VALID_DATAVERSE.setAlias(UUID.randomUUID().toString()); + VALID_DATAVERSE.setMetadataBlockFacetRoot(true); + + Mockito.lenient().when(dataverseService.findByAlias(VALID_DATAVERSE.getAlias())).thenReturn(VALID_DATAVERSE); + Mockito.lenient().when(httpRequest.getHeader("X-Dataverse-key")).thenReturn(UUID.randomUUID().toString()); + Mockito.lenient().when(privateUrlSvc.getPrivateUrlUserFromToken(Mockito.anyString())).thenReturn(new PrivateUrlUser(0)); + } + + @Test + public void listMetadataBlockFacets_should_return_404_when_dataverse_is_not_found() { + String dataverseAlias = UUID.randomUUID().toString(); + Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(null); + Response result = target.listMetadataBlockFacets(dataverseAlias); + MatcherAssert.assertThat(result.getStatus(), Matchers.is(404)); + } + + @Test + public void listMetadataBlockFacets_should_return_the_list_of_metadataBlockFacetDTOs() throws Exception{ + MetadataBlock metadataBlock = Mockito.mock(MetadataBlock.class); + Mockito.when(metadataBlock.getName()).thenReturn("test_metadata_block_name"); + Mockito.when(metadataBlock.getLocaleDisplayFacet()).thenReturn("test_metadata_facet_name"); + DataverseMetadataBlockFacet dataverseMetadataBlockFacet = new DataverseMetadataBlockFacet(); + dataverseMetadataBlockFacet.setDataverse(VALID_DATAVERSE); + dataverseMetadataBlockFacet.setMetadataBlock(metadataBlock); + Mockito.when(engineSvc.submit(Mockito.any(ListMetadataBlockFacetsCommand.class))).thenReturn(Arrays.asList(dataverseMetadataBlockFacet)); + + Response response = target.listMetadataBlockFacets(VALID_DATAVERSE.getAlias()); + + MatcherAssert.assertThat(response.getStatus(), Matchers.is(200)); + MatcherAssert.assertThat(response.getEntity(), Matchers.notNullValue()); + DataverseMetadataBlockFacetDTO result = (DataverseMetadataBlockFacetDTO)response.getEntity(); + MatcherAssert.assertThat(result.getDataverseId(), Matchers.is(VALID_DATAVERSE.getId())); + MatcherAssert.assertThat(result.getDataverseAlias(), Matchers.is(VALID_DATAVERSE.getAlias())); + MatcherAssert.assertThat(result.isMetadataBlockFacetRoot(), Matchers.is(VALID_DATAVERSE.isMetadataBlockFacetRoot())); + MatcherAssert.assertThat(result.getMetadataBlocks().size(), Matchers.is(1)); + MatcherAssert.assertThat(result.getMetadataBlocks().get(0).getMetadataBlockName(), Matchers.is("test_metadata_block_name")); + MatcherAssert.assertThat(result.getMetadataBlocks().get(0).getMetadataBlockFacet(), Matchers.is("test_metadata_facet_name")); + + Mockito.verify(engineSvc).submit(Mockito.any(ListMetadataBlockFacetsCommand.class)); + } + + @Test + public void listMetadataBlockFacets_should_return_empty_list_when_metadata_block_facet_is_null() throws Exception{ + Mockito.when(engineSvc.submit(Mockito.any(ListMetadataBlockFacetsCommand.class))).thenReturn(null); + + Response response = target.listMetadataBlockFacets(VALID_DATAVERSE.getAlias()); + + MatcherAssert.assertThat(response.getStatus(), Matchers.is(200)); + DataverseMetadataBlockFacetDTO result = (DataverseMetadataBlockFacetDTO)response.getEntity(); + MatcherAssert.assertThat(result.getDataverseId(), Matchers.is(VALID_DATAVERSE.getId())); + MatcherAssert.assertThat(result.getDataverseAlias(), Matchers.is(VALID_DATAVERSE.getAlias())); + MatcherAssert.assertThat(result.isMetadataBlockFacetRoot(), Matchers.is(VALID_DATAVERSE.isMetadataBlockFacetRoot())); + MatcherAssert.assertThat(result.getMetadataBlocks(), Matchers.is(Collections.emptyList())); + + Mockito.verify(engineSvc).submit(Mockito.any(ListMetadataBlockFacetsCommand.class)); + } + + @Test + public void setMetadataBlockFacets_should_return_404_when_dataverse_is_not_found() { + String dataverseAlias = UUID.randomUUID().toString(); + Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(null); + Response result = target.setMetadataBlockFacets(dataverseAlias, Collections.emptyList()); + MatcherAssert.assertThat(result.getStatus(), Matchers.is(404)); + } + + @Test + public void setMetadataBlockFacets_should_return_400_when_dataverse_is_not_metadatablock_root() { + String dataverseAlias = UUID.randomUUID().toString(); + Dataverse dataverse = new Dataverse(); + dataverse.setMetadataBlockRoot(false); + Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(dataverse); + + List metadataBlocks = Arrays.asList("test_block"); + Response result = target.setMetadataBlockFacets(dataverseAlias, metadataBlocks); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(400)); + } + + @Test + public void setMetadataBlockFacets_should_return_400_when_invalid_metadata_block() { + Mockito.when(metadataBlockSvc.findByName("valid_block")).thenReturn(new MetadataBlock()); + Mockito.when(metadataBlockSvc.findByName("invalid_block")).thenReturn(null); + List metadataBlocks = Arrays.asList("valid_block", "invalid_block"); + Response result = target.setMetadataBlockFacets(VALID_DATAVERSE.getAlias(), metadataBlocks); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(400)); + } + + @Test + public void setMetadataBlockFacets_should_return_200_when_all_is_valid() throws Exception{ + Mockito.when(metadataBlockSvc.findByName("valid_block")).thenReturn(new MetadataBlock()); + List metadataBlocks = Arrays.asList("valid_block"); + Response result = target.setMetadataBlockFacets(VALID_DATAVERSE.getAlias(), metadataBlocks); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); + Mockito.verify(engineSvc).submit(Mockito.any(UpdateDataverseCommand.class)); + } + + @Test + public void setMetadataBlockFacets_should_support_empty_metadatablock_list() throws Exception{ + Response result = target.setMetadataBlockFacets(VALID_DATAVERSE.getAlias(), Collections.emptyList()); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); + Mockito.verify(engineSvc).submit(Mockito.any(UpdateDataverseCommand.class)); + } +} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlocksCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlocksCommandTest.java new file mode 100644 index 00000000000..520c91f47ff --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/ListMetadataBlocksCommandTest.java @@ -0,0 +1,66 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.authorization.Permission; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +import edu.harvard.iq.dataverse.engine.command.DataverseRequest; +import edu.harvard.iq.dataverse.engine.command.exception.CommandException; +import edu.harvard.iq.dataverse.mocks.MocksFactory; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.List; + +/** + * + * @author adaybujeda + */ +public class ListMetadataBlocksCommandTest { + + private DataverseRequest dataverseRequest; + private Dataverse dataverse; + private DataverseMetadataBlockFacet metadataBlockFacet; + + @Before + public void beforeEachTest() { + dataverseRequest = Mockito.mock(DataverseRequest.class); + dataverse = Mockito.mock(Dataverse.class); + metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setId(MocksFactory.nextId()); + Mockito.when(dataverse.getMetadataBlockFacets()).thenReturn(Arrays.asList(metadataBlockFacet)); + } + + @Test + public void execute_should_return_dataverse_metadata_block_facets() throws CommandException { + ListMetadataBlockFacetsCommand target = new ListMetadataBlockFacetsCommand(dataverseRequest, dataverse); + + List result = target.execute(Mockito.mock(CommandContext.class)); + + MatcherAssert.assertThat(result.size(), Matchers.is(1)); + MatcherAssert.assertThat(result.get(0), Matchers.is(metadataBlockFacet)); + } + + + @Test + public void getRequiredPermissions_should_return_empty_for_all_when_dataverse_is_released() { + Mockito.when(dataverse.isReleased()).thenReturn(true); + ListMetadataBlockFacetsCommand target = new ListMetadataBlockFacetsCommand(dataverseRequest, dataverse); + + MatcherAssert.assertThat(target.getRequiredPermissions().get(""), Matchers.emptyCollectionOf(Permission.class)); + } + + @Test + public void getRequiredPermissions_should_return_ViewUnpublishedDataverse_for_all_when_dataverse_is_not_released() { + Mockito.when(dataverse.isReleased()).thenReturn(false); + ListMetadataBlockFacetsCommand target = new ListMetadataBlockFacetsCommand(dataverseRequest, dataverse); + + MatcherAssert.assertThat(target.getRequiredPermissions().get("").size(), Matchers.is(1)); + MatcherAssert.assertThat(target.getRequiredPermissions().get(""), Matchers.hasItems(Permission.ViewUnpublishedDataverse)); + } + +} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/search/SearchIncludeFragmentTest.java b/src/test/java/edu/harvard/iq/dataverse/search/SearchIncludeFragmentTest.java new file mode 100644 index 00000000000..f94da336ca3 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/search/SearchIncludeFragmentTest.java @@ -0,0 +1,126 @@ +package edu.harvard.iq.dataverse.search; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.MetadataBlock; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class SearchIncludeFragmentTest { + + + @Test + public void getFriendlyNamesFromFilterQuery_should_return_null_when_null_or_empty_filter_query() { + SearchIncludeFragment target = new SearchIncludeFragment(); + + MatcherAssert.assertThat(target.getFriendlyNamesFromFilterQuery(null), Matchers.nullValue()); + MatcherAssert.assertThat(target.getFriendlyNamesFromFilterQuery(""), Matchers.nullValue()); + } + + @Test + public void getFriendlyNamesFromFilterQuery_should_return_null_when_filter_query_does_not_have_key_value_pair_separated_by_colon() { + SearchIncludeFragment target = new SearchIncludeFragment(); + + MatcherAssert.assertThat(target.getFriendlyNamesFromFilterQuery("key_value_no_colon"), Matchers.nullValue()); + } + + @Test + public void getFriendlyNamesFromFilterQuery_key_should_return_key_when_filter_query_key_does_not_match_any_friendly_names() { + SearchIncludeFragment target = new SearchIncludeFragment(); + + List result = target.getFriendlyNamesFromFilterQuery("key:value"); + MatcherAssert.assertThat(result.get(0), Matchers.is("key")); + } + + @Test + public void getFriendlyNamesFromFilterQuery_key_should_return_friendly_name_when_filter_query_key_does_match_friendly_name() { + SearchIncludeFragment target = new SearchIncludeFragment(); + target.datasetfieldFriendlyNamesBySolrField = Map.of("key", "KeyFriendlyName"); + + List result = target.getFriendlyNamesFromFilterQuery("key:value"); + MatcherAssert.assertThat(result.get(0), Matchers.is("KeyFriendlyName")); + } + + @Test + public void getFriendlyNamesFromFilterQuery_key_should_return_static_friendly_name_when_filter_query_key_does_not_match_friendly_name_but_matches_static_names() { + SearchIncludeFragment target = new SearchIncludeFragment(); + target.staticSolrFieldFriendlyNamesBySolrField = Map.of("key", "staticKeyFriendlyName"); + + List result = target.getFriendlyNamesFromFilterQuery("key:value"); + MatcherAssert.assertThat(result.get(0), Matchers.is("staticKeyFriendlyName")); + } + + @Test + public void getFriendlyNamesFromFilterQuery_value_should_return_metadata_block_facet_label_when_key_is_metadata_types_and_value_matches_metadata_block_name() { + SearchIncludeFragment target = new SearchIncludeFragment(); + Dataverse dataverse = Mockito.mock(Dataverse.class); + MetadataBlock block = Mockito.mock(MetadataBlock.class); + Mockito.when(block.getName()).thenReturn("metadata_block_name"); + Mockito.when(block.getLocaleDisplayFacet()).thenReturn("display_facet"); + DataverseMetadataBlockFacet blockFacet = new DataverseMetadataBlockFacet(); + blockFacet.setMetadataBlock(block); + Mockito.when(dataverse.getMetadataBlockFacets()).thenReturn(Arrays.asList(blockFacet)); + target.setDataverse(dataverse); + + List result = target.getFriendlyNamesFromFilterQuery(String.format("%s:\"metadata_block_name\"", SearchFields.METADATA_TYPES)); + MatcherAssert.assertThat(result.get(1), Matchers.is("display_facet")); + + Mockito.verify(dataverse, Mockito.times(2)).getMetadataBlockFacets(); + Mockito.verify(block).getName(); + Mockito.verify(block).getLocaleDisplayFacet(); + } + + @Test + public void getFriendlyNamesFromFilterQuery_value_should_return_value_when_key_is_metadata_types_and_value_does_not_matches_metadata_block_name() { + SearchIncludeFragment target = new SearchIncludeFragment(); + Dataverse dataverse = Mockito.mock(Dataverse.class); + MetadataBlock block = Mockito.mock(MetadataBlock.class); + Mockito.when(block.getName()).thenReturn("metadata_block_name"); + Mockito.when(block.getLocaleDisplayFacet()).thenReturn("display_facet"); + DataverseMetadataBlockFacet blockFacet = new DataverseMetadataBlockFacet(); + blockFacet.setMetadataBlock(block); + Mockito.when(dataverse.getMetadataBlockFacets()).thenReturn(Arrays.asList(blockFacet)); + target.setDataverse(dataverse); + + List result = target.getFriendlyNamesFromFilterQuery(String.format("%s:\"no_match_block_name\"", SearchFields.METADATA_TYPES)); + MatcherAssert.assertThat(result.get(1), Matchers.is("no_match_block_name")); + + Mockito.verify(dataverse, Mockito.times(2)).getMetadataBlockFacets(); + Mockito.verify(block).getName(); + } + + @Test + public void getFriendlyNamesFromFilterQuery_value_should_return_value_when_key_is_metadata_types_and_dataverse_is_null() { + SearchIncludeFragment target = new SearchIncludeFragment(); + + List result = target.getFriendlyNamesFromFilterQuery(String.format("%s:\"no_dataverse\"", SearchFields.METADATA_TYPES)); + MatcherAssert.assertThat(result.get(1), Matchers.is("no_dataverse")); + } + + @Test + public void getFriendlyNamesFromFilterQuery_value_should_return_value_when_key_is_metadata_types_and_metadata_blocks_are_null() { + SearchIncludeFragment target = new SearchIncludeFragment(); + Dataverse dataverse = Mockito.mock(Dataverse.class); + Mockito.when(dataverse.getMetadataBlockFacets()).thenReturn(null); + target.setDataverse(dataverse); + + List result = target.getFriendlyNamesFromFilterQuery(String.format("%s:\"no_metadata_blocks\"", SearchFields.METADATA_TYPES)); + MatcherAssert.assertThat(result.get(1), Matchers.is("no_metadata_blocks")); + + Mockito.verify(dataverse).getMetadataBlockFacets(); + } + + @Test + public void getFriendlyNamesFromFilterQuery_value_should_remove_quotes_from_beginning_and_end() { + SearchIncludeFragment target = new SearchIncludeFragment(); + + List result = target.getFriendlyNamesFromFilterQuery("key:\"value_\"_with_quotes\""); + MatcherAssert.assertThat(result.get(1), Matchers.is("value_\"_with_quotes")); + } +} \ No newline at end of file diff --git a/src/test/resources/propertyFiles/metadataBlockTest.properties b/src/test/resources/propertyFiles/metadataBlockTest.properties new file mode 100644 index 00000000000..25ec317c14f --- /dev/null +++ b/src/test/resources/propertyFiles/metadataBlockTest.properties @@ -0,0 +1,2 @@ +metadatablock.displayName=property_value_for_displayName +metadatablock.displayFacet=property_value_for_displayFacet \ No newline at end of file From c3aa8991202799c2ab14386297f42eb9b8ad933f Mon Sep 17 00:00:00 2001 From: Aday Bujeda Date: Tue, 26 Jul 2022 13:04:40 +0100 Subject: [PATCH 2/6] Added Update/Delete commands for metadata block facets --- .../source/_static/api/dataverse-facets.json | 1 + .../_static/api/metadata-block-facets.json | 1 + doc/sphinx-guides/source/api/native-api.rst | 33 +++++++-- .../harvard/iq/dataverse/api/Dataverses.java | 19 +++++- .../DeleteMetadataBlockFacetsCommand.java | 39 +++++++++++ .../UpdateMetadataBlockFacetsCommand.java | 48 +++++++++++++ .../iq/dataverse/api/DataversesTest.java | 59 ++++++++++------ .../DeleteMetadataBlockFacetsCommandTest.java | 49 ++++++++++++++ .../UpdateMetadataBlockFacetsCommandTest.java | 67 +++++++++++++++++++ 9 files changed, 287 insertions(+), 29 deletions(-) create mode 100644 doc/sphinx-guides/source/_static/api/dataverse-facets.json create mode 100644 doc/sphinx-guides/source/_static/api/metadata-block-facets.json create mode 100644 src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java create mode 100644 src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java diff --git a/doc/sphinx-guides/source/_static/api/dataverse-facets.json b/doc/sphinx-guides/source/_static/api/dataverse-facets.json new file mode 100644 index 00000000000..20a8412440d --- /dev/null +++ b/doc/sphinx-guides/source/_static/api/dataverse-facets.json @@ -0,0 +1 @@ +["authorName", "authorAffiliation"] \ No newline at end of file diff --git a/doc/sphinx-guides/source/_static/api/metadata-block-facets.json b/doc/sphinx-guides/source/_static/api/metadata-block-facets.json new file mode 100644 index 00000000000..bc497846592 --- /dev/null +++ b/doc/sphinx-guides/source/_static/api/metadata-block-facets.json @@ -0,0 +1 @@ +["socialscience", "geospatial"] \ No newline at end of file diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 85e7fa4fe55..68327ca9dd7 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -219,15 +219,15 @@ Assign search facets for a given Dataverse collection identified by ``id``: export SERVER_URL=https://demo.dataverse.org export ID=root - curl -H X-Dataverse-key:$API_TOKEN" -X POST $SERVER_URL/api/dataverses/$ID/facets --upload-file facets.json + curl -H X-Dataverse-key:$API_TOKEN" -X POST $SERVER_URL/api/dataverses/$ID/facets --upload-file dataverse-facets.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/root/facets --upload-file facets.json + curl -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -X POST https://demo.dataverse.org/api/dataverses/root/facets --upload-file dataverse-facets.json -Where ``facets.json`` contains a JSON encoded list of metadata keys (e.g. ``["authorName","authorAffiliation"]``). +Where :download:`dataverse-facets.json <../_static/api/dataverse-facets.json>` contains a JSON encoded list of metadata keys (e.g. ``["authorName","authorAffiliation"]``). List Metadata Block Facets Configured for a Dataverse Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -251,7 +251,7 @@ The fully expanded example above (without environment variables) looks like this Set Metadata Block Facets for a Dataverse Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Set the ``metadataBlockFacetRoot`` to ``true`` and assign the metadata blocks that will appear in the ``Metadata Types`` facet category for a given Dataverse collection identified by ``id``: +Set the ``metadataBlockFacetRoot`` to ``true`` and assign the metadata blocks that will appear in the ``Dataset Features`` facet category for a given Dataverse collection identified by ``id``: .. code-block:: bash @@ -259,15 +259,34 @@ Set the ``metadataBlockFacetRoot`` to ``true`` and assign the metadata blocks th export SERVER_URL=https://demo.dataverse.org export ID=root - curl -H X-Dataverse-key:$API_TOKEN" -X POST $SERVER_URL/api/dataverses/$ID/metadatablockfacets --upload-file metadata_blocks.json + curl -H X-Dataverse-key:$API_TOKEN" -X POST $SERVER_URL/api/dataverses/$ID/metadatablockfacets --upload-file metadata-block-facets.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/root/metadatablockfacets --upload-file metadata_blocks.json + curl -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -X POST https://demo.dataverse.org/api/dataverses/root/metadatablockfacets --upload-file metadata-block-facets.json -Where ``metadata_blocks.json`` contains a JSON encoded list of metadata block names (e.g. ``["socialscience","geospatial"]``). +Where :download:`metadata-block-facets.json <../_static/api/metadata-block-facets.json>` contains a JSON encoded list of metadata block names (e.g. ``["socialscience","geospatial"]``). This endpoint supports an empty list (e.g. ``[]``) + +Delete Metadata Block Facets Configured for a Dataverse Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Deletes the metadata block facet configuration 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=root + + curl -H X-Dataverse-key:$API_TOKEN -X DELETE $SERVER_URL/api/dataverses/$ID/metadatablockfacets + +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 DELETE https://demo.dataverse.org/api/dataverses/root/metadatablockfacets Create a New Role in a Dataverse Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 54cd3869a47..4b5a4ab1267 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -43,6 +43,7 @@ import edu.harvard.iq.dataverse.engine.command.impl.DeleteDataverseCommand; import edu.harvard.iq.dataverse.engine.command.impl.DeleteDataverseLinkingDataverseCommand; import edu.harvard.iq.dataverse.engine.command.impl.DeleteExplicitGroupCommand; +import edu.harvard.iq.dataverse.engine.command.impl.DeleteMetadataBlockFacetsCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetDataverseCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetDataverseStorageSizeCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetExplicitGroupCommand; @@ -65,6 +66,7 @@ import edu.harvard.iq.dataverse.engine.command.impl.UpdateDataverseDefaultContributorRoleCommand; import edu.harvard.iq.dataverse.engine.command.impl.UpdateDataverseMetadataBlocksCommand; import edu.harvard.iq.dataverse.engine.command.impl.UpdateExplicitGroupCommand; +import edu.harvard.iq.dataverse.engine.command.impl.UpdateMetadataBlockFacetsCommand; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; import edu.harvard.iq.dataverse.util.BundleUtil; import edu.harvard.iq.dataverse.util.ConstraintViolationUtil; @@ -757,9 +759,7 @@ public Response setMetadataBlockFacets(@PathParam("identifier") String dvIdtf, L metadataBlockFacets.add(metadataBlockFacet); } - dataverse.setMetadataBlockFacetRoot(true); - dataverse.setMetadataBlockFacets(metadataBlockFacets); - execCommand(new UpdateDataverseCommand(dataverse, null, null, createDataverseRequest(findUserOrDie()), null)); + execCommand(new UpdateMetadataBlockFacetsCommand(createDataverseRequest(findUserOrDie()), dataverse, metadataBlockFacets)); return ok(String.format("Metadata block facets updated. DataverseId: %s blocks: %s", dvIdtf, metadataBlockNames)); } catch (WrappedResponse ex) { @@ -767,6 +767,19 @@ public Response setMetadataBlockFacets(@PathParam("identifier") String dvIdtf, L } } + @DELETE + @Path("{identifier}/metadatablockfacets") + public Response deleteMetadataBlockFacets(@PathParam("identifier") String dvIdtf) { + try { + Dataverse dataverse = findDataverseOrDie(dvIdtf); + execCommand(new DeleteMetadataBlockFacetsCommand(createDataverseRequest(findUserOrDie()), dataverse)); + return ok(String.format("Metadata block facets deleted. DataverseId: %s", dvIdtf)); + + } catch (WrappedResponse ex) { + return ex.getResponse(); + } + } + // FIXME: This listContent method is way too optimistic, always returning "ok" and never "error". // TODO: Investigate why there was a change in the timeframe of when pull request #4350 was merged // (2438-4295-dois-for-files branch) such that a contributor API token no longer allows this method diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java new file mode 100644 index 00000000000..fd80b0ede5c --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java @@ -0,0 +1,39 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.authorization.Permission; +import edu.harvard.iq.dataverse.engine.command.AbstractCommand; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +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 java.util.Collections; + +/** + * + * @author adaybujeda + */ +@RequiredPermissions( Permission.EditDataverse ) +public class DeleteMetadataBlockFacetsCommand extends AbstractCommand { + + private final Dataverse editedDv; + + public DeleteMetadataBlockFacetsCommand(DataverseRequest aRequest, Dataverse editedDv) { + super(aRequest, editedDv); + this.editedDv = editedDv; + } + + @Override + public Dataverse execute(CommandContext ctxt) throws CommandException { + editedDv.setMetadataBlockFacetRoot(false); + editedDv.setMetadataBlockFacets(Collections.emptyList()); + Dataverse updated = ctxt.dataverses().save(editedDv); + return updated; + } + + // Visible for testing + public Dataverse getEditedDataverse() { + return this.editedDv; + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java new file mode 100644 index 00000000000..05ba3dd5819 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java @@ -0,0 +1,48 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.authorization.Permission; +import edu.harvard.iq.dataverse.engine.command.AbstractCommand; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +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 java.util.List; + +/** + * + * @author adaybujeda + */ +@RequiredPermissions( Permission.EditDataverse ) +public class UpdateMetadataBlockFacetsCommand extends AbstractCommand { + + private final Dataverse editedDv; + private final List metadataBlockFacets; + + public UpdateMetadataBlockFacetsCommand(DataverseRequest aRequest, Dataverse editedDv, List metadataBlockFacets) { + super(aRequest, editedDv); + this.editedDv = editedDv; + this.metadataBlockFacets = metadataBlockFacets; + } + + @Override + public Dataverse execute(CommandContext ctxt) throws CommandException { + editedDv.setMetadataBlockFacetRoot(true); + editedDv.setMetadataBlockFacets(metadataBlockFacets); + Dataverse updated = ctxt.dataverses().save(editedDv); + return updated; + } + + // Visible for testing + public Dataverse getEditedDataverse() { + return this.editedDv; + } + + // Visible for testing + public List getMetadataBlockFacets() { + return this.metadataBlockFacets; + } + +} diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java index a230db066d8..6a0f26655fb 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java @@ -13,8 +13,9 @@ import edu.harvard.iq.dataverse.api.imports.ImportServiceBean; import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroupServiceBean; import edu.harvard.iq.dataverse.authorization.users.PrivateUrlUser; +import edu.harvard.iq.dataverse.engine.command.impl.DeleteMetadataBlockFacetsCommand; import edu.harvard.iq.dataverse.engine.command.impl.ListMetadataBlockFacetsCommand; -import edu.harvard.iq.dataverse.engine.command.impl.UpdateDataverseCommand; +import edu.harvard.iq.dataverse.engine.command.impl.UpdateMetadataBlockFacetsCommand; import edu.harvard.iq.dataverse.mocks.MocksFactory; import edu.harvard.iq.dataverse.privateurl.PrivateUrlServiceBean; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; @@ -23,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -77,7 +79,6 @@ public void beforeEachTest() { VALID_DATAVERSE = new Dataverse(); VALID_DATAVERSE.setId(MocksFactory.nextId()); VALID_DATAVERSE.setAlias(UUID.randomUUID().toString()); - VALID_DATAVERSE.setMetadataBlockFacetRoot(true); Mockito.lenient().when(dataverseService.findByAlias(VALID_DATAVERSE.getAlias())).thenReturn(VALID_DATAVERSE); Mockito.lenient().when(httpRequest.getHeader("X-Dataverse-key")).thenReturn(UUID.randomUUID().toString()); @@ -89,7 +90,9 @@ public void listMetadataBlockFacets_should_return_404_when_dataverse_is_not_foun String dataverseAlias = UUID.randomUUID().toString(); Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(null); Response result = target.listMetadataBlockFacets(dataverseAlias); + MatcherAssert.assertThat(result.getStatus(), Matchers.is(404)); + Mockito.verifyNoMoreInteractions(engineSvc); } @Test @@ -138,20 +141,9 @@ public void setMetadataBlockFacets_should_return_404_when_dataverse_is_not_found String dataverseAlias = UUID.randomUUID().toString(); Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(null); Response result = target.setMetadataBlockFacets(dataverseAlias, Collections.emptyList()); - MatcherAssert.assertThat(result.getStatus(), Matchers.is(404)); - } - - @Test - public void setMetadataBlockFacets_should_return_400_when_dataverse_is_not_metadatablock_root() { - String dataverseAlias = UUID.randomUUID().toString(); - Dataverse dataverse = new Dataverse(); - dataverse.setMetadataBlockRoot(false); - Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(dataverse); - List metadataBlocks = Arrays.asList("test_block"); - Response result = target.setMetadataBlockFacets(dataverseAlias, metadataBlocks); - - MatcherAssert.assertThat(result.getStatus(), Matchers.is(400)); + MatcherAssert.assertThat(result.getStatus(), Matchers.is(404)); + Mockito.verifyNoMoreInteractions(engineSvc); } @Test @@ -162,16 +154,24 @@ public void setMetadataBlockFacets_should_return_400_when_invalid_metadata_block Response result = target.setMetadataBlockFacets(VALID_DATAVERSE.getAlias(), metadataBlocks); MatcherAssert.assertThat(result.getStatus(), Matchers.is(400)); + Mockito.verifyNoMoreInteractions(engineSvc); } @Test - public void setMetadataBlockFacets_should_return_200_when_all_is_valid() throws Exception{ - Mockito.when(metadataBlockSvc.findByName("valid_block")).thenReturn(new MetadataBlock()); + public void setMetadataBlockFacets_should_return_200_when_update_is_successful() throws Exception { + MetadataBlock validBlock = new MetadataBlock(); + Mockito.when(metadataBlockSvc.findByName("valid_block")).thenReturn(validBlock); List metadataBlocks = Arrays.asList("valid_block"); Response result = target.setMetadataBlockFacets(VALID_DATAVERSE.getAlias(), metadataBlocks); MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); - Mockito.verify(engineSvc).submit(Mockito.any(UpdateDataverseCommand.class)); + ArgumentCaptor updateCommand = ArgumentCaptor.forClass(UpdateMetadataBlockFacetsCommand.class); + Mockito.verify(engineSvc).submit(updateCommand.capture()); + + MatcherAssert.assertThat(updateCommand.getValue().getEditedDataverse(), Matchers.is(VALID_DATAVERSE)); + MatcherAssert.assertThat(updateCommand.getValue().getMetadataBlockFacets().size(), Matchers.is(1)); + MatcherAssert.assertThat(updateCommand.getValue().getMetadataBlockFacets().get(0).getDataverse(), Matchers.is(VALID_DATAVERSE)); + MatcherAssert.assertThat(updateCommand.getValue().getMetadataBlockFacets().get(0).getMetadataBlock(), Matchers.is(validBlock)); } @Test @@ -179,6 +179,27 @@ public void setMetadataBlockFacets_should_support_empty_metadatablock_list() thr Response result = target.setMetadataBlockFacets(VALID_DATAVERSE.getAlias(), Collections.emptyList()); MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); - Mockito.verify(engineSvc).submit(Mockito.any(UpdateDataverseCommand.class)); + Mockito.verify(engineSvc).submit(Mockito.any(UpdateMetadataBlockFacetsCommand.class)); + } + + @Test + public void deleteMetadataBlockFacets_should_return_404_when_dataverse_is_not_found() { + String dataverseAlias = UUID.randomUUID().toString(); + Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(null); + Response result = target.deleteMetadataBlockFacets(dataverseAlias); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(404)); + Mockito.verifyNoMoreInteractions(engineSvc); + } + + @Test + public void deleteMetadataBlockFacets_should_return_200_when_dataverse_is_found() throws Exception{ + Response result = target.deleteMetadataBlockFacets(VALID_DATAVERSE.getAlias()); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); + ArgumentCaptor deleteCommand = ArgumentCaptor.forClass(DeleteMetadataBlockFacetsCommand.class); + Mockito.verify(engineSvc).submit(deleteCommand.capture()); + + MatcherAssert.assertThat(deleteCommand.getValue().getEditedDataverse(), Matchers.is(VALID_DATAVERSE)); } } \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java new file mode 100644 index 00000000000..55a3858a7c0 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java @@ -0,0 +1,49 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +import edu.harvard.iq.dataverse.engine.command.DataverseRequest; +import edu.harvard.iq.dataverse.engine.command.exception.CommandException; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Collections; + +/** + * + * @author adaybujeda + */ +public class DeleteMetadataBlockFacetsCommandTest { + + private DataverseRequest dataverseRequest; + private Dataverse dataverse; + + @Before + public void beforeEachTest() { + dataverseRequest = Mockito.mock(DataverseRequest.class); + dataverse = Mockito.mock(Dataverse.class); + } + + @Test + public void should_set_metadataBlockFacetRoot_to_false_and_facets_to_empty_list() throws CommandException { + DeleteMetadataBlockFacetsCommand target = new DeleteMetadataBlockFacetsCommand(dataverseRequest, dataverse); + + CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); + target.execute(context); + + Mockito.verify(dataverse).setMetadataBlockFacetRoot(false); + Mockito.verify(dataverse).setMetadataBlockFacets(Collections.emptyList()); + Mockito.verify(context.dataverses()).save(dataverse); + } + + @Test + public void getEditedDataverse_should_return_set_dataverse() { + DeleteMetadataBlockFacetsCommand target = new DeleteMetadataBlockFacetsCommand(dataverseRequest, dataverse); + + MatcherAssert.assertThat(target.getEditedDataverse(), Matchers.is(dataverse)); + } + +} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java new file mode 100644 index 00000000000..766398926a9 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java @@ -0,0 +1,67 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +import edu.harvard.iq.dataverse.engine.command.DataverseRequest; +import edu.harvard.iq.dataverse.engine.command.exception.CommandException; +import edu.harvard.iq.dataverse.mocks.MocksFactory; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author adaybujeda + */ +public class UpdateMetadataBlockFacetsCommandTest { + + private DataverseRequest dataverseRequest; + private Dataverse dataverse; + + @Before + public void beforeEachTest() { + dataverseRequest = Mockito.mock(DataverseRequest.class); + dataverse = Mockito.mock(Dataverse.class); + } + + @Test + public void should_set_metadataBlockFacetRoot_to_true_and_update_facets() throws CommandException { + DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setId(MocksFactory.nextId()); + List metadataBlockFacets = Arrays.asList(metadataBlockFacet); + + UpdateMetadataBlockFacetsCommand target = new UpdateMetadataBlockFacetsCommand(dataverseRequest, dataverse, metadataBlockFacets); + + CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); + target.execute(context); + + Mockito.verify(dataverse).setMetadataBlockFacetRoot(true); + Mockito.verify(dataverse).setMetadataBlockFacets(metadataBlockFacets); + Mockito.verify(context.dataverses()).save(dataverse); + } + + @Test + public void getEditedDataverse_should_return_set_dataverse() { + UpdateMetadataBlockFacetsCommand target = new UpdateMetadataBlockFacetsCommand(dataverseRequest, dataverse, Collections.emptyList()); + + MatcherAssert.assertThat(target.getEditedDataverse(), Matchers.is(dataverse)); + } + + @Test + public void getMetadataBlockFacets_should_return_set_metadata_block_facets() { + DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setId(MocksFactory.nextId()); + List metadataBlockFacets = Arrays.asList(metadataBlockFacet); + UpdateMetadataBlockFacetsCommand target = new UpdateMetadataBlockFacetsCommand(dataverseRequest, dataverse, metadataBlockFacets); + + MatcherAssert.assertThat(target.getMetadataBlockFacets(), Matchers.is(metadataBlockFacets)); + } + +} \ No newline at end of file From e84ceee6731d2f8f5f39d8d92826c2c7d5b6bedc Mon Sep 17 00:00:00 2001 From: Aday Bujeda Date: Thu, 4 Aug 2022 17:22:26 +0100 Subject: [PATCH 3/6] Updated metadata block facet DB script after 5.11 release --- ...a-block-facet.sql => V5.11.1.2__8536-metadata-block-facet.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V5.11.0.2__8536-metadata-block-facet.sql => V5.11.1.2__8536-metadata-block-facet.sql} (100%) diff --git a/src/main/resources/db/migration/V5.11.0.2__8536-metadata-block-facet.sql b/src/main/resources/db/migration/V5.11.1.2__8536-metadata-block-facet.sql similarity index 100% rename from src/main/resources/db/migration/V5.11.0.2__8536-metadata-block-facet.sql rename to src/main/resources/db/migration/V5.11.1.2__8536-metadata-block-facet.sql From 12f5192e488e5a16b18544d7f3d22e5d0087ff8a Mon Sep 17 00:00:00 2001 From: Aday Bujeda Date: Tue, 16 Aug 2022 08:31:02 +0100 Subject: [PATCH 4/6] Updated Metadata block facet API with new requirements --- doc/sphinx-guides/source/api/native-api.rst | 16 ++- .../harvard/iq/dataverse/api/Dataverses.java | 26 ++-- .../DeleteMetadataBlockFacetsCommand.java | 39 ------ .../UpdateMetadataBlockFacetRootCommand.java | 64 ++++++++++ .../UpdateMetadataBlockFacetsCommand.java | 6 +- .../iq/dataverse/api/DataversesTest.java | 38 ++++-- .../DeleteMetadataBlockFacetsCommandTest.java | 49 -------- ...dateMetadataBlockFacetRootCommandTest.java | 112 ++++++++++++++++++ .../UpdateMetadataBlockFacetsCommandTest.java | 15 ++- 9 files changed, 248 insertions(+), 117 deletions(-) delete mode 100644 src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java create mode 100644 src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java delete mode 100644 src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 68327ca9dd7..d6a641b6544 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -251,7 +251,9 @@ The fully expanded example above (without environment variables) looks like this Set Metadata Block Facets for a Dataverse Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Set the ``metadataBlockFacetRoot`` to ``true`` and assign the metadata blocks that will appear in the ``Dataset Features`` facet category for a given Dataverse collection identified by ``id``: +Sets the metadata blocks that will appear in the ``Dataset Features`` facet category for a given Dataverse collection identified by ``id``. + +To clear the metadata blocks set by a parent dataverse, submit an empty array (e.g. ``[]``): .. code-block:: bash @@ -269,10 +271,12 @@ The fully expanded example above (without environment variables) looks like this Where :download:`metadata-block-facets.json <../_static/api/metadata-block-facets.json>` contains a JSON encoded list of metadata block names (e.g. ``["socialscience","geospatial"]``). This endpoint supports an empty list (e.g. ``[]``) -Delete Metadata Block Facets Configured for a Dataverse Collection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Configure a Dataverse Collection to Inherit Its Metadata Block Facets from Its Parent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Set whether the Dataverse collection is a metadata block facet root, or does it uses its parent metadata block facets. Possible values are ``true`` and ``false`` (both are valid JSON expressions). -Deletes the metadata block facet configuration for a given Dataverse collection ``id``: +When updating the root to false, it will clear any metadata block facets from the dataverse. When updating to true, it will copy the metadata block facets from the parent dataverse: .. code-block:: bash @@ -280,13 +284,13 @@ Deletes the metadata block facet configuration for a given Dataverse collection export SERVER_URL=https://demo.dataverse.org export ID=root - curl -H X-Dataverse-key:$API_TOKEN -X DELETE $SERVER_URL/api/dataverses/$ID/metadatablockfacets + curl -H X-Dataverse-key:$API_TOKEN -X POST $SERVER_URL/api/dataverses/$ID/metadatablockfacets/isRoot -d 'true' 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 DELETE https://demo.dataverse.org/api/dataverses/root/metadatablockfacets + curl -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -X POST https://demo.dataverse.org/api/dataverses/root/metadatablockfacets/isRoot -d 'true' Create a New Role in a Dataverse Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 4b5a4ab1267..c84cfe070c2 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -13,13 +13,11 @@ import edu.harvard.iq.dataverse.api.dto.DataverseMetadataBlockFacetDTO; import edu.harvard.iq.dataverse.authorization.DataverseRole; import edu.harvard.iq.dataverse.DvObject; -import edu.harvard.iq.dataverse.DvObjectContainer; import edu.harvard.iq.dataverse.GlobalId; import edu.harvard.iq.dataverse.GuestbookResponseServiceBean; import edu.harvard.iq.dataverse.GuestbookServiceBean; import edu.harvard.iq.dataverse.MetadataBlock; import edu.harvard.iq.dataverse.RoleAssignment; -import static edu.harvard.iq.dataverse.api.AbstractApiBean.error; import edu.harvard.iq.dataverse.api.dto.ExplicitGroupDTO; import edu.harvard.iq.dataverse.api.dto.RoleAssignmentDTO; import edu.harvard.iq.dataverse.api.dto.RoleDTO; @@ -43,7 +41,7 @@ import edu.harvard.iq.dataverse.engine.command.impl.DeleteDataverseCommand; import edu.harvard.iq.dataverse.engine.command.impl.DeleteDataverseLinkingDataverseCommand; import edu.harvard.iq.dataverse.engine.command.impl.DeleteExplicitGroupCommand; -import edu.harvard.iq.dataverse.engine.command.impl.DeleteMetadataBlockFacetsCommand; +import edu.harvard.iq.dataverse.engine.command.impl.UpdateMetadataBlockFacetRootCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetDataverseCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetDataverseStorageSizeCommand; import edu.harvard.iq.dataverse.engine.command.impl.GetExplicitGroupCommand; @@ -74,7 +72,6 @@ import static edu.harvard.iq.dataverse.util.StringUtil.nonEmpty; import edu.harvard.iq.dataverse.util.json.JSONLDUtil; -import edu.harvard.iq.dataverse.util.json.JsonLDTerm; import edu.harvard.iq.dataverse.util.json.JsonParseException; import static edu.harvard.iq.dataverse.util.json.JsonPrinter.brief; import java.io.StringReader; @@ -96,7 +93,6 @@ import javax.json.JsonValue; import javax.json.JsonValue.ValueType; import javax.json.stream.JsonParsingException; -import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; @@ -119,7 +115,6 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -746,11 +741,15 @@ public Response setMetadataBlockFacets(@PathParam("identifier") String dvIdtf, L try { Dataverse dataverse = findDataverseOrDie(dvIdtf); + if(!dataverse.isMetadataBlockFacetRoot()) { + return badRequest(String.format("Dataverse: %s must have metadata block facet root set to true", dvIdtf)); + } + List metadataBlockFacets = new LinkedList<>(); for(String metadataBlockName: metadataBlockNames) { MetadataBlock metadataBlock = findMetadataBlock(metadataBlockName); if (metadataBlock == null) { - return error(Response.Status.BAD_REQUEST, String.format("Invalid metadata block name: %s", metadataBlockName)); + return badRequest(String.format("Invalid metadata block name: %s", metadataBlockName)); } DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); @@ -767,13 +766,16 @@ public Response setMetadataBlockFacets(@PathParam("identifier") String dvIdtf, L } } - @DELETE - @Path("{identifier}/metadatablockfacets") - public Response deleteMetadataBlockFacets(@PathParam("identifier") String dvIdtf) { + @POST + @Path("{identifier}/metadatablockfacets/isRoot") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response updateMetadataBlockFacetsRoot(@PathParam("identifier") String dvIdtf, String body) { try { + final boolean blockFacetsRoot = parseBooleanOrDie(body); Dataverse dataverse = findDataverseOrDie(dvIdtf); - execCommand(new DeleteMetadataBlockFacetsCommand(createDataverseRequest(findUserOrDie()), dataverse)); - return ok(String.format("Metadata block facets deleted. DataverseId: %s", dvIdtf)); + execCommand(new UpdateMetadataBlockFacetRootCommand(createDataverseRequest(findUserOrDie()), dataverse, blockFacetsRoot)); + return ok(String.format("Metadata block facets root updated. DataverseId: %s blockFacetsRoot: %s", dvIdtf, blockFacetsRoot)); } catch (WrappedResponse ex) { return ex.getResponse(); diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java deleted file mode 100644 index fd80b0ede5c..00000000000 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommand.java +++ /dev/null @@ -1,39 +0,0 @@ -package edu.harvard.iq.dataverse.engine.command.impl; - -import edu.harvard.iq.dataverse.Dataverse; -import edu.harvard.iq.dataverse.authorization.Permission; -import edu.harvard.iq.dataverse.engine.command.AbstractCommand; -import edu.harvard.iq.dataverse.engine.command.CommandContext; -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 java.util.Collections; - -/** - * - * @author adaybujeda - */ -@RequiredPermissions( Permission.EditDataverse ) -public class DeleteMetadataBlockFacetsCommand extends AbstractCommand { - - private final Dataverse editedDv; - - public DeleteMetadataBlockFacetsCommand(DataverseRequest aRequest, Dataverse editedDv) { - super(aRequest, editedDv); - this.editedDv = editedDv; - } - - @Override - public Dataverse execute(CommandContext ctxt) throws CommandException { - editedDv.setMetadataBlockFacetRoot(false); - editedDv.setMetadataBlockFacets(Collections.emptyList()); - Dataverse updated = ctxt.dataverses().save(editedDv); - return updated; - } - - // Visible for testing - public Dataverse getEditedDataverse() { - return this.editedDv; - } -} diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java new file mode 100644 index 00000000000..32ef7d54a7e --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java @@ -0,0 +1,64 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.authorization.Permission; +import edu.harvard.iq.dataverse.engine.command.AbstractCommand; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +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 java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * + * @author adaybujeda + */ +@RequiredPermissions( Permission.EditDataverse ) +public class UpdateMetadataBlockFacetRootCommand extends AbstractCommand { + + private final Dataverse editedDv; + private final boolean metadataBlockFacetRoot; + + public UpdateMetadataBlockFacetRootCommand(DataverseRequest aRequest, Dataverse editedDv, boolean metadataBlockFacetRoot) { + super(aRequest, editedDv); + this.editedDv = editedDv; + this.metadataBlockFacetRoot = metadataBlockFacetRoot; + } + + @Override + public Dataverse execute(CommandContext ctxt) throws CommandException { + if(editedDv.isMetadataBlockFacetRoot() != metadataBlockFacetRoot) { + // Update metadata block facets when root changes value + // if you set root to be false (i.e. inherit), it should clear the blocks. + // if you set to true (i.e. use your own), it should make a copy of what is in the parent + List newBlockFacets = Collections.emptyList(); + if (metadataBlockFacetRoot) { + newBlockFacets = editedDv.getMetadataBlockFacets().stream().map(blockFacet -> { + DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); + metadataBlockFacet.setDataverse(editedDv); + metadataBlockFacet.setMetadataBlock(blockFacet.getMetadataBlock()); + return metadataBlockFacet; + }).collect(Collectors.toList()); + } + editedDv.setMetadataBlockFacets(newBlockFacets); + } + + editedDv.setMetadataBlockFacetRoot(metadataBlockFacetRoot); + Dataverse updated = ctxt.dataverses().save(editedDv); + return updated; + } + + // Visible for testing + public Dataverse getEditedDataverse() { + return this.editedDv; + } + + // Visible for testing + public boolean getMetadataBlockFacetRoot() { + return metadataBlockFacetRoot; + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java index 05ba3dd5819..72a41f5cc3c 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommand.java @@ -8,6 +8,7 @@ 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.engine.command.exception.IllegalCommandException; import java.util.List; @@ -29,7 +30,10 @@ public UpdateMetadataBlockFacetsCommand(DataverseRequest aRequest, Dataverse edi @Override public Dataverse execute(CommandContext ctxt) throws CommandException { - editedDv.setMetadataBlockFacetRoot(true); + if (!editedDv.isMetadataBlockFacetRoot()) { + throw new IllegalCommandException("Cannot update metadata blocks facets when dataverse has metadata block facet root set to false", this); + } + editedDv.setMetadataBlockFacets(metadataBlockFacets); Dataverse updated = ctxt.dataverses().save(editedDv); return updated; diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java index 6a0f26655fb..a25dbaa1f1b 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java @@ -13,8 +13,8 @@ import edu.harvard.iq.dataverse.api.imports.ImportServiceBean; import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroupServiceBean; import edu.harvard.iq.dataverse.authorization.users.PrivateUrlUser; -import edu.harvard.iq.dataverse.engine.command.impl.DeleteMetadataBlockFacetsCommand; import edu.harvard.iq.dataverse.engine.command.impl.ListMetadataBlockFacetsCommand; +import edu.harvard.iq.dataverse.engine.command.impl.UpdateMetadataBlockFacetRootCommand; import edu.harvard.iq.dataverse.engine.command.impl.UpdateMetadataBlockFacetsCommand; import edu.harvard.iq.dataverse.mocks.MocksFactory; import edu.harvard.iq.dataverse.privateurl.PrivateUrlServiceBean; @@ -79,6 +79,7 @@ public void beforeEachTest() { VALID_DATAVERSE = new Dataverse(); VALID_DATAVERSE.setId(MocksFactory.nextId()); VALID_DATAVERSE.setAlias(UUID.randomUUID().toString()); + VALID_DATAVERSE.setMetadataBlockFacetRoot(true); Mockito.lenient().when(dataverseService.findByAlias(VALID_DATAVERSE.getAlias())).thenReturn(VALID_DATAVERSE); Mockito.lenient().when(httpRequest.getHeader("X-Dataverse-key")).thenReturn(UUID.randomUUID().toString()); @@ -146,6 +147,19 @@ public void setMetadataBlockFacets_should_return_404_when_dataverse_is_not_found Mockito.verifyNoMoreInteractions(engineSvc); } + @Test + public void setMetadataBlockFacets_should_return_400_when_dataverse_has_metadata_facet_root_set_to_false() { + String dataverseAlias = UUID.randomUUID().toString(); + Dataverse dataverse = Mockito.mock(Dataverse.class); + Mockito.when(dataverse.isMetadataBlockFacetRoot()).thenReturn(false); + Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(dataverse); + + Response result = target.setMetadataBlockFacets(dataverseAlias, Collections.emptyList()); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(400)); + Mockito.verifyNoMoreInteractions(engineSvc); + } + @Test public void setMetadataBlockFacets_should_return_400_when_invalid_metadata_block() { Mockito.when(metadataBlockSvc.findByName("valid_block")).thenReturn(new MetadataBlock()); @@ -183,23 +197,31 @@ public void setMetadataBlockFacets_should_support_empty_metadatablock_list() thr } @Test - public void deleteMetadataBlockFacets_should_return_404_when_dataverse_is_not_found() { + public void updateMetadataBlockFacetsRoot_should_return_404_when_dataverse_is_not_found() { String dataverseAlias = UUID.randomUUID().toString(); Mockito.when(dataverseService.findByAlias(dataverseAlias)).thenReturn(null); - Response result = target.deleteMetadataBlockFacets(dataverseAlias); + Response result = target.updateMetadataBlockFacetsRoot(dataverseAlias, "true"); MatcherAssert.assertThat(result.getStatus(), Matchers.is(404)); Mockito.verifyNoMoreInteractions(engineSvc); } @Test - public void deleteMetadataBlockFacets_should_return_200_when_dataverse_is_found() throws Exception{ - Response result = target.deleteMetadataBlockFacets(VALID_DATAVERSE.getAlias()); + public void updateMetadataBlockFacetsRoot_should_return_400_when_invalid_boolean() throws Exception{ + Response result = target.updateMetadataBlockFacetsRoot(VALID_DATAVERSE.getAlias(), "invalid"); + + MatcherAssert.assertThat(result.getStatus(), Matchers.is(400)); + Mockito.verifyNoMoreInteractions(engineSvc); + } + + @Test + public void updateMetadataBlockFacetsRoot_should_return_200_when_dataverse_is_found() throws Exception{ + Response result = target.updateMetadataBlockFacetsRoot(VALID_DATAVERSE.getAlias(), "true"); MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); - ArgumentCaptor deleteCommand = ArgumentCaptor.forClass(DeleteMetadataBlockFacetsCommand.class); - Mockito.verify(engineSvc).submit(deleteCommand.capture()); + ArgumentCaptor updateRootCommand = ArgumentCaptor.forClass(UpdateMetadataBlockFacetRootCommand.class); + Mockito.verify(engineSvc).submit(updateRootCommand.capture()); - MatcherAssert.assertThat(deleteCommand.getValue().getEditedDataverse(), Matchers.is(VALID_DATAVERSE)); + MatcherAssert.assertThat(updateRootCommand.getValue().getEditedDataverse(), Matchers.is(VALID_DATAVERSE)); } } \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java deleted file mode 100644 index 55a3858a7c0..00000000000 --- a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteMetadataBlockFacetsCommandTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package edu.harvard.iq.dataverse.engine.command.impl; - -import edu.harvard.iq.dataverse.Dataverse; -import edu.harvard.iq.dataverse.engine.command.CommandContext; -import edu.harvard.iq.dataverse.engine.command.DataverseRequest; -import edu.harvard.iq.dataverse.engine.command.exception.CommandException; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import java.util.Collections; - -/** - * - * @author adaybujeda - */ -public class DeleteMetadataBlockFacetsCommandTest { - - private DataverseRequest dataverseRequest; - private Dataverse dataverse; - - @Before - public void beforeEachTest() { - dataverseRequest = Mockito.mock(DataverseRequest.class); - dataverse = Mockito.mock(Dataverse.class); - } - - @Test - public void should_set_metadataBlockFacetRoot_to_false_and_facets_to_empty_list() throws CommandException { - DeleteMetadataBlockFacetsCommand target = new DeleteMetadataBlockFacetsCommand(dataverseRequest, dataverse); - - CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); - target.execute(context); - - Mockito.verify(dataverse).setMetadataBlockFacetRoot(false); - Mockito.verify(dataverse).setMetadataBlockFacets(Collections.emptyList()); - Mockito.verify(context.dataverses()).save(dataverse); - } - - @Test - public void getEditedDataverse_should_return_set_dataverse() { - DeleteMetadataBlockFacetsCommand target = new DeleteMetadataBlockFacetsCommand(dataverseRequest, dataverse); - - MatcherAssert.assertThat(target.getEditedDataverse(), Matchers.is(dataverse)); - } - -} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java new file mode 100644 index 00000000000..05a4adbb512 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java @@ -0,0 +1,112 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseMetadataBlockFacet; +import edu.harvard.iq.dataverse.MetadataBlock; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +import edu.harvard.iq.dataverse.engine.command.DataverseRequest; +import edu.harvard.iq.dataverse.engine.command.exception.CommandException; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author adaybujeda + */ +public class UpdateMetadataBlockFacetRootCommandTest { + + private DataverseRequest dataverseRequest; + private Dataverse dataverse; + + @Before + public void beforeEachTest() { + dataverseRequest = Mockito.mock(DataverseRequest.class); + dataverse = Mockito.mock(Dataverse.class); + } + + @Test + public void should_set_metadataBlockFacetRoot_and_not_update_metadata_block_facets_when_root_value_does_not_change() throws CommandException { + boolean metadataBlockFacetRoot = true; + Mockito.when(dataverse.isMetadataBlockFacetRoot()).thenReturn(metadataBlockFacetRoot); + UpdateMetadataBlockFacetRootCommand target = new UpdateMetadataBlockFacetRootCommand(dataverseRequest, dataverse, metadataBlockFacetRoot); + + CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); + target.execute(context); + + Mockito.verify(dataverse).isMetadataBlockFacetRoot(); + Mockito.verify(dataverse).setMetadataBlockFacetRoot(metadataBlockFacetRoot); + Mockito.verifyNoMoreInteractions(dataverse); + Mockito.verify(context.dataverses()).save(dataverse); + } + + @Test + public void should_set_metadataBlockFacetRoot_and_update_metadata_block_facets_to_empty_list_when_root_value_changes_to_false() throws CommandException { + Mockito.when(dataverse.isMetadataBlockFacetRoot()).thenReturn(true); + UpdateMetadataBlockFacetRootCommand target = new UpdateMetadataBlockFacetRootCommand(dataverseRequest, dataverse, false); + + CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); + target.execute(context); + + Mockito.verify(dataverse).isMetadataBlockFacetRoot(); + Mockito.verify(dataverse).setMetadataBlockFacetRoot(false); + Mockito.verify(dataverse).setMetadataBlockFacets(Collections.emptyList()); + Mockito.verifyNoMoreInteractions(dataverse); + Mockito.verify(context.dataverses()).save(dataverse); + } + + @Test + public void should_set_metadataBlockFacetRoot_and_update_metadata_block_facets_to_parent_list_when_root_value_changes_to_true() throws CommandException { + Dataverse parentDataverse = Mockito.mock(Dataverse.class); + DataverseMetadataBlockFacet blockFacet1 = new DataverseMetadataBlockFacet(); + MetadataBlock block1 = Mockito.mock(MetadataBlock.class); + blockFacet1.setDataverse(parentDataverse); + blockFacet1.setMetadataBlock(block1); + DataverseMetadataBlockFacet blockFacet2 = new DataverseMetadataBlockFacet(); + MetadataBlock block2 = Mockito.mock(MetadataBlock.class); + blockFacet2.setDataverse(parentDataverse); + blockFacet2.setMetadataBlock(block2); + Mockito.when(dataverse.isMetadataBlockFacetRoot()).thenReturn(false); + Mockito.when(dataverse.getMetadataBlockFacets()).thenReturn(Arrays.asList(blockFacet1, blockFacet2)); + UpdateMetadataBlockFacetRootCommand target = new UpdateMetadataBlockFacetRootCommand(dataverseRequest, dataverse, true); + + CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); + target.execute(context); + + Mockito.verify(dataverse).isMetadataBlockFacetRoot(); + Mockito.verify(dataverse).getMetadataBlockFacets(); + Mockito.verify(dataverse).setMetadataBlockFacetRoot(true); + + ArgumentCaptor> createdBlockFacets = ArgumentCaptor.forClass(List.class); + Mockito.verify(dataverse).setMetadataBlockFacets(createdBlockFacets.capture()); + MatcherAssert.assertThat(createdBlockFacets.getValue().size(), Matchers.is(2)); + MatcherAssert.assertThat(createdBlockFacets.getValue().get(0).getDataverse(), Matchers.is(dataverse)); + MatcherAssert.assertThat(createdBlockFacets.getValue().get(0).getMetadataBlock(), Matchers.is(block1)); + MatcherAssert.assertThat(createdBlockFacets.getValue().get(1).getDataverse(), Matchers.is(dataverse)); + MatcherAssert.assertThat(createdBlockFacets.getValue().get(1).getMetadataBlock(), Matchers.is(block2)); + Mockito.verifyNoMoreInteractions(dataverse); + Mockito.verify(context.dataverses()).save(dataverse); + } + + @Test + public void getEditedDataverse_should_return_set_dataverse() { + UpdateMetadataBlockFacetRootCommand target = new UpdateMetadataBlockFacetRootCommand(dataverseRequest, dataverse, true); + + MatcherAssert.assertThat(target.getEditedDataverse(), Matchers.is(dataverse)); + } + + @Test + public void getMetadataBlockFacetRoot_should_return_set_metadata_block_facet_root() { + UpdateMetadataBlockFacetRootCommand target = new UpdateMetadataBlockFacetRootCommand(dataverseRequest, dataverse, true); + + MatcherAssert.assertThat(target.getMetadataBlockFacetRoot(), Matchers.is(true)); + } + +} \ No newline at end of file diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java index 766398926a9..2d64de80f3d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetsCommandTest.java @@ -5,6 +5,7 @@ import edu.harvard.iq.dataverse.engine.command.CommandContext; import edu.harvard.iq.dataverse.engine.command.DataverseRequest; import edu.harvard.iq.dataverse.engine.command.exception.CommandException; +import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException; import edu.harvard.iq.dataverse.mocks.MocksFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -31,8 +32,19 @@ public void beforeEachTest() { dataverse = Mockito.mock(Dataverse.class); } + @Test(expected = IllegalCommandException.class) + public void should_throw_IllegalCommandException_when_dataverse_is_not_metadata_facet_root() throws CommandException { + Mockito.when(dataverse.isMetadataBlockFacetRoot()).thenReturn(false); + + UpdateMetadataBlockFacetsCommand target = new UpdateMetadataBlockFacetsCommand(dataverseRequest, dataverse, Collections.emptyList()); + + CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); + target.execute(context); + } + @Test - public void should_set_metadataBlockFacetRoot_to_true_and_update_facets() throws CommandException { + public void should_update_facets() throws CommandException { + Mockito.when(dataverse.isMetadataBlockFacetRoot()).thenReturn(true); DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet(); metadataBlockFacet.setId(MocksFactory.nextId()); List metadataBlockFacets = Arrays.asList(metadataBlockFacet); @@ -42,7 +54,6 @@ public void should_set_metadataBlockFacetRoot_to_true_and_update_facets() throws CommandContext context = Mockito.mock(CommandContext.class, Mockito.RETURNS_DEEP_STUBS); target.execute(context); - Mockito.verify(dataverse).setMetadataBlockFacetRoot(true); Mockito.verify(dataverse).setMetadataBlockFacets(metadataBlockFacets); Mockito.verify(context.dataverses()).save(dataverse); } From f3d2ee158adb1e102d29b02fe632f57de8687f36 Mon Sep 17 00:00:00 2001 From: Aday Bujeda Date: Tue, 16 Aug 2022 19:07:47 +0100 Subject: [PATCH 5/6] Updated updateMetadataBlockFacetRoot API to only update when there is a change in value --- .../edu/harvard/iq/dataverse/api/Dataverses.java | 4 ++++ .../impl/UpdateMetadataBlockFacetRootCommand.java | 7 ++++--- ....sql => V5.11.1.5__8536-metadata-block-facet.sql} | 0 .../edu/harvard/iq/dataverse/api/DataversesTest.java | 12 +++++++++++- .../UpdateMetadataBlockFacetRootCommandTest.java | 5 ++--- 5 files changed, 21 insertions(+), 7 deletions(-) rename src/main/resources/db/migration/{V5.11.1.2__8536-metadata-block-facet.sql => V5.11.1.5__8536-metadata-block-facet.sql} (100%) 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 c84cfe070c2..90130cb3944 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -774,6 +774,10 @@ public Response updateMetadataBlockFacetsRoot(@PathParam("identifier") String dv try { final boolean blockFacetsRoot = parseBooleanOrDie(body); Dataverse dataverse = findDataverseOrDie(dvIdtf); + if(dataverse.isMetadataBlockFacetRoot() == blockFacetsRoot) { + return ok(String.format("No update needed, dataverse already consistent with new value. DataverseId: %s blockFacetsRoot: %s", dvIdtf, blockFacetsRoot)); + } + execCommand(new UpdateMetadataBlockFacetRootCommand(createDataverseRequest(findUserOrDie()), dataverse, blockFacetsRoot)); return ok(String.format("Metadata block facets root updated. DataverseId: %s blockFacetsRoot: %s", dvIdtf, blockFacetsRoot)); diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java index 32ef7d54a7e..2e5b6b59ebe 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommand.java @@ -45,11 +45,12 @@ public Dataverse execute(CommandContext ctxt) throws CommandException { }).collect(Collectors.toList()); } editedDv.setMetadataBlockFacets(newBlockFacets); + + editedDv.setMetadataBlockFacetRoot(metadataBlockFacetRoot); + return ctxt.dataverses().save(editedDv); } - editedDv.setMetadataBlockFacetRoot(metadataBlockFacetRoot); - Dataverse updated = ctxt.dataverses().save(editedDv); - return updated; + return editedDv; } // Visible for testing diff --git a/src/main/resources/db/migration/V5.11.1.2__8536-metadata-block-facet.sql b/src/main/resources/db/migration/V5.11.1.5__8536-metadata-block-facet.sql similarity index 100% rename from src/main/resources/db/migration/V5.11.1.2__8536-metadata-block-facet.sql rename to src/main/resources/db/migration/V5.11.1.5__8536-metadata-block-facet.sql diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java index a25dbaa1f1b..10113110b66 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataversesTest.java @@ -215,9 +215,19 @@ public void updateMetadataBlockFacetsRoot_should_return_400_when_invalid_boolean } @Test - public void updateMetadataBlockFacetsRoot_should_return_200_when_dataverse_is_found() throws Exception{ + public void updateMetadataBlockFacetsRoot_should_return_200_and_make_no_update_when_dataverse_is_found_and_facet_root_has_not_changed() { + // VALID_DATAVERSE.metadataBlockFacetRoot is true Response result = target.updateMetadataBlockFacetsRoot(VALID_DATAVERSE.getAlias(), "true"); + MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); + Mockito.verifyZeroInteractions(engineSvc); + } + + @Test + public void updateMetadataBlockFacetsRoot_should_return_200_and_execute_command_when_dataverse_is_found_and_facet_root_has_changed() throws Exception { + // VALID_DATAVERSE.metadataBlockFacetRoot is true + Response result = target.updateMetadataBlockFacetsRoot(VALID_DATAVERSE.getAlias(), "false"); + MatcherAssert.assertThat(result.getStatus(), Matchers.is(200)); ArgumentCaptor updateRootCommand = ArgumentCaptor.forClass(UpdateMetadataBlockFacetRootCommand.class); Mockito.verify(engineSvc).submit(updateRootCommand.capture()); diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java index 05a4adbb512..711e7881af5 100644 --- a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateMetadataBlockFacetRootCommandTest.java @@ -33,7 +33,7 @@ public void beforeEachTest() { } @Test - public void should_set_metadataBlockFacetRoot_and_not_update_metadata_block_facets_when_root_value_does_not_change() throws CommandException { + public void should_not_update_dataverse_when_root_value_does_not_change() throws CommandException { boolean metadataBlockFacetRoot = true; Mockito.when(dataverse.isMetadataBlockFacetRoot()).thenReturn(metadataBlockFacetRoot); UpdateMetadataBlockFacetRootCommand target = new UpdateMetadataBlockFacetRootCommand(dataverseRequest, dataverse, metadataBlockFacetRoot); @@ -42,9 +42,8 @@ public void should_set_metadataBlockFacetRoot_and_not_update_metadata_block_face target.execute(context); Mockito.verify(dataverse).isMetadataBlockFacetRoot(); - Mockito.verify(dataverse).setMetadataBlockFacetRoot(metadataBlockFacetRoot); Mockito.verifyNoMoreInteractions(dataverse); - Mockito.verify(context.dataverses()).save(dataverse); + Mockito.verifyZeroInteractions(context.dataverses()); } @Test From 34f64cd203e501dff60e85b2addf55d93ec069ce Mon Sep 17 00:00:00 2001 From: Aday Bujeda Date: Thu, 18 Aug 2022 17:38:30 +0100 Subject: [PATCH 6/6] Improved Documentation for Metadata Blocks Facet API --- doc/sphinx-guides/source/api/native-api.rst | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index d6a641b6544..1c00053ef4f 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -253,7 +253,9 @@ Set Metadata Block Facets for a Dataverse Collection Sets the metadata blocks that will appear in the ``Dataset Features`` facet category for a given Dataverse collection identified by ``id``. -To clear the metadata blocks set by a parent dataverse, submit an empty array (e.g. ``[]``): +In order to set or clear the metadata blocks for a collection, you must first :ref:`set the metadata block facet root to true`. + +To clear the metadata blocks set by a parent collection, submit an empty array (e.g. ``[]``): .. code-block:: bash @@ -261,22 +263,24 @@ To clear the metadata blocks set by a parent dataverse, submit an empty array (e export SERVER_URL=https://demo.dataverse.org export ID=root - curl -H X-Dataverse-key:$API_TOKEN" -X POST $SERVER_URL/api/dataverses/$ID/metadatablockfacets --upload-file metadata-block-facets.json + curl -H X-Dataverse-key:$API_TOKEN" -X POST -H "Content-type:application/json" $SERVER_URL/api/dataverses/$ID/metadatablockfacets --upload-file metadata-block-facets.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/root/metadatablockfacets --upload-file metadata-block-facets.json + curl -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -X POST -H "Content-type:application/json" https://demo.dataverse.org/api/dataverses/root/metadatablockfacets --upload-file metadata-block-facets.json Where :download:`metadata-block-facets.json <../_static/api/metadata-block-facets.json>` contains a JSON encoded list of metadata block names (e.g. ``["socialscience","geospatial"]``). This endpoint supports an empty list (e.g. ``[]``) +.. _metadata-block-facet-root-api: + Configure a Dataverse Collection to Inherit Its Metadata Block Facets from Its Parent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set whether the Dataverse collection is a metadata block facet root, or does it uses its parent metadata block facets. Possible values are ``true`` and ``false`` (both are valid JSON expressions). -When updating the root to false, it will clear any metadata block facets from the dataverse. When updating to true, it will copy the metadata block facets from the parent dataverse: +When updating the root to false, it will clear any metadata block facets from the collection. When updating to true, it will copy the metadata block facets from the parent collection: .. code-block:: bash @@ -284,13 +288,13 @@ When updating the root to false, it will clear any metadata block facets from th export SERVER_URL=https://demo.dataverse.org export ID=root - curl -H X-Dataverse-key:$API_TOKEN -X POST $SERVER_URL/api/dataverses/$ID/metadatablockfacets/isRoot -d 'true' + curl -H X-Dataverse-key:$API_TOKEN -X POST -H "Content-type:application/json" $SERVER_URL/api/dataverses/$ID/metadatablockfacets/isRoot -d 'true' 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/root/metadatablockfacets/isRoot -d 'true' + curl -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -X POST -H "Content-type:application/json" https://demo.dataverse.org/api/dataverses/root/metadatablockfacets/isRoot -d 'true' Create a New Role in a Dataverse Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~