Skip to content

Commit

Permalink
make configure tools available at the dataset level #9589
Browse files Browse the repository at this point in the history
  • Loading branch information
pdurbin committed Sep 15, 2023
1 parent 4e1bc5b commit cbd395e
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 3 deletions.
1 change: 1 addition & 0 deletions doc/release-notes/9589-ds-configure-tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Configure tools are now available at the dataset level. They appear under the "Edit Dataset" menu. See also #9589.
2 changes: 1 addition & 1 deletion doc/sphinx-guides/source/admin/external-tools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Dataset level explore tools allow the user to explore all the files in a dataset
Dataset Level Configure Tools
+++++++++++++++++++++++++++++

Configure tools at the dataset level are not currently supported.
Dataset level configure tools can be launched by users who have edit access to the dataset and are found under the "Edit Dataset" menu.

Writing Your Own External Tool
------------------------------
Expand Down
4 changes: 2 additions & 2 deletions doc/sphinx-guides/source/api/external-tools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ How External Tools Are Presented to Users
An external tool can appear in your Dataverse installation in a variety of ways:

- as an explore, preview, query or configure option for a file
- as an explore option for a dataset
- as an explore or configure option for a dataset
- as an embedded preview on the file landing page

See also the :ref:`testing-external-tools` section of the Admin Guide for some perspective on how Dataverse installations will expect to test your tool before announcing it to their users.
Expand Down Expand Up @@ -92,7 +92,7 @@ Terminology

scope Whether the external tool appears and operates at the **file** level or the **dataset** level. Note that a file level tool much also specify the type of file it operates on (see "contentType" below).

types Whether the external tool is an **explore** tool, a **preview** tool, a **query** tool, a **configure** tool or any combination of these (multiple types are supported for a single tool). Configure tools require an API token because they make changes to data files (files within datasets). Configure tools are currently not supported at the dataset level. The older "type" keyword that allows you to pass a single type as a string is deprecated but still supported.
types Whether the external tool is an **explore** tool, a **preview** tool, a **query** tool, a **configure** tool or any combination of these (multiple types are supported for a single tool). Configure tools require an API token because they make changes to data files (files within datasets). The older "type" keyword that allows you to pass a single type as a string is deprecated but still supported.

