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 729174dedfc..8a1ea220e2b 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java @@ -58,6 +58,9 @@ import jakarta.ejb.EJB; import jakarta.ejb.EJBException; import jakarta.inject.Inject; +import jakarta.json.Json; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonObjectBuilder; import jakarta.json.*; import jakarta.json.stream.JsonParsingException; import jakarta.servlet.http.HttpServletRequest; @@ -5192,6 +5195,45 @@ public Response getExternalToolDVParams(@Context ContainerRequestContext crc, } } + /** + * Public endpoint: list dataset-scope external tools applicable to a dataset, with toolUrlWithQueryParams resolved. + * Example: GET /api/datasets/{id}/externalTools?type=explore + * If the request is authenticated, callback URLs for tools that declare allowedApiCalls will be signed. + */ + @GET + @Path("{id}/externalTools") + public Response getDatasetExternalTools(@Context ContainerRequestContext crc, + @PathParam("id") String idSupplied, + @QueryParam("type") String typeSupplied, + @QueryParam("locale") String locale) { + ExternalTool.Type type; + try { + type = ExternalTool.Type.fromString(typeSupplied); + } catch (IllegalArgumentException ex) { + return error(BAD_REQUEST, ex.getLocalizedMessage()); + } + try { + Dataset dataset = findDatasetOrDie(idSupplied); + + ApiToken apiToken = null; + User user = getRequestUser(crc); + apiToken = authSvc.getValidApiTokenForUser(user); + + String localeCode = (locale != null && !locale.isBlank()) ? locale : null; + + JsonArrayBuilder tools = Json.createArrayBuilder(); + List datasetTools = externalToolService.findDatasetToolsByType(type); + for (ExternalTool tool : datasetTools) { + ExternalToolHandler externalToolHandler = new ExternalToolHandler(tool, dataset, apiToken, localeCode); + JsonObjectBuilder toolToJson = externalToolService.getToolAsJsonWithQueryParameters(externalToolHandler); + tools.add(toolToJson); + } + return ok(tools); + } catch (WrappedResponse wr) { + return wr.getResponse(); + } + } + @GET @Path("summaryFieldNames") public Response getDatasetSummaryFieldNames() { diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index 5834e7e0008..d1bebc2e1e3 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -886,6 +886,59 @@ public Response getExternalToolFMParams(@Context ContainerRequestContext crc, @P eth = new ExternalToolHandler(externalTool, target.getDataFile(), apiToken, target, locale); return ok(eth.createPostBody(eth.getParams(JsonUtil.getJsonObject(externalTool.getToolParameters())), JsonUtil.getJsonArray(externalTool.getAllowedApiCalls()))); } + + /** + * Public: return all external tools applicable to a file (by its content type and requirements), each with toolUrlWithQueryParams resolved. + * Example: GET /api/files/{id}/externalTools?type=preview|explore|query[&fileMetadataId=...][&locale=en] + */ + @GET + @Path("{id}/externalTools") + public Response getFileExternalTools(@Context ContainerRequestContext crc, + @PathParam("id") String idSupplied, + @QueryParam("type") String typeSupplied, + @QueryParam("fileMetadataId") Long fileMetadataId, + @QueryParam("locale") String locale) { + ExternalTool.Type type; + try { + type = ExternalTool.Type.fromString(typeSupplied); + } catch (IllegalArgumentException ex) { + return error(BAD_REQUEST, ex.getLocalizedMessage()); + } + try { + DataFile dataFile = findDataFileOrDie(idSupplied); + + // Resolve FileMetadata context + FileMetadata fm = null; + if (fileMetadataId != null) { + fm = fileSvc.findFileMetadata(fileMetadataId); + if (fm == null) { + return error(BAD_REQUEST, "FileMetadata not found for id " + fileMetadataId); + } + } else { + fm = dataFile.getFileMetadata(); + } + + ApiToken apiToken = null; + User user = getRequestUser(crc); + apiToken = authSvc.getValidApiTokenForUser(user); + + String localeCode = (locale != null && !locale.isBlank()) ? locale : null; + + // Get all tools of the requested type, then filter by file using service logic (handles tabular/TSV_ALT, requirements, storage access) + List typeTools = externalToolService.findFileToolsByType(type); + List applicable = externalToolService.findExternalToolsByFile(typeTools, dataFile); + + JsonArrayBuilder tools = Json.createArrayBuilder(); + for (ExternalTool tool : applicable) { + ExternalToolHandler handler = new ExternalToolHandler(tool, dataFile, apiToken, fm, localeCode); + JsonObjectBuilder toolJson = externalToolService.getToolAsJsonWithQueryParameters(handler); + tools.add(toolJson); + } + return ok(tools); + } catch (WrappedResponse wr) { + return wr.getResponse(); + } + } @GET @Path("fixityAlgorithm")