From acb2ec32f5dd54f764d11fc0c8201dd327d4bac1 Mon Sep 17 00:00:00 2001 From: Leonid Andreev Date: Wed, 16 Feb 2022 10:46:42 -0500 Subject: [PATCH 1/5] Basic framework for a lock-listing API. Work in progress, lots to figure out/finalize. (IQSS/dataverse.harvard.edu#135) --- .../edu/harvard/iq/dataverse/DatasetLock.java | 8 +++-- .../iq/dataverse/DatasetServiceBean.java | 11 +++++++ .../harvard/iq/dataverse/api/Datasets.java | 30 ++++++++++++++++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java b/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java index 93f4aca13d1..20c71110215 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java @@ -53,8 +53,12 @@ @NamedQueries({ @NamedQuery(name = "DatasetLock.getLocksByDatasetId", query = "SELECT lock FROM DatasetLock lock WHERE lock.dataset.id=:datasetId"), - @NamedQuery(name = "DatasetLock.getLocksByAuthenticatedUserId", - query = "SELECT lock FROM DatasetLock lock WHERE lock.user.id=:authenticatedUserId") + @NamedQuery(name = "DatasetLock.getLocksByType", + query = "SELECT lock FROM DatasetLock lock WHERE lock.reason=:lockType"), + @NamedQuery(name = "DatasetLock.getLocksByAuthenticatedUserId", + query = "SELECT lock FROM DatasetLock lock WHERE lock.user.id=:authenticatedUserId"), + @NamedQuery(name = "DatasetLock.getLocksByTypeAndAuthenticatedUserId", + query = "SELECT lock FROM DatasetLock lock WHERE lock.reason=:lockType AND lock.user.id=:authenticatedUserId") } ) public class DatasetLock implements Serializable { diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java index 8ebdc4745e6..ae84fe728be 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java @@ -533,6 +533,17 @@ public void updateDatasetLock(DatasetLock datasetLock) { em.merge(datasetLock); } + public List getDatasetLocksByType(DatasetLock.Reason lockType) { + + TypedQuery query = em.createNamedQuery("DatasetLock.getLocksByType", DatasetLock.class); + query.setParameter("lockType", lockType); + try { + return query.getResultList(); + } catch (javax.persistence.NoResultException e) { + return null; + } + } + /* getTitleFromLatestVersion methods use native query to return a dataset title diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java index 0d8f60119db..c51badb9941 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java @@ -2517,7 +2517,7 @@ public Command handleLatestPublished() { @GET @Path("{identifier}/locks") - public Response getLocks(@PathParam("identifier") String id, @QueryParam("type") DatasetLock.Reason lockType) { + public Response getLocksForDataset(@PathParam("identifier") String id, @QueryParam("type") DatasetLock.Reason lockType) { Dataset dataset = null; try { @@ -2642,6 +2642,34 @@ public Response lockDataset(@PathParam("identifier") String id, @PathParam("type }); } + @GET + @Path("locks") + public Response listLocks(@QueryParam("type") DatasetLock.Reason lockType) { + // This API is here, under /datasets, and not under /admin, because we + // likely want it to be accessible to admin users who may not necessarily + // have localhost access, that would be required to get to /api/admin in + // most installations. It is still reasonable however to limit access to + // this api to admin users only. (?) + AuthenticatedUser user; + try { + user = findAuthenticatedUserOrDie(); + } catch (WrappedResponse ex) { + return error(Response.Status.UNAUTHORIZED, "Authentication is required."); + } + if (!user.isSuperuser()) { + return error(Response.Status.FORBIDDEN, "Superusers only."); + } + + if (lockType == null) { + // Not 100% sure if it really needs to be a required parameter really. + return error(Response.Status.BAD_REQUEST, "Required parameter missing: type"); + } + List locks = datasetService.getDatasetLocksByType(lockType); + + return ok(locks.stream().map(lock -> json(lock)).collect(toJsonArray())); + } + + @GET @Path("{id}/makeDataCount/citations") public Response getMakeDataCountCitations(@PathParam("id") String idSupplied) { From ef636435efe8c6737597da1e8dbce89f36b5d6e8 Mon Sep 17 00:00:00 2001 From: Leonid Andreev Date: Fri, 18 Feb 2022 20:51:07 -0500 Subject: [PATCH 2/5] Expanding the locks API to list locks installation-wide, across all datasets. (IQSS/dataverse.harvard.edu#135) --- doc/sphinx-guides/source/api/native-api.rst | 38 +++++++- .../edu/harvard/iq/dataverse/DatasetLock.java | 8 +- .../iq/dataverse/DatasetServiceBean.java | 33 +++++-- .../harvard/iq/dataverse/api/Datasets.java | 42 +++++++-- .../iq/dataverse/util/json/JsonPrinter.java | 3 +- .../harvard/iq/dataverse/api/DatasetsIT.java | 93 ++++++++++++++++++- .../edu/harvard/iq/dataverse/api/UtilIT.java | 28 ++++++ 7 files changed, 217 insertions(+), 28 deletions(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index f8c3a3ef1ff..391f8f88b0e 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -1520,6 +1520,9 @@ The fully expanded example above (without environment variables) looks like this Dataset Locks ~~~~~~~~~~~~~ +Manage Locks on a Specific Dataset +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + To check if a dataset is locked: .. code-block:: bash @@ -1551,7 +1554,7 @@ The fully expanded example above (without environment variables) looks like this curl "https://demo.dataverse.org/api/datasets/24/locks?type=Ingest" -Currently implemented lock types are ``Ingest``, ``Workflow``, ``InReview``, ``DcmUpload``, ``pidRegister``, and ``EditInProgress``. +Currently implemented lock types are ``Ingest``, ``Workflow``, ``InReview``, ``DcmUpload``, ``finalizePublication``, ``EditInProgress`` and ``FileValidationFailed``. The API will output the list of locks, for example:: @@ -1560,12 +1563,14 @@ The API will output the list of locks, for example:: { "lockType":"Ingest", "date":"Fri Aug 17 15:05:51 EDT 2018", - "user":"dataverseAdmin" + "user":"dataverseAdmin", + "dataset":"doi:12.34567/FK2/ABCDEF" }, { "lockType":"Workflow", "date":"Fri Aug 17 15:02:00 EDT 2018", - "user":"dataverseAdmin" + "user":"dataverseAdmin", + "dataset":"doi:12.34567/FK2/ABCDEF" } ] } @@ -1612,7 +1617,7 @@ Or, to delete a lock of the type specified only. Note that this requires “supe export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx export SERVER_URL=https://demo.dataverse.org export ID=24 - export LOCK_TYPE=pidRegister + export LOCK_TYPE=finalizePublication curl -H "X-Dataverse-key: $API_TOKEN" -X DELETE $SERVER_URL/api/datasets/$ID/locks?type=$LOCK_TYPE @@ -1620,12 +1625,35 @@ 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/datasets/24/locks?type=pidRegister + curl -H "X-Dataverse-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X DELETE https://demo.dataverse.org/api/datasets/24/locks?type=finalizePublication If the dataset is not locked (or if there is no lock of the specified type), the API will exit with a warning message. (Note that the API calls above all support both the database id and persistent identifier notation for referencing the dataset) +List Locks Across All Datasets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Note that this API requires “superuser” credentials. You must supply the ``X-Dataverse-key`` header with the api token of an admin user (as in the example above). + +The output of this API is formatted identically to the API that lists the locks for a specific dataset, as in one of the examples above. + +Use the following API to list ALL the locks on all the datasets in your installation + + ``/api/datasets/locks`` + +The listing can be filtered by specific lock type **and/or** user, using the following *optional* query parameters: + +* ``userIdentifier`` - To list the locks owned by a specific user +* ``type`` - To list the locks of the type specified. If the supplied value does not match a known lock type, the API will return an error and a list of valid lock types. As of writing this, the implemented lock types are ``Ingest``, ``Workflow``, ``InReview``, ``DcmUpload``, ``finalizePublication``, ``EditInProgress`` and ``FileValidationFailed``. + +For example: + +.. code-block:: bash + + curl "http://localhost:8080/api/datasets/locks?type=Ingest&userIdentifier=davis4ever" + + .. _dataset-metrics-api: Dataset Metrics diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java b/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java index 20c71110215..d0ba86ab68e 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetLock.java @@ -51,14 +51,16 @@ @Entity @Table(indexes = {@Index(columnList="user_id"), @Index(columnList="dataset_id")}) @NamedQueries({ + @NamedQuery(name = "DatasetLock.findAll", + query="SELECT lock FROM DatasetLock lock ORDER BY lock.id"), @NamedQuery(name = "DatasetLock.getLocksByDatasetId", query = "SELECT lock FROM DatasetLock lock WHERE lock.dataset.id=:datasetId"), @NamedQuery(name = "DatasetLock.getLocksByType", - query = "SELECT lock FROM DatasetLock lock WHERE lock.reason=:lockType"), + query = "SELECT lock FROM DatasetLock lock WHERE lock.reason=:lockType ORDER BY lock.id"), @NamedQuery(name = "DatasetLock.getLocksByAuthenticatedUserId", - query = "SELECT lock FROM DatasetLock lock WHERE lock.user.id=:authenticatedUserId"), + query = "SELECT lock FROM DatasetLock lock WHERE lock.user.id=:authenticatedUserId ORDER BY lock.id"), @NamedQuery(name = "DatasetLock.getLocksByTypeAndAuthenticatedUserId", - query = "SELECT lock FROM DatasetLock lock WHERE lock.reason=:lockType AND lock.user.id=:authenticatedUserId") + query = "SELECT lock FROM DatasetLock lock WHERE lock.reason=:lockType AND lock.user.id=:authenticatedUserId ORDER BY lock.id") } ) public class DatasetLock implements Serializable { diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java index ae84fe728be..f5a4acdffb8 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java @@ -452,13 +452,7 @@ public boolean checkDatasetLock(Long datasetId) { public List getDatasetLocksByUser( AuthenticatedUser user) { - TypedQuery query = em.createNamedQuery("DatasetLock.getLocksByAuthenticatedUserId", DatasetLock.class); - query.setParameter("authenticatedUserId", user.getId()); - try { - return query.getResultList(); - } catch (javax.persistence.NoResultException e) { - return null; - } + return listLocks(null, user); } @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) @@ -533,10 +527,29 @@ public void updateDatasetLock(DatasetLock datasetLock) { em.merge(datasetLock); } - public List getDatasetLocksByType(DatasetLock.Reason lockType) { + /* + * Lists all dataset locks, optionally filtered by lock type or user, or both + * @param lockType + * @param user + * @return a list of DatasetLocks + */ + public List listLocks(DatasetLock.Reason lockType, AuthenticatedUser user) { - TypedQuery query = em.createNamedQuery("DatasetLock.getLocksByType", DatasetLock.class); - query.setParameter("lockType", lockType); + TypedQuery query; + + if (lockType == null && user == null) { + query = em.createNamedQuery("DatasetLock.findAll", DatasetLock.class); + } else if (user == null) { + query = em.createNamedQuery("DatasetLock.getLocksByType", DatasetLock.class); + query.setParameter("lockType", lockType); + } else if (lockType == null) { + query = em.createNamedQuery("DatasetLock.getLocksByAuthenticatedUserId", DatasetLock.class); + query.setParameter("authenticatedUserId", user.getId()); + } else { + query = em.createNamedQuery("DatasetLock.getLocksByTypeAndAuthenticatedUserId", DatasetLock.class); + query.setParameter("lockType", lockType); + query.setParameter("authenticatedUserId", user.getId()); + } try { return query.getResultList(); } catch (javax.persistence.NoResultException e) { diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java index c51badb9941..5676fad8d5b 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java @@ -2644,27 +2644,53 @@ public Response lockDataset(@PathParam("identifier") String id, @PathParam("type @GET @Path("locks") - public Response listLocks(@QueryParam("type") DatasetLock.Reason lockType) { + public Response listLocks(@QueryParam("type") String lockType, @QueryParam("userIdentifier") String userIdentifier) { //DatasetLock.Reason lockType) { // This API is here, under /datasets, and not under /admin, because we // likely want it to be accessible to admin users who may not necessarily // have localhost access, that would be required to get to /api/admin in // most installations. It is still reasonable however to limit access to // this api to admin users only. (?) - AuthenticatedUser user; + AuthenticatedUser apiUser; try { - user = findAuthenticatedUserOrDie(); + apiUser = findAuthenticatedUserOrDie(); } catch (WrappedResponse ex) { return error(Response.Status.UNAUTHORIZED, "Authentication is required."); } - if (!user.isSuperuser()) { + if (!apiUser.isSuperuser()) { return error(Response.Status.FORBIDDEN, "Superusers only."); } - if (lockType == null) { - // Not 100% sure if it really needs to be a required parameter really. - return error(Response.Status.BAD_REQUEST, "Required parameter missing: type"); + // Locks can be optinally filtered by type, user or both. + DatasetLock.Reason lockTypeValue = null; + AuthenticatedUser user = null; + + // For the lock type, we use a QueryParam of type String, instead of + // DatasetLock.Reason; that would be less code to write, but this way + // we can check if the value passed matches a valid lock type ("reason") + // and provide a helpful error message if it doesn't. If you use a + // QueryParam of an Enum type, trying to pass an invalid value to it + // results in a potentially confusing "404/NOT FOUND - requested + // resource is not available". + if (lockType != null && !lockType.isEmpty()) { + try { + lockTypeValue = DatasetLock.Reason.valueOf(lockType); + } catch (IllegalArgumentException iax) { + String validValues = Strings.join(",", DatasetLock.Reason.values()); + String errorMessage = "Invalid lock type value: " + lockType + + "; valid lock types: " + validValues; + return error(Response.Status.BAD_REQUEST, errorMessage); + } + } + + if (userIdentifier != null && !userIdentifier.isEmpty()) { + user = authSvc.getAuthenticatedUser(userIdentifier); + if (user == null) { + return error(Response.Status.BAD_REQUEST, "Unknown user identifier: "+userIdentifier); + } } - List locks = datasetService.getDatasetLocksByType(lockType); + + //List locks = datasetService.getDatasetLocksByType(lockType); + List locks = datasetService.listLocks(lockTypeValue, user); return ok(locks.stream().map(lock -> json(lock)).collect(toJsonArray())); } diff --git a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java index 39c84562a09..ccde79bf233 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java @@ -28,6 +28,7 @@ import edu.harvard.iq.dataverse.util.SystemConfig; import edu.harvard.iq.dataverse.workflow.Workflow; import edu.harvard.iq.dataverse.workflow.step.WorkflowStepData; +import java.math.BigDecimal; import java.net.URISyntaxException; import java.util.*; @@ -135,8 +136,8 @@ public static JsonObjectBuilder json(DatasetLock lock) { .add("lockType", lock.getReason().toString()) .add("date", lock.getStartTime().toString()) .add("user", lock.getUser().getUserIdentifier()) + .add("dataset", lock.getDataset().getGlobalId().asString()) .add("message", lock.getInfo()); - } public static JsonObjectBuilder json( RoleAssigneeDisplayInfo d ) { diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java index c08a71eea65..23c17c071ff 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java @@ -1819,6 +1819,7 @@ public void testDatasetLocksApi() { Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); createDatasetResponse.prettyPrint(); Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); + String persistentIdentifier = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); // This should return an empty list, as the dataset should have no locks just yet: Response checkDatasetLocks = UtilIT.checkDatasetLocks(datasetId.longValue(), null, apiToken); @@ -1850,7 +1851,97 @@ public void testDatasetLocksApi() { lockDatasetResponse.then().assertThat() .body("message", equalTo("dataset already locked with lock type Ingest")) .statusCode(FORBIDDEN.getStatusCode()); - + + // Let's also test the new (as of 5.10) API that lists the locks + // present across all datasets. + + // First, we'll try listing ALL locks currently in the system, and make sure that the ingest lock + // for this dataset is on the list: + checkDatasetLocks = UtilIT.listAllLocks(apiToken); + checkDatasetLocks.prettyPrint(); + checkDatasetLocks.then().assertThat() + .statusCode(200); + + boolean lockListedCorrectly = false; + List> listedLockEntries = checkDatasetLocks.body().jsonPath().getList("data"); + for (int i = 0; i < listedLockEntries.size(); i++) { + if ("Ingest".equals(listedLockEntries.get(i).get("lockType")) + && username.equals(listedLockEntries.get(i).get("user")) + && persistentIdentifier.equals(listedLockEntries.get(i).get("dataset"))) { + lockListedCorrectly = true; + break; + } + } + assertTrue("Lock missing from the output of /api/datasets/locks", lockListedCorrectly); + + // Try the same, but with an api token of a random, non-super user + // (this should get rejected): + createUser = UtilIT.createRandomUser(); + createUser.prettyPrint(); + String wrongApiToken = UtilIT.getApiTokenFromResponse(createUser); + checkDatasetLocks = UtilIT.listAllLocks(wrongApiToken); + checkDatasetLocks.prettyPrint(); + checkDatasetLocks.then().assertThat() + .statusCode(FORBIDDEN.getStatusCode()); + + // Try to narrow the listing down to the lock of type=Ingest specifically; + // verify that the lock in question is still being listed: + checkDatasetLocks = UtilIT.listLocksByType("Ingest", apiToken); + checkDatasetLocks.prettyPrint(); + // We'll again assume that it's possible that the API is going to list + // *multiple* locks; i.e. that there are other datasets with the lock + // of type "Ingest" on them. So we'll go through the list and look for the + // lock for this specific dataset again. + lockListedCorrectly = false; + listedLockEntries = checkDatasetLocks.body().jsonPath().getList("data"); + for (int i = 0; i < listedLockEntries.size(); i++) { + if ("Ingest".equals(listedLockEntries.get(i).get("lockType")) + && username.equals(listedLockEntries.get(i).get("user")) + && persistentIdentifier.equals(listedLockEntries.get(i).get("dataset"))) { + lockListedCorrectly = true; + break; + } + } + assertTrue("Lock missing from the output of /api/datasets/locks?type=Ingest", lockListedCorrectly); + + + // Try to list locks of an invalid type: + checkDatasetLocks = UtilIT.listLocksByType("BadLockType", apiToken); + checkDatasetLocks.prettyPrint(); + checkDatasetLocks.then().assertThat() + .body("message", startsWith("Invalid lock type value: BadLockType")) + .statusCode(BAD_REQUEST.getStatusCode()); + + // List the locks owned by the current user; verify that the lock above + // is still listed: + checkDatasetLocks = UtilIT.listLocksByUser(username, apiToken); + checkDatasetLocks.prettyPrint(); + // Safe to assume there should be only one: + checkDatasetLocks.then().assertThat() + .body("data[0].lockType", equalTo("Ingest")) + .body("data[0].user", equalTo(username)) + .body("data[0].dataset", equalTo(persistentIdentifier)) + .statusCode(200); + + // Further narrow down the listing to both the type AND user: + checkDatasetLocks = UtilIT.listLocksByTypeAndUser("Ingest", username, apiToken); + checkDatasetLocks.prettyPrint(); + // Even safer to assume there should be only one: + checkDatasetLocks.then().assertThat() + .statusCode(200) + .body("data[0].lockType", equalTo("Ingest")) + .body("data[0].user", equalTo(username)) + .body("data[0].dataset", equalTo(persistentIdentifier)); + + + // Finally, try asking for the locks owned by this user AND of type "InReview". + // This should produce an empty list: + checkDatasetLocks = UtilIT.listLocksByTypeAndUser("InReview", username, apiToken); + checkDatasetLocks.prettyPrint(); + checkDatasetLocks.then().assertThat() + .statusCode(200) + .body("data", equalTo(emptyArray)); + // And now test deleting the lock: Response unlockDatasetResponse = UtilIT.unlockDataset(datasetId.longValue(), "Ingest", apiToken); unlockDatasetResponse.prettyPrint(); diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java index 5ccccd8ec08..e9623599f51 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -2396,6 +2396,34 @@ static Response checkDatasetLocks(String idOrPersistentId, String lockType, Stri return response; } + static Response listAllLocks(String apiToken) { + Response response = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .get("api/datasets/locks"); + return response; + } + + static Response listLocksByType(String lockType, String apiToken) { + Response response = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .get("api/datasets/locks?type="+lockType); + return response; + } + + static Response listLocksByUser(String userIdentifier, String apiToken) { + Response response = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .get("api/datasets/locks?userIdentifier="+userIdentifier); + return response; + } + + static Response listLocksByTypeAndUser(String lockType, String userIdentifier, String apiToken) { + Response response = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .get("api/datasets/locks?type="+lockType+"&userIdentifier="+userIdentifier); + return response; + } + static Response lockDataset(long datasetId, String lockType, String apiToken) { Response response = given() .header(API_TOKEN_HTTP_HEADER, apiToken) From 9ba691f62fe8543c1ff378f3939a731e111f2068 Mon Sep 17 00:00:00 2001 From: Leonid Andreev Date: Fri, 18 Feb 2022 21:02:22 -0500 Subject: [PATCH 3/5] does this need a colon? (IQSS/dataverse.harvard.edu#135) --- doc/sphinx-guides/source/api/native-api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 391f8f88b0e..dce5ff5c039 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -1638,7 +1638,7 @@ Note that this API requires “superuser” credentials. You must supply the ``X The output of this API is formatted identically to the API that lists the locks for a specific dataset, as in one of the examples above. -Use the following API to list ALL the locks on all the datasets in your installation +Use the following API to list ALL the locks on all the datasets in your installation: ``/api/datasets/locks`` From 5bf349ff7af172d129b915eb8e8eaad1998fd41f Mon Sep 17 00:00:00 2001 From: Leonid Andreev Date: Fri, 18 Feb 2022 21:09:10 -0500 Subject: [PATCH 4/5] adding the api token to the api example (IQSS/dataverse.harvard.edu#135) --- doc/sphinx-guides/source/api/native-api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index dce5ff5c039..5be7edbc091 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -1634,7 +1634,7 @@ If the dataset is not locked (or if there is no lock of the specified type), the List Locks Across All Datasets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Note that this API requires “superuser” credentials. You must supply the ``X-Dataverse-key`` header with the api token of an admin user (as in the example above). +Note that this API requires “superuser” credentials. You must supply the ``X-Dataverse-key`` header with the api token of an admin user (as in the example below). The output of this API is formatted identically to the API that lists the locks for a specific dataset, as in one of the examples above. @@ -1651,7 +1651,7 @@ For example: .. code-block:: bash - curl "http://localhost:8080/api/datasets/locks?type=Ingest&userIdentifier=davis4ever" + curl -H "X-Dataverse-key: xxx" "http://localhost:8080/api/datasets/locks?type=Ingest&userIdentifier=davis4ever" .. _dataset-metrics-api: From 0905ad38ea61a00179af4d2022fbe365e3375ab8 Mon Sep 17 00:00:00 2001 From: Leonid Andreev Date: Wed, 23 Feb 2022 14:51:19 -0500 Subject: [PATCH 5/5] minor/cosmetic. IQSS/dataverse.harvard.edu#135 --- src/main/java/edu/harvard/iq/dataverse/api/Datasets.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java index 5676fad8d5b..dfdce43e895 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java @@ -2649,7 +2649,7 @@ public Response listLocks(@QueryParam("type") String lockType, @QueryParam("user // likely want it to be accessible to admin users who may not necessarily // have localhost access, that would be required to get to /api/admin in // most installations. It is still reasonable however to limit access to - // this api to admin users only. (?) + // this api to admin users only. AuthenticatedUser apiUser; try { apiUser = findAuthenticatedUserOrDie();