toolUrl The **base URL** of the tool before query parameters are added.

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/DatasetPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ public void setShowIngestSuccess(boolean showIngestSuccess) {
Map<Long, List<ExternalTool>> fileQueryToolsByFileId = new HashMap<>();
List<ExternalTool> fileQueryTools = new ArrayList<>();
private List<ExternalTool> datasetExploreTools;
private List<ExternalTool> datasetConfigureTools;

public Boolean isHasRsyncScript() {
return hasRsyncScript;
Expand Down Expand Up @@ -2153,6 +2154,7 @@ private String init(boolean initFull) {
previewTools = externalToolService.findFileToolsByType(ExternalTool.Type.PREVIEW);
fileQueryTools = externalToolService.findFileToolsByType(ExternalTool.Type.QUERY);
datasetExploreTools = externalToolService.findDatasetToolsByType(ExternalTool.Type.EXPLORE);
datasetConfigureTools = externalToolService.findDatasetToolsByType(ExternalTool.Type.CONFIGURE);
rowsPerPage = 10;
if (dataset.getId() != null && canUpdateDataset()) {
hasRestrictedFiles = workingVersion.isHasRestrictedFile();
Expand Down Expand Up @@ -5572,6 +5574,10 @@ public List<ExternalTool> getDatasetExploreTools() {
return datasetExploreTools;
}

public List<ExternalTool> getDatasetConfigureTools() {
return datasetConfigureTools;
}

Boolean thisLatestReleasedVersion = null;

public boolean isThisLatestReleasedVersion() {
Expand Down Expand Up @@ -5789,6 +5795,16 @@ public void explore(ExternalTool externalTool) {
PrimeFaces.current().executeScript(externalToolHandler.getExploreScript());
}

public void configure(ExternalTool externalTool) {
ApiToken apiToken = null;
User user = session.getUser();
if (user instanceof AuthenticatedUser) {
apiToken = authService.findApiTokenByUser((AuthenticatedUser) user);
}
ExternalToolHandler externalToolHandler = new ExternalToolHandler(externalTool, dataset, apiToken, session.getLocaleCode());
PrimeFaces.current().executeScript(externalToolHandler.getConfigureScript());
}

private FileMetadata fileMetadataForAction;

public FileMetadata getFileMetadataForAction() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,11 @@ public String getExploreScript() {
logger.fine("Exploring with " + toolUrl);
return getScriptForUrl(toolUrl);
}

// TODO: Consider merging with getExploreScript
public String getConfigureScript() {
String toolUrl = this.getToolUrlWithQueryParams();
logger.fine("Configuring with " + toolUrl);
return getScriptForUrl(toolUrl);
}
}
1 change: 1 addition & 0 deletions src/main/java/propertyFiles/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,7 @@ dataset.pageTitle=Add New Dataset
dataset.accessBtn=Access Dataset
dataset.accessBtn.header.download=Download Options
dataset.accessBtn.header.explore=Explore Options
dataset.accessBtn.header.configure=Configure Options
dataset.accessBtn.header.compute=Compute Options
dataset.accessBtn.download.size=ZIP ({0})
dataset.accessBtn.too.big=The dataset is too large to download. Please select the files you need from the files table.
Expand Down
11 changes: 11 additions & 0 deletions src/main/webapp/dataset.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,17 @@
<h:outputText value="#{bundle['dataset.editBtn.itemLabel.thumbnailsAndWidgets']}"/>
</h:outputLink>
</li>
<ui:fragment rendered="#{DatasetPage.datasetConfigureTools.size() >= 1}">
<li class="dropdown-header">#{bundle['dataset.accessBtn.header.configure']} <span class="glyphicon glyphicon-wrench"/></li>
<!-- Configure tool links -->
<ui:repeat var="tool" value="#{DatasetPage.datasetConfigureTools}">
<li>
<h:commandLink styleClass="btn-explore" action="#{DatasetPage.explore(tool)}">
<h:outputText value="#{tool.getDisplayNameLang()}"/>
</h:commandLink>
</li>
</ui:repeat>
</ui:fragment>
<ui:fragment rendered="#{!DatasetPage.dataset.released and DatasetPage.dataset.latestVersion.versionState=='DRAFT' and permissionsWrapper.canIssueDeleteDatasetCommand(DatasetPage.dataset)}">
<li role="separator" class="divider"></li>
<li>
Expand Down
79 changes: 79 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/ExternalToolsIT.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.harvard.iq.dataverse.api;

import edu.harvard.iq.dataverse.util.json.JsonUtil;
import io.restassured.RestAssured;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
Expand Down Expand Up @@ -235,6 +236,84 @@ public void testDatasetLevelTool1() {

}

@Test
public void testDatasetLevelToolConfigure() {

// Delete all external tools before testing.
Response getTools = UtilIT.getExternalTools();
getTools.prettyPrint();
getTools.then().assertThat()
.statusCode(OK.getStatusCode());
String body = getTools.getBody().asString();
JsonReader bodyObject = Json.createReader(new StringReader(body));
JsonArray tools = bodyObject.readObject().getJsonArray("data");
for (int i = 0; i < tools.size(); i++) {
JsonObject tool = tools.getJsonObject(i);
int id = tool.getInt("id");
Response deleteExternalTool = UtilIT.deleteExternalTool(id);
deleteExternalTool.prettyPrint();
}

Response createUser = UtilIT.createRandomUser();
createUser.prettyPrint();
createUser.then().assertThat()
.statusCode(OK.getStatusCode());
String apiToken = UtilIT.getApiTokenFromResponse(createUser);

Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken);
createDataverseResponse.prettyPrint();
createDataverseResponse.then().assertThat()
.statusCode(CREATED.getStatusCode());

String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse);

Response createDataset = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken);
createDataset.prettyPrint();
createDataset.then().assertThat()
.statusCode(CREATED.getStatusCode());

Integer datasetId = JsonPath.from(createDataset.getBody().asString()).getInt("data.id");
String datasetPid = JsonPath.from(createDataset.getBody().asString()).getString("data.persistentId");

String toolManifest = """
{
"displayName": "Turbo Dataset Config",
"description": "Read/write access.",
"types": [
"configure"
],
"scope": "dataset",
"toolUrl": "http://datasettool1.com",
"toolParameters": {
"queryParameters": [
{
"datasetPid": "{datasetPid}"
},
{
"localeCode": "{localeCode}"
}
]
}
}
""";

Response addExternalTool = UtilIT.addExternalTool(JsonUtil.getJsonObject(toolManifest));
addExternalTool.prettyPrint();
addExternalTool.then().assertThat()
.statusCode(OK.getStatusCode())
.body("data.displayName", CoreMatchers.equalTo("Turbo Dataset Config"));

Response getExternalToolsByDatasetId = UtilIT.getExternalToolsForDataset(datasetId.toString(), "configure", apiToken);
getExternalToolsByDatasetId.prettyPrint();
getExternalToolsByDatasetId.then().assertThat()
.body("data[0].displayName", CoreMatchers.equalTo("Turbo Dataset Config"))
.body("data[0].scope", CoreMatchers.equalTo("dataset"))
.body("data[0].types[0]", CoreMatchers.equalTo("configure"))
.body("data[0].toolUrlWithQueryParams", CoreMatchers.equalTo("http://datasettool1.com?datasetPid=" + datasetPid))
.statusCode(OK.getStatusCode());

}

@Test
public void testAddFilelToolNoFileId() throws IOException {
JsonObjectBuilder job = Json.createObjectBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package edu.harvard.iq.dataverse.externaltools;

import edu.harvard.iq.dataverse.DOIServiceBean;
import edu.harvard.iq.dataverse.DataFile;
import edu.harvard.iq.dataverse.DataFileServiceBean;
import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.DatasetVersion;
import edu.harvard.iq.dataverse.FileMetadata;
import edu.harvard.iq.dataverse.GlobalId;
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.settings.JvmSettings;
Expand All @@ -15,6 +17,7 @@
import jakarta.json.Json;
import jakarta.json.JsonObject;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -234,4 +237,43 @@ public void testGetToolUrlWithAllowedApiCalls() {
assertTrue(signedUrl.contains("&token="));
System.out.println(JsonUtil.prettyPrint(jo));
}

@Test
@JvmSetting(key = JvmSettings.SITE_URL, value = "https://librascholar.org")
public void testDatasetConfigureTool() {
List<ExternalToolType> externalToolTypes = new ArrayList<>();
var externalToolType = new ExternalToolType();
externalToolType.setType(ExternalTool.Type.CONFIGURE);
externalToolTypes.add(externalToolType);
var scope = ExternalTool.Scope.DATASET;
String toolUrl = "http://example.com";
var externalTool = new ExternalTool("displayName", "toolName", "description", externalToolTypes, scope, toolUrl, "{}", DataFileServiceBean.MIME_TYPE_TSV_ALT);

externalTool.setToolParameters(Json.createObjectBuilder()
.add("queryParameters", Json.createArrayBuilder()
.add(Json.createObjectBuilder()
.add("siteUrl", "{siteUrl}")
)
.add(Json.createObjectBuilder()
.add("datasetPid", "{datasetPid}")
)
.add(Json.createObjectBuilder()
.add("localeCode", "{localeCode}")
)
)
.build().toString());

var dataset = new Dataset();
dataset.setGlobalId(new GlobalId(DOIServiceBean.DOI_PROTOCOL, "10.5072", "ABC123", null, DOIServiceBean.DOI_RESOLVER_URL, null));
ApiToken nullApiToken = null;
String nullLocaleCode = "en";
var externalToolHandler = new ExternalToolHandler(externalTool, dataset, nullApiToken, nullLocaleCode);
System.out.println("tool: " + externalToolHandler.getToolUrlWithQueryParams());
assertEquals("http://example.com?siteUrl=https://librascholar.org&datasetPid=doi:10.5072/ABC123&localeCode=en", externalToolHandler.getToolUrlWithQueryParams());
assertFalse(externalToolHandler.getExternalTool().isExploreTool());
assertEquals("configure", externalToolHandler.getExternalTool().getExternalToolTypes().get(0).getType().toString());
assertEquals("dataset", externalToolHandler.getExternalTool().getScope().toString());

}

}

0 comments on commit cbd395e

Please sign in to comment.